Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions integration_test/test_helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ class FakeSecureStorage implements FlutterSecureStorage {
// TODO: implement containsKey
throw UnimplementedError();
}
final Map<String, List<ValueChanged<String?>>> _listeners = {};

@override
Map<String, List<ValueChanged<String?>>> get getListeners => _listeners;

@override
// TODO: implement iOptions
Expand Down Expand Up @@ -263,9 +267,12 @@ class FakeBackgroundService implements BackgroundService {
}

class FakeMostroService implements MostroService {
FakeMostroService(this.ref);
@override
final Ref ref;
FakeMostroService(this._ref);

final Ref _ref;

@override
Ref get ref => _ref;

@override
void init({List<NostrKeyPairs>? keys}) {}
Expand Down
27 changes: 27 additions & 0 deletions lib/common/top_snackbar.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'package:flutter/material.dart';

void showTopSnackBar(
BuildContext context,
String message, {
Color? backgroundColor,
Duration duration = const Duration(seconds: 3),
}) {
final messenger = ScaffoldMessenger.of(context);

// Remove any existing SnackBars
messenger.clearSnackBars();

messenger.showSnackBar(
SnackBar(
content: Text(message),
behavior: SnackBarBehavior.floating,
duration: duration,
backgroundColor: backgroundColor,
margin: EdgeInsets.only(
top: MediaQuery.of(context).padding.top + 8,
left: 16,
right: 16,
),
),
);
}
17 changes: 8 additions & 9 deletions lib/core/deep_link_handler.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:mostro_mobile/common/top_snackbar.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:logger/logger.dart';
Expand Down Expand Up @@ -147,16 +148,14 @@ class DeepLinkHandler {

/// Shows an error snack bar
void _showErrorSnackBar(BuildContext? context, String message) {
if (context == null) return;
if (context == null) return;

ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: Colors.red,
duration: const Duration(seconds: 3),
),
);
}
showTopSnackBar(
context,
message,
backgroundColor: Colors.red,
);
}

/// Disposes the deep link handler
void dispose() {
Expand Down
8 changes: 5 additions & 3 deletions lib/features/auth/screens/login_screen.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:mostro_mobile/common/top_snackbar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
Expand All @@ -20,9 +21,10 @@ class LoginScreen extends HookConsumerWidget {
if (state is AuthAuthenticated) {
context.go('/');
} else if (state is AuthFailure) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.error)),
);
showTopSnackBar(
context,
state.error,
);
}
});

Expand Down
10 changes: 6 additions & 4 deletions lib/features/auth/screens/register_screen.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:mostro_mobile/common/top_snackbar.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
Expand Down Expand Up @@ -39,10 +40,11 @@ class RegisterScreen extends HookConsumerWidget {
// Navigate to home after successful registration
context.go('/');
} else if (state is AuthFailure) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(state.error)),
);
}
showTopSnackBar(
context,
state.error,
);
}
});

// Trigger biometrics check on first build
Expand Down
29 changes: 14 additions & 15 deletions lib/features/chat/widgets/encrypted_file_message.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:typed_data';
import 'dart:convert';
import 'dart:io';
import 'package:mostro_mobile/common/top_snackbar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:dart_nostr/dart_nostr.dart';
Expand Down Expand Up @@ -114,7 +115,7 @@ class _EncryptedFileMessageState extends ConsumerState<EncryptedFileMessage> {
),
const SizedBox(height: 8),
Text(
'Failed to load image',
S.of(context)!.failedToLoadImage,
style: TextStyle(
color: AppTheme.textSecondary,
fontSize: 12,
Expand Down Expand Up @@ -366,7 +367,7 @@ class _EncryptedFileMessageState extends ConsumerState<EncryptedFileMessage> {
),
const SizedBox(height: 4),
Text(
'Failed to load file',
S.of(context)!.error,
style: TextStyle(
fontSize: 12,
color: Colors.red,
Expand Down Expand Up @@ -494,23 +495,21 @@ class _EncryptedFileMessageState extends ConsumerState<EncryptedFileMessage> {

if (mounted) {
if (result.type != ResultType.done) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Could not open file: ${result.message}'),
backgroundColor: Colors.orange,
duration: const Duration(seconds: 3),
),
);
showTopSnackBar(
context,
S.of(context)!.couldNotOpenFile,
backgroundColor: Colors.orange,
duration: const Duration(seconds: 3),
);
}
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error opening file: $e'),
backgroundColor: Colors.red,
),
);
showTopSnackBar(
context,
S.of(context)!.errorOpeningFile,
backgroundColor: Colors.red,
);
}
}
}
Expand Down
26 changes: 13 additions & 13 deletions lib/features/chat/widgets/encrypted_image_message.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:typed_data';
import 'dart:convert';
import 'dart:io';
import 'package:mostro_mobile/common/top_snackbar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:dart_nostr/dart_nostr.dart';
Expand Down Expand Up @@ -373,23 +374,22 @@ class _EncryptedImageMessageState extends ConsumerState<EncryptedImageMessage> {

if (mounted) {
if (result.type != ResultType.done) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('$couldNotOpenMsg: ${result.message}'),
backgroundColor: Colors.orange,
duration: const Duration(seconds: 3),
),
);
showTopSnackBar(
context,
'$couldNotOpenMsg: ${result.message}',
backgroundColor: Colors.orange,
duration: const Duration(seconds: 3),
);

}
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('$errorOpeningMsg: $e'),
backgroundColor: Colors.red,
),
);
showTopSnackBar(
context,
'$errorOpeningMsg: $e',
backgroundColor: Colors.red,
);
}
}
}
Expand Down
19 changes: 7 additions & 12 deletions lib/features/chat/widgets/message_bubble.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:dart_nostr/nostr/model/event/event.dart';
import 'package:mostro_mobile/common/top_snackbar.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
Expand Down Expand Up @@ -154,18 +155,12 @@ class MessageBubble extends ConsumerWidget {
// Only copy if text is not empty
if (text.isNotEmpty) {
Clipboard.setData(ClipboardData(text: text));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(S.of(context)!.messageCopiedToClipboard),
duration: const Duration(seconds: 2),
backgroundColor: AppTheme.backgroundCard,
behavior: SnackBarBehavior.floating,
margin: const EdgeInsets.all(16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
);
showTopSnackBar(
context,
S.of(context)!.messageCopiedToClipboard,
backgroundColor: AppTheme.backgroundCard,
duration: const Duration(seconds: 2),
);
}
}

