diff --git a/lib/src/components/best_friends_selector.dart b/lib/src/components/best_friends_selector.dart index 0ff7c40..8ca5fe7 100644 --- a/lib/src/components/best_friends_selector.dart +++ b/lib/src/components/best_friends_selector.dart @@ -1,7 +1,7 @@ import 'dart:collection'; import 'package:fixnum/fixnum.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/components/flame.dart'; import 'package:twonly/src/components/headline.dart'; import 'package:twonly/src/components/initialsavatar.dart'; @@ -29,7 +29,7 @@ class BestFriendsSelector extends StatelessWidget { return Column( children: [ - HeadLineComponent(AppLocalizations.of(context)!.shareImageBestFriends), + HeadLineComponent(context.lang.shareImageBestFriends), Column( spacing: 8, children: List.generate( diff --git a/lib/src/components/image_editor/action_button.dart b/lib/src/components/image_editor/action_button.dart index aabc347..3c85c02 100644 --- a/lib/src/components/image_editor/action_button.dart +++ b/lib/src/components/image_editor/action_button.dart @@ -5,24 +5,38 @@ class ActionButton extends StatelessWidget { final VoidCallback? onPressed; final IconData? icon; final Color? color; + final String tooltipText; + final bool disable; - const ActionButton(this.icon, {super.key, this.onPressed, this.color}); + const ActionButton(this.icon, + {super.key, + this.onPressed, + this.color, + required this.tooltipText, + this.disable = false}); @override Widget build(BuildContext context) { - return IconButton( - icon: FaIcon( - icon, - size: 30, - color: color ?? Colors.white, - shadows: [ - Shadow( - color: const Color.fromARGB(122, 0, 0, 0), - blurRadius: 5.0, - ) - ], + return Tooltip( + message: tooltipText, + child: IconButton( + icon: FaIcon( + icon, + size: 30, + color: disable + ? const Color.fromARGB(154, 255, 255, 255) + : color ?? Colors.white, + shadows: [ + Shadow( + color: const Color.fromARGB(122, 0, 0, 0), + blurRadius: 5.0, + ) + ], + ), + onPressed: () { + if (!disable && onPressed != null) onPressed!(); + }, ), - onPressed: onPressed, ); } } diff --git a/lib/src/components/image_editor/layers/draw_layer.dart b/lib/src/components/image_editor/layers/draw_layer.dart index 7466d5f..6c6acc3 100644 --- a/lib/src/components/image_editor/layers/draw_layer.dart +++ b/lib/src/components/image_editor/layers/draw_layer.dart @@ -4,6 +4,7 @@ import 'package:hand_signature/signature.dart'; import 'package:screenshot/screenshot.dart'; import 'package:twonly/src/components/image_editor/action_button.dart'; import 'package:twonly/src/components/image_editor/data/layer.dart'; +import 'package:twonly/src/utils/misc.dart'; class DrawLayer extends StatefulWidget { final DrawLayerData layerData; @@ -106,6 +107,7 @@ class _DrawLayerState extends State { children: [ ActionButton( FontAwesomeIcons.check, + tooltipText: context.lang.imageEditorDrawOk, onPressed: () async { widget.layerData.isEditing = false; }, @@ -113,6 +115,7 @@ class _DrawLayerState extends State { Expanded(child: Container()), ActionButton( FontAwesomeIcons.arrowRotateLeft, + tooltipText: context.lang.undo, color: widget.layerData.control.paths.isNotEmpty ? Colors.white : Colors.white.withAlpha(80), @@ -125,6 +128,7 @@ class _DrawLayerState extends State { }, ), ActionButton( + tooltipText: context.lang.redo, FontAwesomeIcons.arrowRotateRight, color: undoList.isNotEmpty ? Colors.white diff --git a/lib/src/components/image_editor/layers/text_layer.dart b/lib/src/components/image_editor/layers/text_layer.dart index 857a4d2..3cc4cfe 100755 --- a/lib/src/components/image_editor/layers/text_layer.dart +++ b/lib/src/components/image_editor/layers/text_layer.dart @@ -150,6 +150,7 @@ class _TextViewState extends State { }, child: ActionButton( FontAwesomeIcons.trashCan, + tooltipText: "", color: deleteLayer ? Colors.red : Colors.white, ), ), diff --git a/lib/src/components/message_send_state_icon.dart b/lib/src/components/message_send_state_icon.dart index 90915ed..bdd491d 100644 --- a/lib/src/components/message_send_state_icon.dart +++ b/lib/src/components/message_send_state_icon.dart @@ -4,7 +4,7 @@ import 'package:provider/provider.dart'; import 'package:twonly/src/model/json/message.dart'; import 'package:twonly/src/model/messages_model.dart'; import 'package:twonly/src/providers/download_change_provider.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:twonly/src/utils/misc.dart'; enum MessageSendState { received, @@ -44,29 +44,29 @@ class MessageSendStateIcon extends StatelessWidget { switch (message.getSendState()) { case MessageSendState.receivedOpened: icon = Icon(Icons.crop_square, size: 14, color: color); - text = AppLocalizations.of(context)!.messageSendState_Received; + text = context.lang.messageSendState_Received; break; case MessageSendState.sendOpened: icon = FaIcon(FontAwesomeIcons.paperPlane, size: 12, color: color); - text = AppLocalizations.of(context)!.messageSendState_Opened; + text = context.lang.messageSendState_Opened; break; case MessageSendState.received: icon = Icon(Icons.square_rounded, size: 14, color: color); - text = AppLocalizations.of(context)!.messageSendState_Received; + text = context.lang.messageSendState_Received; break; case MessageSendState.send: icon = FaIcon(FontAwesomeIcons.solidPaperPlane, size: 12, color: color); - text = AppLocalizations.of(context)!.messageSendState_Send; + text = context.lang.messageSendState_Send; break; case MessageSendState.sending: case MessageSendState.receiving: icon = loaderIcon; - text = AppLocalizations.of(context)!.messageSendState_Sending; + text = context.lang.messageSendState_Sending; break; } if (!message.isDownloaded) { - text = AppLocalizations.of(context)!.messageSendState_TapToLoad; + text = context.lang.messageSendState_TapToLoad; } bool isDownloading = false; @@ -78,7 +78,7 @@ class MessageSendStateIcon extends StatelessWidget { } if (isDownloading) { - text = AppLocalizations.of(context)!.messageSendState_Loading; + text = context.lang.messageSendState_Loading; icon = loaderIcon; } diff --git a/lib/src/components/notification_badge.dart b/lib/src/components/notification_badge.dart index 675ed50..bf79500 100644 --- a/lib/src/components/notification_badge.dart +++ b/lib/src/components/notification_badge.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; class NotificationBadge extends StatelessWidget { - final int count; + final String count; final Widget child; const NotificationBadge( @@ -9,25 +9,29 @@ class NotificationBadge extends StatelessWidget { @override Widget build(BuildContext context) { - if (count == 0) return child; + if (count == "0") return child; + bool infinity = count == "∞"; return Stack( children: [ child, Positioned( - right: 5, + right: 3, top: 0, - child: Container( - padding: EdgeInsets.all(5.0), // Add some padding - decoration: BoxDecoration( - color: Colors.red, // Background color - shape: BoxShape.circle, // Make it circular - ), - child: Center( - child: Text( - count.toString(), - style: TextStyle( - color: Colors.white, // Text color - fontSize: 10, + child: SizedBox( + height: 18, + width: 18, + child: CircleAvatar( + backgroundColor: Colors.red, + child: Center( + child: Transform.rotate( + angle: infinity ? 90 * (3.141592653589793 / 180) : 0, + child: Text( + infinity ? "8" : count, + style: TextStyle( + color: Colors.white, // Text color + fontSize: 10, + ), + ), ), ), ), diff --git a/lib/src/localization/app_en.arb b/lib/src/localization/app_en.arb index 15405a9..99424cc 100644 --- a/lib/src/localization/app_en.arb +++ b/lib/src/localization/app_en.arb @@ -29,6 +29,16 @@ "messageSendState_Sending": "Sending", "messageSendState_TapToLoad": "Tap to load", "messageSendState_Loading": "Downloading", + "imageEditorDrawOk": "Take drawing", + "undo": "Undo", + "redo": "Redo", + "close": "Close", + "switchFrontAndBackCamera": "Switch between front and back camera.", + "addTextItem": "Text", + "protectAsARealTwonly": "Send as real twonly!", + "addDrawing": "Drawing", + "addEmoji": "Emoji", + "toogleFlashLight": "Toggle the flash light", "chatListDetailTitle": "Your chat with {username}", "searchUsernameNotFoundLong": "\"{username}\" is not a twonly user. Please check the username and try again.", "errorUnknown": "An unexpected error has occurred. Please try again later.", diff --git a/lib/src/providers/api/api.dart b/lib/src/providers/api/api.dart index 16b09ae..0f8dd20 100644 --- a/lib/src/providers/api/api.dart +++ b/lib/src/providers/api/api.dart @@ -154,7 +154,8 @@ Future encryptAndUploadMediaFile(Int64 target, Uint8List imageBytes) async { await uploadMediaFile(messageId, target, encryptBytes); } -Future sendImage(List userIds, Uint8List imageBytes) async { +Future sendImage(List userIds, Uint8List imageBytes, bool isRealTwonly, + int maxShowTime) async { // 1. set notifier provider Uint8List? imageBytesCompressed = await getCompressedImage(imageBytes); diff --git a/lib/src/utils/misc.dart b/lib/src/utils/misc.dart index 937e598..2e7a7f7 100644 --- a/lib/src/utils/misc.dart +++ b/lib/src/utils/misc.dart @@ -10,6 +10,10 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:twonly/src/proto/api/error.pb.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +extension LocalizationExtension on BuildContext { + AppLocalizations get lang => AppLocalizations.of(this)!; +} + Future writeLogToFile(LogRecord record) async { final directory = await getApplicationDocumentsDirectory(); final logFile = File('${directory.path}/app.log'); @@ -57,31 +61,31 @@ Uint8List getRandomUint8List(int length) { String errorCodeToText(BuildContext context, ErrorCode code) { switch (code.toString()) { case "Unknown": - return AppLocalizations.of(context)!.errorUnknown; + return context.lang.errorUnknown; case "BadRequest": - return AppLocalizations.of(context)!.errorBadRequest; + return context.lang.errorBadRequest; case "TooManyRequests": - return AppLocalizations.of(context)!.errorTooManyRequests; + return context.lang.errorTooManyRequests; case "InternalError": - return AppLocalizations.of(context)!.errorInternalError; + return context.lang.errorInternalError; case "InvalidInvitationCode": - return AppLocalizations.of(context)!.errorInvalidInvitationCode; + return context.lang.errorInvalidInvitationCode; case "UsernameAlreadyTaken": - return AppLocalizations.of(context)!.errorUsernameAlreadyTaken; + return context.lang.errorUsernameAlreadyTaken; case "SignatureNotValid": - return AppLocalizations.of(context)!.errorSignatureNotValid; + return context.lang.errorSignatureNotValid; case "UsernameNotFound": - return AppLocalizations.of(context)!.errorUsernameNotFound; + return context.lang.errorUsernameNotFound; case "UsernameNotValid": - return AppLocalizations.of(context)!.errorUsernameNotValid; + return context.lang.errorUsernameNotValid; case "InvalidPublicKey": - return AppLocalizations.of(context)!.errorInvalidPublicKey; + return context.lang.errorInvalidPublicKey; case "SessionAlreadyAuthenticated": - return AppLocalizations.of(context)!.errorSessionAlreadyAuthenticated; + return context.lang.errorSessionAlreadyAuthenticated; case "SessionNotAuthenticated": - return AppLocalizations.of(context)!.errorSessionNotAuthenticated; + return context.lang.errorSessionNotAuthenticated; case "OnlyOneSessionAllowed": - return AppLocalizations.of(context)!.errorOnlyOneSessionAllowed; + return context.lang.errorOnlyOneSessionAllowed; default: return code.toString(); // Fallback for unrecognized keys } diff --git a/lib/src/views/camera_to_share/camera_preview_view.dart b/lib/src/views/camera_to_share/camera_preview_view.dart index d4b592c..3386c9c 100644 --- a/lib/src/views/camera_to_share/camera_preview_view.dart +++ b/lib/src/views/camera_to_share/camera_preview_view.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:camerawesome/camerawesome_plugin.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/components/image_editor/action_button.dart'; import 'package:twonly/src/components/media_view_sizing.dart'; import 'package:twonly/src/components/permissions_view.dart'; @@ -176,6 +177,8 @@ class _CameraPreviewViewState extends State { children: [ ActionButton( FontAwesomeIcons.repeat, + tooltipText: + context.lang.switchFrontAndBackCamera, onPressed: () async { cameraState.switchCameraSensor( aspectRatio: @@ -185,6 +188,7 @@ class _CameraPreviewViewState extends State { // SizedBox(height: 20), ActionButton( FontAwesomeIcons.bolt, + tooltipText: context.lang.toogleFlashLight, color: isFlashOn ? const Color.fromARGB(255, 255, 230, 0) : const Color.fromARGB( diff --git a/lib/src/views/camera_to_share/share_image_editor_view.dart b/lib/src/views/camera_to_share/share_image_editor_view.dart index 957e4c1..054e886 100644 --- a/lib/src/views/camera_to_share/share_image_editor_view.dart +++ b/lib/src/views/camera_to_share/share_image_editor_view.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:twonly/src/components/image_editor/action_button.dart'; import 'package:twonly/src/components/media_view_sizing.dart'; +import 'package:twonly/src/components/notification_badge.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/views/camera_to_share/share_image_view.dart'; import 'dart:async'; @@ -27,6 +27,8 @@ class ShareImageEditorView extends StatefulWidget { class _ShareImageEditorView extends State { bool _imageSaved = false; bool _imageSaving = false; + bool _isRealTwonly = false; + int _maxShowTime = 18; ImageItem currentImage = ImageItem(); ScreenshotController screenshotController = ScreenshotController(); @@ -51,26 +53,31 @@ class _ShareImageEditorView extends State { return []; } return [ - BottomButton( - icon: FontAwesomeIcons.font, - onTap: () async { + ActionButton( + FontAwesomeIcons.font, + tooltipText: context.lang.addTextItem, + onPressed: () async { undoLayers.clear(); removedLayers.clear(); layers.add(TextLayerData()); setState(() {}); }, ), - BottomButton( - icon: FontAwesomeIcons.pencil, - onTap: () async { + const SizedBox(height: 8), + ActionButton( + FontAwesomeIcons.pencil, + tooltipText: context.lang.addDrawing, + onPressed: () async { undoLayers.clear(); removedLayers.clear(); layers.add(DrawLayerData()); }, ), - BottomButton( - icon: FontAwesomeIcons.faceGrinWide, - onTap: () async { + const SizedBox(height: 8), + ActionButton( + FontAwesomeIcons.faceGrinWide, + tooltipText: context.lang.addEmoji, + onPressed: () async { EmojiLayerData? layer = await showModalBottomSheet( context: context, backgroundColor: Colors.black, @@ -78,16 +85,49 @@ class _ShareImageEditorView extends State { return const Emojis(); }, ); - if (layer == null) return; - undoLayers.clear(); removedLayers.clear(); layers.add(layer); - setState(() {}); }, ), + const SizedBox(height: 8), + NotificationBadge( + count: _maxShowTime == 999999 ? "∞" : _maxShowTime.toString(), + // count: "", + child: ActionButton( + FontAwesomeIcons.stopwatch, + tooltipText: context.lang.protectAsARealTwonly, + disable: _isRealTwonly, + onPressed: () async { + if (_maxShowTime == 999999) { + _maxShowTime = 4; + } else if (_maxShowTime >= 22) { + _maxShowTime = 999999; + } else { + _maxShowTime = _maxShowTime + 4; + } + + // _maxShowTime = + // _isRealTwonly = !_isRealTwonly; + }, + ), + ), + const SizedBox(height: 8), + ActionButton( + FontAwesomeIcons.shieldHeart, + tooltipText: context.lang.protectAsARealTwonly, + color: _isRealTwonly + ? Theme.of(context).colorScheme.primary + : Colors.white, + onPressed: () async { + _isRealTwonly = !_isRealTwonly; + if (_isRealTwonly) { + _maxShowTime = 12; + } + }, + ), ]; } @@ -100,6 +140,7 @@ class _ShareImageEditorView extends State { return [ ActionButton( FontAwesomeIcons.xmark, + tooltipText: context.lang.close, onPressed: () async { Navigator.pop(context); }, @@ -108,9 +149,8 @@ class _ShareImageEditorView extends State { const SizedBox(width: 8), ActionButton( FontAwesomeIcons.rotateLeft, - color: layers.length > 1 || removedLayers.isNotEmpty - ? Colors.white - : Colors.grey, + tooltipText: context.lang.undo, + disable: layers.length <= 1 && removedLayers.isEmpty, onPressed: () { if (removedLayers.isNotEmpty) { layers.add(removedLayers.removeLast()); @@ -126,7 +166,8 @@ class _ShareImageEditorView extends State { const SizedBox(width: 8), ActionButton( FontAwesomeIcons.rotateRight, - color: undoLayers.isNotEmpty ? Colors.white : Colors.grey, + tooltipText: context.lang.redo, + disable: undoLayers.isEmpty, onPressed: () { if (undoLayers.isEmpty) return; layers.add(undoLayers.removeLast()); @@ -211,7 +252,7 @@ class _ShareImageEditorView extends State { ), ), Positioned( - right: 0, + right: 6, top: 100, child: Container( alignment: Alignment.bottomCenter, @@ -266,10 +307,8 @@ class _ShareImageEditorView extends State { } }, label: Text(_imageSaved - ? AppLocalizations.of(context)! - .shareImagedEditorSavedImage - : AppLocalizations.of(context)! - .shareImagedEditorSaveImage), + ? context.lang.shareImagedEditorSavedImage + : context.lang.shareImagedEditorSaveImage), ), const SizedBox(width: 20), FilledButton.icon( @@ -280,8 +319,12 @@ class _ShareImageEditorView extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => - ShareImageView(imageBytes: imageBytes)), + builder: (context) => ShareImageView( + imageBytes: imageBytes, + isRealTwonly: _isRealTwonly, + maxShowTime: _maxShowTime, + ), + ), ); }, style: ButtonStyle( @@ -290,7 +333,7 @@ class _ShareImageEditorView extends State { ), ), label: Text( - AppLocalizations.of(context)!.shareImagedEditorShareWith, + context.lang.shareImagedEditorShareWith, style: TextStyle(fontSize: 17), ), ), @@ -302,57 +345,3 @@ class _ShareImageEditorView extends State { ); } } - -/// Button used in bottomNavigationBar in ImageEditor -class BottomButton extends StatelessWidget { - final VoidCallback? onTap, onLongPress; - final IconData icon; - final Color color; - - const BottomButton({ - super.key, - this.onTap, - this.color = Colors.white, - this.onLongPress, - required this.icon, - }); - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: onTap, - onLongPress: onLongPress, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 12), - child: Column( - children: [ - ActionButton( - icon, - color: color, - onPressed: onTap ?? () {}, - ), - const SizedBox(height: 8), - ], - ), - ), - ); - } -} - -// /// Show image drawing surface over image -// class ImageEditorDrawing extends StatefulWidget { -// final ImageItem image; - -// const ImageEditorDrawing({ -// super.key, -// required this.image, -// }); - -// @override -// State createState() => _ImageEditorDrawingState(); -// } - -// class _ImageEditorDrawingState extends State { - -// } -// } diff --git a/lib/src/views/camera_to_share/share_image_view.dart b/lib/src/views/camera_to_share/share_image_view.dart index fc639d6..98f444b 100644 --- a/lib/src/views/camera_to_share/share_image_view.dart +++ b/lib/src/views/camera_to_share/share_image_view.dart @@ -2,7 +2,6 @@ import 'dart:collection'; import 'dart:typed_data'; import 'package:fixnum/fixnum.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:twonly/src/components/best_friends_selector.dart'; import 'package:twonly/src/components/flame.dart'; @@ -14,8 +13,14 @@ import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/views/home_view.dart'; class ShareImageView extends StatefulWidget { - const ShareImageView({super.key, required this.imageBytes}); + const ShareImageView( + {super.key, + required this.imageBytes, + required this.isRealTwonly, + required this.maxShowTime}); final Uint8List imageBytes; + final bool isRealTwonly; + final int maxShowTime; @override State createState() => _ShareImageView(); @@ -92,11 +97,22 @@ class _ShareImageView extends State { _updateUsers(usersFiltered); } + void updateStatus(Int64 userId, bool checked) { + if (checked) { + if (widget.isRealTwonly) { + _selectedUserIds.clear(); + } + _selectedUserIds.add(userId); + } else { + _selectedUserIds.remove(userId); + } + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text(AppLocalizations.of(context)!.shareImageTitle), + title: Text(context.lang.shareImageTitle), ), body: Padding( padding: EdgeInsets.only(bottom: 20, left: 10, top: 20, right: 10), @@ -106,30 +122,24 @@ class _ShareImageView extends State { padding: EdgeInsets.symmetric(horizontal: 10), child: TextField( onChanged: _filterUsers, - decoration: getInputDecoration(context, - AppLocalizations.of(context)!.searchUsernameInput))), + decoration: getInputDecoration( + context, context.lang.searchUsernameInput))), const SizedBox(height: 10), BestFriendsSelector( users: _bestFriends, selectedUserIds: _selectedUserIds, maxTotalMediaCounter: maxTotalMediaCounter, - updateStatus: (userId, checked) { - if (checked) { - _selectedUserIds.add(userId); - } else { - _selectedUserIds.remove(userId); - } - }, + updateStatus: updateStatus, ), const SizedBox(height: 10), if (_otherUsers.isNotEmpty) - HeadLineComponent( - AppLocalizations.of(context)!.shareImageAllUsers), + HeadLineComponent(context.lang.shareImageAllUsers), Expanded( child: UserList( List.from(_otherUsers), maxTotalMediaCounter, selectedUserIds: _selectedUserIds, + updateStatus: updateStatus, ), ) ], @@ -145,7 +155,12 @@ class _ShareImageView extends State { FilledButton.icon( icon: FaIcon(FontAwesomeIcons.solidPaperPlane), onPressed: () async { - sendImage(_selectedUserIds.toList(), widget.imageBytes); + sendImage( + _selectedUserIds.toList(), + widget.imageBytes, + widget.isRealTwonly, + widget.maxShowTime, + ); // TODO: pop back to the HomeView page popUntil did not work. check later how to improve in case of pushing more then 2 Navigator.pop(context); @@ -158,7 +173,7 @@ class _ShareImageView extends State { ), ), label: Text( - AppLocalizations.of(context)!.shareImagedEditorSendImage, + context.lang.shareImagedEditorSendImage, style: TextStyle(fontSize: 17), ), ), @@ -172,7 +187,8 @@ class _ShareImageView extends State { class UserList extends StatelessWidget { const UserList(this.users, this.maxTotalMediaCounter, - {super.key, required this.selectedUserIds}); + {super.key, required this.selectedUserIds, required this.updateStatus}); + final Function(Int64, bool) updateStatus; final List users; final int maxTotalMediaCounter; final HashSet selectedUserIds; @@ -201,19 +217,11 @@ class UserList extends StatelessWidget { value: selectedUserIds.contains(user.userId), onChanged: (bool? value) { if (value == null) return; - if (value) { - selectedUserIds.add(user.userId); - } else { - selectedUserIds.remove(user.userId); - } + updateStatus(user.userId, value); }, ), onTap: () { - if (!selectedUserIds.contains(user.userId)) { - selectedUserIds.add(user.userId); - } else { - selectedUserIds.remove(user.userId); - } + updateStatus(user.userId, !selectedUserIds.contains(user.userId)); }, ); }, diff --git a/lib/src/views/chats/chat_item_details_view.dart b/lib/src/views/chats/chat_item_details_view.dart index e26ce92..63ce37e 100644 --- a/lib/src/views/chats/chat_item_details_view.dart +++ b/lib/src/views/chats/chat_item_details_view.dart @@ -10,7 +10,7 @@ import 'package:twonly/src/providers/api/api.dart'; import 'package:twonly/src/providers/download_change_provider.dart'; import 'package:twonly/src/providers/messages_change_provider.dart'; import 'package:twonly/src/views/chats/media_viewer_view.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:twonly/src/utils/misc.dart'; class ChatListEntry extends StatelessWidget { const ChatListEntry(this.message, this.user, this.lastMessageFromSameUser, @@ -188,8 +188,7 @@ class _ChatItemDetailsViewState extends State { } return Scaffold( appBar: AppBar( - title: Text(AppLocalizations.of(context)! - .chatListDetailTitle(widget.user.displayName)), + title: Text(context.lang.chatListDetailTitle(widget.user.displayName)), ), body: Column( children: [ @@ -226,8 +225,7 @@ class _ChatItemDetailsViewState extends State { _sendMessage(); }, decoration: InputDecoration( - hintText: - AppLocalizations.of(context)!.chatListDetailInput, + hintText: context.lang.chatListDetailInput, contentPadding: EdgeInsets.symmetric(horizontal: 10) // border: OutlineInputBorder(), ), diff --git a/lib/src/views/chats/chat_list_view.dart b/lib/src/views/chats/chat_list_view.dart index c5f16dd..862d266 100644 --- a/lib/src/views/chats/chat_list_view.dart +++ b/lib/src/views/chats/chat_list_view.dart @@ -17,7 +17,6 @@ import 'package:twonly/src/views/home_view.dart'; import 'package:twonly/src/views/chats/media_viewer_view.dart'; import 'package:twonly/src/views/profile_view.dart'; import 'package:twonly/src/views/chats/search_username_view.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter/material.dart'; class ChatItem { @@ -86,7 +85,10 @@ class _ChatListViewState extends State { // title: actions: [ NotificationBadge( - count: context.watch().newContactRequests, + count: context + .watch() + .newContactRequests + .toString(), child: IconButton( icon: FaIcon(FontAwesomeIcons.userPlus, size: 18), onPressed: () { @@ -131,10 +133,8 @@ class _ChatListViewState extends State { : globalUpdateOfHomeViewPageIndex(1); }, label: Text((activeUsers.isEmpty) - ? AppLocalizations.of(context)! - .chatListViewSearchUserNameBtn - : AppLocalizations.of(context)! - .chatListViewSendFirstTwonly)), + ? context.lang.chatListViewSearchUserNameBtn + : context.lang.chatListViewSendFirstTwonly)), ), ) : ListView.builder( diff --git a/lib/src/views/chats/search_username_view.dart b/lib/src/views/chats/search_username_view.dart index d3d5cc1..d1b4a31 100644 --- a/lib/src/views/chats/search_username_view.dart +++ b/lib/src/views/chats/search_username_view.dart @@ -1,6 +1,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:twonly/src/utils/misc.dart'; import 'package:provider/provider.dart'; import 'package:twonly/main.dart'; import 'package:twonly/src/components/headline.dart'; @@ -77,7 +77,7 @@ class _SearchUsernameView extends State { return Scaffold( appBar: AppBar( - title: Text(AppLocalizations.of(context)!.searchUsernameTitle), + title: Text(context.lang.searchUsernameTitle), ), body: Padding( padding: EdgeInsets.only(bottom: 20, left: 10, top: 20, right: 10), @@ -90,8 +90,8 @@ class _SearchUsernameView extends State { _addNewUser(context); }, controller: searchUserName, - decoration: getInputDecoration( - AppLocalizations.of(context)!.searchUsernameInput), + decoration: + getInputDecoration(context.lang.searchUsernameInput), ), ), const SizedBox(height: 20), @@ -101,8 +101,7 @@ class _SearchUsernameView extends State { showAlertDialog(context, "Coming soon", "This feature is not yet implemented!"); }, - label: - Text(AppLocalizations.of(context)!.searchUsernameQrCodeBtn), + label: Text(context.lang.searchUsernameQrCodeBtn), ), SizedBox(height: 30), if (context @@ -110,8 +109,7 @@ class _SearchUsernameView extends State { .allContacts .where((contact) => !contact.accepted) .isNotEmpty) - HeadLineComponent( - AppLocalizations.of(context)!.searchUsernameNewFollowerTitle), + HeadLineComponent(context.lang.searchUsernameNewFollowerTitle), Expanded( child: ContactsListView(), ) diff --git a/lib/src/views/onboarding/register_view.dart b/lib/src/views/onboarding/register_view.dart index 54556e6..e4f24de 100644 --- a/lib/src/views/onboarding/register_view.dart +++ b/lib/src/views/onboarding/register_view.dart @@ -3,7 +3,6 @@ import 'package:logging/logging.dart'; import 'package:twonly/main.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:twonly/src/model/json/user_data.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/signal.dart'; @@ -75,14 +74,14 @@ class _RegisterViewState extends State { children: [ const SizedBox(height: 50), Text( - AppLocalizations.of(context)!.registerTitle, + context.lang.registerTitle, textAlign: TextAlign.center, style: TextStyle(fontSize: 30), ), Padding( padding: EdgeInsets.symmetric(horizontal: 30), child: Text( - AppLocalizations.of(context)!.registerSlogan, + context.lang.registerSlogan, textAlign: TextAlign.center, style: TextStyle(fontSize: 12), ), @@ -92,7 +91,7 @@ class _RegisterViewState extends State { child: Padding( padding: EdgeInsets.only(left: 10, right: 10), child: Text( - AppLocalizations.of(context)!.registerUsernameSlogan, + context.lang.registerUsernameSlogan, textAlign: TextAlign.center, style: TextStyle(fontSize: 15), ), @@ -107,7 +106,7 @@ class _RegisterViewState extends State { ], style: TextStyle(fontSize: 17), decoration: getInputDecoration( - AppLocalizations.of(context)!.registerUsernameDecoration, + context.lang.registerUsernameDecoration, ), ), const SizedBox(height: 5), @@ -115,7 +114,7 @@ class _RegisterViewState extends State { child: Padding( padding: EdgeInsets.only(left: 10, right: 10), child: Text( - AppLocalizations.of(context)!.registerUsernameLimits, + context.lang.registerUsernameLimits, textAlign: TextAlign.center, style: TextStyle(fontSize: 7), ), @@ -166,7 +165,7 @@ class _RegisterViewState extends State { Colors.grey) : null), label: Text( - AppLocalizations.of(context)!.registerSubmitButton, + context.lang.registerSubmitButton, style: TextStyle(fontSize: 17), ), ),