Expand Down
12 changes: 6 additions & 6 deletions lib/features/chat/widgets/message_input.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:io';
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:mostro_mobile/common/top_snackbar.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:file_picker/file_picker.dart';
import 'package:mime/mime.dart';
Expand Down Expand Up @@ -127,12 +128,11 @@ class _MessageInputState extends ConsumerState<MessageInput> {
} catch (e) {
// Show error to user
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error uploading file: $e'),
backgroundColor: Colors.red,
),
);
showTopSnackBar(
context,
S.of(context)!.errorUploadingFile,
backgroundColor: Colors.red,
);
}
} finally {
if (mounted) {
Expand Down
21 changes: 10 additions & 11 deletions lib/features/disputes/widgets/dispute_input_section.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:mostro_mobile/common/top_snackbar.dart';
import 'package:mostro_mobile/core/app_theme.dart';
import 'package:mostro_mobile/generated/l10n.dart';

Expand Down Expand Up @@ -127,21 +128,19 @@ class _DisputeInputSectionState extends State<DisputeInputSection> {
await Future.delayed(const Duration(milliseconds: 500));

if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Message sent: $message'),
backgroundColor: Colors.green,
),
);
showTopSnackBar(
context,
'Message sent: $message',
backgroundColor: Colors.green,
);
}
} catch (error) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Failed to send message: $error'),
showTopSnackBar(
context,
'Failed to send message: $error',
backgroundColor: Colors.red,
),
);
);
}
} finally {
if (mounted) {
Expand Down
14 changes: 7 additions & 7 deletions lib/features/disputes/widgets/dispute_message_bubble.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:mostro_mobile/common/top_snackbar.dart';
import 'package:flutter/services.dart';
import 'package:mostro_mobile/core/app_theme.dart';
import 'package:mostro_mobile/generated/l10n.dart';
Expand Down Expand Up @@ -88,13 +89,12 @@ class DisputeMessageBubble extends StatelessWidget {

void _copyToClipboard(BuildContext context, String text) {
Clipboard.setData(ClipboardData(text: text));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(S.of(context)?.messageCopiedToClipboard ?? 'Message copied to clipboard'),
duration: const Duration(seconds: 1),
backgroundColor: Colors.green,
),
);
showTopSnackBar(
context,
S.of(context)?.messageCopiedToClipboard ?? 'Message copied to clipboard',
backgroundColor: Colors.green,
duration: const Duration(seconds: 1),
);
}

String _formatTime(DateTime dateTime) {
Expand Down
17 changes: 11 additions & 6 deletions lib/features/key_manager/key_management_screen.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/foundation.dart';
import 'package:mostro_mobile/common/top_snackbar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
Expand Down Expand Up @@ -90,15 +91,19 @@ class _KeyManagementScreenState extends ConsumerState<KeyManagementScreen> {
await keyManager.importMnemonic(importValue);
await _loadKeys();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(S.of(context)!.keyImportedSuccessfully)),
);
showTopSnackBar(
context,
S.of(context)!.keyImportedSuccessfully,
backgroundColor: Colors.green,
);
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(S.of(context)!.importFailed(e.toString()))),
);
showTopSnackBar(
context,
S.of(context)!.importFailed(e.toString()),
backgroundColor: Colors.red,
);
}
}
}
Expand Down
Loading
Loading