From 2b82a7453789d3dc1d93ab5278162702cedbe0ed Mon Sep 17 00:00:00 2001 From: Carlito Date: Fri, 19 Nov 2021 10:32:26 +0100 Subject: [PATCH 01/17] Fix chat crash on Flutter 2.5.3 Replace the new overscroll.disallowGlow() by the (soon-to-be) deprecated overscroll.disallowGlow() as the former is gonna be introduced in Flutter 2.5.6 (not out yet). --- lib/widgets/chat/chat.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/widgets/chat/chat.dart b/lib/widgets/chat/chat.dart index ec7bdef..4619669 100644 --- a/lib/widgets/chat/chat.dart +++ b/lib/widgets/chat/chat.dart @@ -36,7 +36,7 @@ class ChatMessages extends StatelessWidget { alignment: Alignment.topCenter, child: NotificationListener( onNotification: (OverscrollIndicatorNotification overscroll) { - overscroll.disallowIndicator(); + overscroll.disallowGlow(); return false; }, child: ListView.builder( From 579fa4d09d364a59a7151c618de660d473469628 Mon Sep 17 00:00:00 2001 From: Carlito Date: Fri, 19 Nov 2021 10:41:05 +0100 Subject: [PATCH 02/17] Correct typos --- lib/models/classes.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/models/classes.dart b/lib/models/classes.dart index 485999e..8e3baf7 100644 --- a/lib/models/classes.dart +++ b/lib/models/classes.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; /// Customer Metadata, this contains the customer's information. class CustomerMetadata { - //Decalration of variables. + //Declaration of variables. /// This is the name of your user. String? name; @@ -65,7 +65,7 @@ class Props { /// Gradient to specify, should be used instead of primaryColor, DO NOT USE BOTH. Gradient? primaryGradient; - /// Required to create the wig¡dget, identifies the account. + /// Required to create the widget, identifies the account. String accountId; /// If you are self-hosting papercups, this base URL should be changed. @@ -95,7 +95,7 @@ class Props { /// This allows you to choose if you want to show your status. //bool showAgentAvailability; - /// This Will allow you to require an emial to chat. Not recommended for an app. + /// This Will allow you to require an email to chat. Not recommended for an app. bool requireEmailUpfront; /// Whether to allow scrolling. @@ -116,8 +116,8 @@ class Props { //Class definition. Props({ required this.accountId, - //this.agentAvailableText, - //this.agentUnavailableText, + // this.agentAvailableText = "We're available.", + // this.agentUnavailableText = "We're away at the moment.", this.baseUrl = "app.papercups.io", this.customer, this.closeIcon = const Icon(Icons.close_rounded), From d88ce840a229e147d926e8d02bcf690f32188b6a Mon Sep 17 00:00:00 2001 From: Carlito Date: Fri, 19 Nov 2021 14:05:47 +0100 Subject: [PATCH 03/17] Internationalize more strings & correct some typos --- lib/models/classes.dart | 27 +++++++++++++++++++----- lib/papercups_flutter.dart | 3 +-- lib/widgets/chat/chatBubble.dart | 13 ++++++------ lib/widgets/chat/chatMessage.dart | 7 +++--- lib/widgets/sendMessage/sendMessage.dart | 2 +- 5 files changed, 35 insertions(+), 17 deletions(-) diff --git a/lib/models/classes.dart b/lib/models/classes.dart index 8e3baf7..bc7adf4 100644 --- a/lib/models/classes.dart +++ b/lib/models/classes.dart @@ -42,7 +42,7 @@ class CustomerMetadata { /// This contains all the possible configurations for the chat widget. class Props { - //Decalration of variables. + //Declaration of variables. /// This is the top section of the widget, normally a welcome text. String title; @@ -81,7 +81,7 @@ class Props { String newMessagePlaceholder; /// This is the placeholder text in the email input section. - String enterEmailPlaceholer; + String enterEmailPlaceholder; /// This is the close button in the header section. Widget closeIcon; @@ -95,6 +95,18 @@ class Props { /// This allows you to choose if you want to show your status. //bool showAgentAvailability; + /// Error message displayed when the customer history couldn't be fetched. + String fetchHistoryErrorMessage; + + /// Message displayed when an attachment has been uploaded. + String attachmentUploadedMessage; + + /// Message displayed when a text has been copied after long press on a chat bubble. + String textCopiedMessage; + + /// Message displayed when the chat is loading. + String loadingMessage; + /// This Will allow you to require an email to chat. Not recommended for an app. bool requireEmailUpfront; @@ -107,10 +119,10 @@ class Props { /// header Padding EdgeInsetsGeometry headerPadding; - /// header Height + /// Header Height double? headerHeight; - /// message send icon for the chat section. + /// Message send icon for the chat section. Widget? sendIcon; //Class definition. @@ -145,7 +157,12 @@ class Props { ), this.companyName = "Bot", this.primaryGradient, - this.enterEmailPlaceholer = "Enter your email", + this.enterEmailPlaceholder = "Enter your email", + this.fetchHistoryErrorMessage = "There was an issue retrieving your details. Please try again!", + this.attachmentUploadedMessage = 'Attachment uploaded', + this.textCopiedMessage = "Text copied to clipboard", + this.attachmentNamePlaceholder = "No Name", + this.loadingMessage = "Loading...", this.sendIcon, }); } diff --git a/lib/papercups_flutter.dart b/lib/papercups_flutter.dart index c4705c5..7098eaa 100644 --- a/lib/papercups_flutter.dart +++ b/lib/papercups_flutter.dart @@ -120,9 +120,8 @@ class _PaperCupsWidgetState extends State { props: widget.props, ).then((failed) { if (failed) { - // TODO: Internationalize this Alert.show( - "There was an issue retrieving your details. Please try again!", + widget.props.getCustomerHistoryErrorMessage, context, backgroundColor: Theme.of(context).bottomAppBarColor, textStyle: Theme.of(context).textTheme.bodyText2, diff --git a/lib/widgets/chat/chatBubble.dart b/lib/widgets/chat/chatBubble.dart index 238c380..6edef0a 100644 --- a/lib/widgets/chat/chatBubble.dart +++ b/lib/widgets/chat/chatBubble.dart @@ -21,7 +21,7 @@ class ChatBubble extends StatelessWidget { required this.maxWidth, required this.text, required this.longDay, - required this.conatinsAttachment, + required this.containsAttachment, }) : super(key: key); final bool userSent; @@ -34,7 +34,7 @@ class ChatBubble extends StatelessWidget { final double maxWidth; final String text; final String? longDay; - final bool conatinsAttachment; + final bool containsAttachment; @override Widget build(BuildContext context) { @@ -122,12 +122,13 @@ class ChatBubble extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (conatinsAttachment) + if (containsAttachment) ...msg.attachments!.map((e) { return Attachment( userSent: userSent, props: widget.props, - fileName: e.fileName ?? "No Name", + // TODO: Internationalize this + fileName: e.fileName ?? widget.props.attachmentNamePlaceholder, textColor: widget.textColor, msgHasText: (msg.attachments!.length > 1 || msg.body != null), @@ -190,14 +191,14 @@ class ChatBubble extends StatelessWidget { ? Text( msg.user!.email!, style: TextStyle( - color: Theme.of(context).disabledColor.withOpacity(0.5), + color: Theme.of(context).textTheme.bodyText1!.color!.withOpacity(0.5), fontSize: 14, ), ) : Text( msg.user!.fullName!, style: TextStyle( - color: Theme.of(context).disabledColor.withOpacity(0.5), + color: Theme.of(context).textTheme.bodyText1!.color!.withOpacity(0.5), fontSize: 14, ), )), diff --git a/lib/widgets/chat/chatMessage.dart b/lib/widgets/chat/chatMessage.dart index 9ed11b1..b983abe 100644 --- a/lib/widgets/chat/chatMessage.dart +++ b/lib/widgets/chat/chatMessage.dart @@ -105,7 +105,8 @@ class _ChatMessageState extends State { longDay = DateFormat.yMMMMd(widget.locale).format(nextMsg.sentAt!); } catch (e) { print("ERROR: Error generating localized date!"); - longDay = "Loading..."; + // TODO: Internationalize this + longDay = widget.props.loadingMessage; } } if (userSent && isLast && widget.timeagoLocale != null) { @@ -132,7 +133,7 @@ class _ChatMessageState extends State { Clipboard.setData(data); // TODO: Internationalize this Alert.show( - "Text copied to clipboard", + widget.props.textCopiedMessage, context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, @@ -166,7 +167,7 @@ class _ChatMessageState extends State { maxWidth: maxWidth, text: text, longDay: longDay, - conatinsAttachment: containsAttachment, + containsAttachment: containsAttachment, ), ), ); diff --git a/lib/widgets/sendMessage/sendMessage.dart b/lib/widgets/sendMessage/sendMessage.dart index 0329cf9..1961e75 100644 --- a/lib/widgets/sendMessage/sendMessage.dart +++ b/lib/widgets/sendMessage/sendMessage.dart @@ -108,7 +108,7 @@ class _SendMessageState extends State { ); // TODO: Internationalize this. Alert.show( - "Attachment uploaded", + widget.props.attachmentUploadedMessage, context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, From 460d4d2bc5642eea39890de017092bf5b541d441 Mon Sep 17 00:00:00 2001 From: Carlito Date: Fri, 19 Nov 2021 14:06:45 +0100 Subject: [PATCH 04/17] Add todo for overscroll.disallowGrow() deprecation --- lib/widgets/chat/chat.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/widgets/chat/chat.dart b/lib/widgets/chat/chat.dart index 4619669..14366ee 100644 --- a/lib/widgets/chat/chat.dart +++ b/lib/widgets/chat/chat.dart @@ -36,6 +36,8 @@ class ChatMessages extends StatelessWidget { alignment: Alignment.topCenter, child: NotificationListener( onNotification: (OverscrollIndicatorNotification overscroll) { + // TODO: Replace when Flutter 2.6.0 stable will be out. + // overscroll.disallowIndicator(); overscroll.disallowGlow(); return false; }, From b4d0aee2febe2b48b566cec859cf44ed359b92e0 Mon Sep 17 00:00:00 2001 From: Carlito Date: Fri, 19 Nov 2021 14:29:22 +0100 Subject: [PATCH 05/17] Internationalize more strings --- lib/models/classes.dart | 22 +++++++++++++++++++--- lib/papercups_flutter.dart | 6 ++++-- lib/widgets/sendMessage/sendMessage.dart | 6 ++++-- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/lib/models/classes.dart b/lib/models/classes.dart index bc7adf4..b1f15ba 100644 --- a/lib/models/classes.dart +++ b/lib/models/classes.dart @@ -107,19 +107,31 @@ class Props { /// Message displayed when the chat is loading. String loadingMessage; + /// Text displayed when there's no Internet connection. + String noConnectionMessage; + + /// Text displayed in the retry button when the chat history couldn't be fetched. + String retryButtonLabel; + + /// Text displayed when the user decides to upload a file. + String fileLabel; + + /// Text displayed when the user decides to upload an image. + String imageLabel; + /// This Will allow you to require an email to chat. Not recommended for an app. bool requireEmailUpfront; /// Whether to allow scrolling. bool scrollEnabled; - /// Company name to show on greeting + /// Company name to show on greeting. String companyName; - /// header Padding + /// Header padding. EdgeInsetsGeometry headerPadding; - /// Header Height + /// Header height. double? headerHeight; /// Message send icon for the chat section. @@ -162,7 +174,11 @@ class Props { this.attachmentUploadedMessage = 'Attachment uploaded', this.textCopiedMessage = "Text copied to clipboard", this.attachmentNamePlaceholder = "No Name", + this.noConnectionMessage = "No Connection", this.loadingMessage = "Loading...", + this.retryButtonLabel = "Retry", + this.imageLabel = 'Image', + this.fileLabel = 'File', this.sendIcon, }); } diff --git a/lib/papercups_flutter.dart b/lib/papercups_flutter.dart index 7098eaa..61e8cb8 100644 --- a/lib/papercups_flutter.dart +++ b/lib/papercups_flutter.dart @@ -242,7 +242,8 @@ class _PaperCupsWidgetState extends State { color: Colors.grey, ), Text( - "No Connection", + // TODO: Internationalize this + widget.props.noConnectionMessage, style: Theme.of(context).textTheme.headline5!.copyWith( color: Colors.grey, ), @@ -281,7 +282,8 @@ class _PaperCupsWidgetState extends State { }); }, icon: Icon(Icons.refresh_rounded), - label: Text("Retry"), + // TODO: Internationalize this + label: Text(widget.props.retryButtonText), ) ], ), diff --git a/lib/widgets/sendMessage/sendMessage.dart b/lib/widgets/sendMessage/sendMessage.dart index 1961e75..7b28afd 100644 --- a/lib/widgets/sendMessage/sendMessage.dart +++ b/lib/widgets/sendMessage/sendMessage.dart @@ -166,7 +166,8 @@ class _SendMessageState extends State { foregroundColor: widget.textColor, child: Icon(Icons.insert_drive_file_outlined), ), - title: Text('File'), + // TODO: Internationalize this + title: Text(widget.props.fileLabel), contentPadding: EdgeInsets.all(0), ), ), @@ -178,7 +179,8 @@ class _SendMessageState extends State { foregroundColor: widget.textColor, child: Icon(Icons.image_outlined), ), - title: Text('Image'), + // TODO: Internationalize this + title: Text(widget.props.imageLabel), contentPadding: EdgeInsets.all(0), ), ), From c8c99ab1b752b9de2bfcc3222b0728af8261ffd6 Mon Sep 17 00:00:00 2001 From: Carlito Date: Fri, 19 Nov 2021 14:38:35 +0100 Subject: [PATCH 06/17] Rename getHistoryErrorMessage prop into historyFetchErrorMessage --- lib/models/classes.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/models/classes.dart b/lib/models/classes.dart index b1f15ba..b7c2c8c 100644 --- a/lib/models/classes.dart +++ b/lib/models/classes.dart @@ -96,7 +96,7 @@ class Props { //bool showAgentAvailability; /// Error message displayed when the customer history couldn't be fetched. - String fetchHistoryErrorMessage; + String historyFetchErrorMessage; /// Message displayed when an attachment has been uploaded. String attachmentUploadedMessage; @@ -170,7 +170,7 @@ class Props { this.companyName = "Bot", this.primaryGradient, this.enterEmailPlaceholder = "Enter your email", - this.fetchHistoryErrorMessage = "There was an issue retrieving your details. Please try again!", + this.historyFetchErrorMessage = "There was an issue retrieving your details. Please try again!", this.attachmentUploadedMessage = 'Attachment uploaded', this.textCopiedMessage = "Text copied to clipboard", this.attachmentNamePlaceholder = "No Name", From 1930b4d1a547f149f13fb35abcfce7dc1ae22c37 Mon Sep 17 00:00:00 2001 From: Carlito Date: Fri, 19 Nov 2021 14:43:40 +0100 Subject: [PATCH 07/17] Rename getHistoryErrorMessage prop into historyFetchErrorMessage --- lib/papercups_flutter.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/papercups_flutter.dart b/lib/papercups_flutter.dart index 61e8cb8..89b1d4e 100644 --- a/lib/papercups_flutter.dart +++ b/lib/papercups_flutter.dart @@ -121,7 +121,7 @@ class _PaperCupsWidgetState extends State { ).then((failed) { if (failed) { Alert.show( - widget.props.getCustomerHistoryErrorMessage, + widget.props.historyFetchErrorMessage, context, backgroundColor: Theme.of(context).bottomAppBarColor, textStyle: Theme.of(context).textTheme.bodyText2, From b3388fc664c82ffb6589be29b6f02bee3fa9657b Mon Sep 17 00:00:00 2001 From: Carlito Date: Fri, 19 Nov 2021 14:53:49 +0100 Subject: [PATCH 08/17] Correct typos --- lib/models/classes.dart | 3 +++ lib/papercups_flutter.dart | 2 +- lib/widgets/sendMessage/requireEmailUpfront.dart | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/models/classes.dart b/lib/models/classes.dart index b7c2c8c..75f4cbe 100644 --- a/lib/models/classes.dart +++ b/lib/models/classes.dart @@ -107,6 +107,9 @@ class Props { /// Message displayed when the chat is loading. String loadingMessage; + /// Text displayed when an attachment doesn't have a file name. + String attachmentNamePlaceholder; + /// Text displayed when there's no Internet connection. String noConnectionMessage; diff --git a/lib/papercups_flutter.dart b/lib/papercups_flutter.dart index 89b1d4e..1eb08dc 100644 --- a/lib/papercups_flutter.dart +++ b/lib/papercups_flutter.dart @@ -283,7 +283,7 @@ class _PaperCupsWidgetState extends State { }, icon: Icon(Icons.refresh_rounded), // TODO: Internationalize this - label: Text(widget.props.retryButtonText), + label: Text(widget.props.retryButtonLabel), ) ], ), diff --git a/lib/widgets/sendMessage/requireEmailUpfront.dart b/lib/widgets/sendMessage/requireEmailUpfront.dart index db0e6b6..6fc9040 100644 --- a/lib/widgets/sendMessage/requireEmailUpfront.dart +++ b/lib/widgets/sendMessage/requireEmailUpfront.dart @@ -86,7 +86,7 @@ class _RequireEmailUpfrontState extends State { style: BorderStyle.solid, ), ), - hintText: widget.props.enterEmailPlaceholer, + hintText: widget.props.enterEmailPlaceholder, hintStyle: const TextStyle( fontSize: 14, ), From 0ecc7e4370e20f540e92f180e203f2a7a73e90f3 Mon Sep 17 00:00:00 2001 From: Carlito Date: Fri, 19 Nov 2021 15:22:43 +0100 Subject: [PATCH 09/17] Replace fullName by displayName --- lib/models/user.dart | 6 +++--- lib/papercups_flutter.dart | 2 +- lib/utils/apiInteraction/getPastCustomerMessages.dart | 4 ++-- lib/utils/socket/joinConversation.dart | 4 ++-- lib/widgets/chat/chatBubble.dart | 8 ++++---- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/models/user.dart b/lib/models/user.dart index 91dd089..43abfa0 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -15,8 +15,8 @@ class User { /// The role of the agent. String? role; - /// The name of the agent. May be null. - String? fullName; + /// The display name of the agent. May be null. + String? displayName; /// The profile photo of the agent, must be a valid URL or null. String? profilePhotoUrl; @@ -27,7 +27,7 @@ class User { this.email, this.id, this.role, - this.fullName, + this.displayName, this.profilePhotoUrl, }); } diff --git a/lib/papercups_flutter.dart b/lib/papercups_flutter.dart index 1eb08dc..4cb7612 100644 --- a/lib/papercups_flutter.dart +++ b/lib/papercups_flutter.dart @@ -91,7 +91,7 @@ class _PaperCupsWidgetState extends State { createdAt: DateTime.now(), accountId: widget.props.accountId, user: User( - fullName: widget.props.companyName, + displayName: widget.props.companyName, ), userId: 0, id: "greeting", diff --git a/lib/utils/apiInteraction/getPastCustomerMessages.dart b/lib/utils/apiInteraction/getPastCustomerMessages.dart index 524e725..90f1c10 100644 --- a/lib/utils/apiInteraction/getPastCustomerMessages.dart +++ b/lib/utils/apiInteraction/getPastCustomerMessages.dart @@ -55,8 +55,8 @@ Future> getPastCustomerMessages( email: val["user"]["email"], id: val["user"]["id"], role: val["user"]["role"], - fullName: (val["user"]["full_name"] != null) - ? val["user"]["full_name"] + displayName: (val["user"]["display_name"] != null) + ? val["user"]["display_name"] : null, profilePhotoUrl: (val["user"]["profile_photo_url"] != null) ? val["user"]["profile_photo_url"] diff --git a/lib/utils/socket/joinConversation.dart b/lib/utils/socket/joinConversation.dart index 6cac98a..89210a6 100644 --- a/lib/utils/socket/joinConversation.dart +++ b/lib/utils/socket/joinConversation.dart @@ -65,8 +65,8 @@ PhoenixChannel? joinConversationAndListen({ id: event.payload!["user"]["id"], role: event.payload!["user"]["role"], fullName: - (event.payload!["user"]["full_name"] != null) - ? event.payload!["user"]["full_name"] + (event.payload!["user"]["display_name"] != null) + ? event.payload!["user"]["display_name"] : null, profilePhotoUrl: (event.payload!["user"] ["profile_photo_url"] != diff --git a/lib/widgets/chat/chatBubble.dart b/lib/widgets/chat/chatBubble.dart index 6edef0a..5b00ad2 100644 --- a/lib/widgets/chat/chatBubble.dart +++ b/lib/widgets/chat/chatBubble.dart @@ -72,7 +72,7 @@ class ChatBubble extends StatelessWidget { : null, child: (msg.user!.profilePhotoUrl != null) ? null - : (msg.user != null && msg.user!.fullName == null) + : (msg.user != null && msg.user!.displayName == null) ? Text( msg.user!.email! .substring(0, 1) @@ -80,7 +80,7 @@ class ChatBubble extends StatelessWidget { style: TextStyle(color: widget.textColor), ) : Text( - msg.user!.fullName! + msg.user!.displayName! .substring(0, 1) .toUpperCase(), style: TextStyle(color: widget.textColor), @@ -187,7 +187,7 @@ class ChatBubble extends StatelessWidget { if (!userSent && ((nextMsg.userId != msg.userId) || (isLast))) Padding( padding: EdgeInsets.only(left: 16, bottom: 5, top: 4), - child: (msg.user!.fullName == null) + child: (msg.user!.displayName == null) ? Text( msg.user!.email!, style: TextStyle( @@ -196,7 +196,7 @@ class ChatBubble extends StatelessWidget { ), ) : Text( - msg.user!.fullName!, + msg.user!.displayName!, style: TextStyle( color: Theme.of(context).textTheme.bodyText1!.color!.withOpacity(0.5), fontSize: 14, From d900b34f6033090cb38ba6be1cc8608efa3bcd1e Mon Sep 17 00:00:00 2001 From: Carlito Date: Fri, 19 Nov 2021 15:28:36 +0100 Subject: [PATCH 10/17] Replace fullName by displayName --- lib/utils/socket/joinConversation.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/socket/joinConversation.dart b/lib/utils/socket/joinConversation.dart index 89210a6..7449abc 100644 --- a/lib/utils/socket/joinConversation.dart +++ b/lib/utils/socket/joinConversation.dart @@ -64,7 +64,7 @@ PhoenixChannel? joinConversationAndListen({ email: event.payload!["user"]["email"], id: event.payload!["user"]["id"], role: event.payload!["user"]["role"], - fullName: + displayName: (event.payload!["user"]["display_name"] != null) ? event.payload!["user"]["display_name"] : null, From 04986de6d7809157891b704f457de3ccc7ffe111 Mon Sep 17 00:00:00 2001 From: Charles Mangwa Date: Mon, 22 Nov 2021 08:36:44 +0100 Subject: [PATCH 11/17] Internationalize more strings --- lib/models/classes.dart | 12 ++++++++++ .../fileInteraction/nativeFilePicker.dart | 23 +++++++++++-------- lib/utils/fileInteraction/webFilePicker.dart | 15 +++++++----- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/lib/models/classes.dart b/lib/models/classes.dart index 75f4cbe..18c4845 100644 --- a/lib/models/classes.dart +++ b/lib/models/classes.dart @@ -98,9 +98,18 @@ class Props { /// Error message displayed when the customer history couldn't be fetched. String historyFetchErrorMessage; + /// Error message displayed when an attachment could not be uploaded. + String attachmentUploadErrorMessage; + /// Message displayed when an attachment has been uploaded. String attachmentUploadedMessage; + /// Message displayed when an attachment is been uploaded. + String attachmentUploadingMessage; + + /// Message displayed after the percentage value of an attachment being uploaded. + String uploadedMessage; + /// Message displayed when a text has been copied after long press on a chat bubble. String textCopiedMessage; @@ -174,11 +183,14 @@ class Props { this.primaryGradient, this.enterEmailPlaceholder = "Enter your email", this.historyFetchErrorMessage = "There was an issue retrieving your details. Please try again!", + this.attachmentUploadErrorMessage = 'Failed to upload attachment', this.attachmentUploadedMessage = 'Attachment uploaded', this.textCopiedMessage = "Text copied to clipboard", + this.attachmentUploadingMessage = 'Uploading...', this.attachmentNamePlaceholder = "No Name", this.noConnectionMessage = "No Connection", this.loadingMessage = "Loading...", + this.uploadedMessage = "uploaded", this.retryButtonLabel = "Retry", this.imageLabel = 'Image', this.fileLabel = 'File', diff --git a/lib/utils/fileInteraction/nativeFilePicker.dart b/lib/utils/fileInteraction/nativeFilePicker.dart index 52cf6f6..e3193e6 100644 --- a/lib/utils/fileInteraction/nativeFilePicker.dart +++ b/lib/utils/fileInteraction/nativeFilePicker.dart @@ -5,11 +5,12 @@ import 'package:papercups_flutter/models/models.dart'; import 'package:papercups_flutter/utils/fileInteraction/uploadFile.dart'; import 'package:papercups_flutter/widgets/alert.dart'; -void nativeFilePicker( - {required FileType type, - required BuildContext context, - required widget, - required Function onUploadSuccess}) async { +void nativeFilePicker({ + required FileType type, + required BuildContext context, + required widget, + required Function onUploadSuccess, +}) async { try { final _paths = (await FilePicker.platform.pickFiles( type: type, @@ -17,7 +18,8 @@ void nativeFilePicker( ?.files; if (_paths != null && _paths.first.path != null) { Alert.show( - "Uploading...", + // TODO: Internationalize this + widget.props.attachmentUploadingMessage, context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, @@ -29,7 +31,8 @@ void nativeFilePicker( filePath: _paths.first.path, onUploadProgress: (sentBytes, totalBytes) { Alert.show( - "${(sentBytes * 100 / totalBytes).toStringAsFixed(2)}% uploaded", + // TODO: Internationalize this + "${(sentBytes * 100 / totalBytes).toStringAsFixed(2)}% ${widget.props.uploadedMessage}", context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, @@ -43,7 +46,8 @@ void nativeFilePicker( } } on PlatformException catch (_) { Alert.show( - "Failed to upload attachment", + // TODO: Internationalize this + widget.props.attachmentUploadErrorMessage, context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, @@ -53,7 +57,8 @@ void nativeFilePicker( throw _; } catch (_) { Alert.show( - "Failed to upload attachment", + // TODO: Internationalize this + widget.props.attachmentUploadErrorMessage, context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, diff --git a/lib/utils/fileInteraction/webFilePicker.dart b/lib/utils/fileInteraction/webFilePicker.dart index 4d4faa8..e0bbb49 100644 --- a/lib/utils/fileInteraction/webFilePicker.dart +++ b/lib/utils/fileInteraction/webFilePicker.dart @@ -5,16 +5,18 @@ import 'package:papercups_flutter/models/attachment.dart'; import 'package:papercups_flutter/utils/fileInteraction/uploadFile.dart'; import 'package:papercups_flutter/widgets/alert.dart'; -Future webFilePicker( - {required BuildContext context, - required Function onUploadSuccess, - required widget}) async { +Future webFilePicker({ + required BuildContext context, + required Function onUploadSuccess, + required widget, +}) async { try { var picked = await FilePicker.platform.pickFiles(); if (picked != null && picked.files.first.bytes != null) { Alert.show( - "Uploading...", + // TODO: Internationalize this + widget.props.attachmentUploadingMessage, context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, @@ -30,7 +32,8 @@ Future webFilePicker( } } on Exception catch (_) { Alert.show( - "Failed to upload attachment", + // TODO: Internationalize this + widget.props.attachmentUploadErrorMessage, context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, From 39260e9c495fca7916965e0e8d61a67dd8a1af52 Mon Sep 17 00:00:00 2001 From: Charles Mangwa Date: Thu, 2 Dec 2021 10:27:00 +0100 Subject: [PATCH 12/17] Fix dashboard messages not showing up live in profile/release mode --- lib/utils/socket/joinConversation.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/utils/socket/joinConversation.dart b/lib/utils/socket/joinConversation.dart index 7449abc..9e83838 100644 --- a/lib/utils/socket/joinConversation.dart +++ b/lib/utils/socket/joinConversation.dart @@ -30,7 +30,8 @@ PhoenixChannel? joinConversationAndListen({ conversation = null; } else { if (event.event.toString().contains("shout") || - event.event.toString().contains("message:created")) { + event.event.toString().contains("message:created") || + event.payload!["type"] == "reply") { // https://github.com/papercups-io/papercups/pull/488 // "message:created" is still not implemented see the PR above. if (event.payload!["customer"] == null) From 0a761dc2bf8b0113ee0c34d541fa0e06a3d97048 Mon Sep 17 00:00:00 2001 From: Charles Mangwa Date: Thu, 9 Dec 2021 12:02:30 +0100 Subject: [PATCH 13/17] Correct typos --- CHANGELOG.md | 10 +++++----- lib/models/models.dart | 2 +- ...agentAvaiability.dart => agentAvailability.dart} | 0 lib/widgets/sendMessage/sendMessage.dart | 13 ++++--------- lib/widgets/widgets.dart | 2 +- 5 files changed, 11 insertions(+), 16 deletions(-) rename lib/widgets/header/{agentAvaiability.dart => agentAvailability.dart} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4bed6c..a5248ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,11 +36,11 @@ These releases are maintenance related, addressing some issues found on pub.dev ## [2.0.0] - 16/02/2021. # 🎉 2.0 has landed! -This release brings major changes to how this package works. Most importantly, it is now completely native, ensuring speed and performace along with more customisability. This will also enable new features such as chat message notifications, in-app overlays, local message retention and many others, make sure to leave suggestions [here](https://github.com/papercups-io/papercups_flutter) for what to build next. +This release brings major changes to how this package works. Most importantly, it is now completely native, ensuring speed and performance along with more customisability. This will also enable new features such as chat message notifications, in-app overlays, local message retention and many others, make sure to leave suggestions [here](https://github.com/papercups-io/papercups_flutter) for what to build next. ### New Features * Native Dart implementation! -* Full themeing control w/gradients. +* Full theming control w/gradients. * Full Internationalization control - every part can be set to any language. * Dark mode * Added elevation option to message box @@ -71,7 +71,7 @@ Thanks to [@Fiyiin](https://github.com/Fiyiin) for helping me out building this ## [0.2.2] - 30/11/2020. -* ⚡️ Performance imporvements to `genIframeUrl` +* ⚡️ Performance improvements to `genIframeUrl` * ✨ Added `toMap()` and `toJsonString()` to the classes, better URI generation. ## [0.2.1] - 26/11/2020. @@ -85,7 +85,7 @@ Thanks to [@Fiyiin](https://github.com/Fiyiin) for helping me out building this ### Enhancements * 📝 Improved docs with higher coverage and syntax highlighting (Thanks to @Immortalin). -* ⚡️ Web widget is now constant. Better preformance. +* ⚡️ Web widget is now constant. Better performance. * ✨ Update example to show new features. ## [0.1.4] - 23/11/2020. @@ -94,7 +94,7 @@ Thanks to [@Fiyiin](https://github.com/Fiyiin) for helping me out building this ## [0.1.3] - 23/11/2020. -* 🐛 Add platfromView stub, should fix platfrom id and pub score +* 🐛 Add platformView stub, should fix platform id and pub score ## [0.1.2] - 22/11/2020. diff --git a/lib/models/models.dart b/lib/models/models.dart index 49167c6..ffbdfb3 100644 --- a/lib/models/models.dart +++ b/lib/models/models.dart @@ -1,4 +1,4 @@ -// Where all models are exported, more comformatble and comapct. +// Where all models are exported, more comfortable and compact. export 'classes.dart'; export 'conversation.dart'; export 'customer.dart'; diff --git a/lib/widgets/header/agentAvaiability.dart b/lib/widgets/header/agentAvailability.dart similarity index 100% rename from lib/widgets/header/agentAvaiability.dart rename to lib/widgets/header/agentAvailability.dart diff --git a/lib/widgets/sendMessage/sendMessage.dart b/lib/widgets/sendMessage/sendMessage.dart index 7b28afd..9b47756 100644 --- a/lib/widgets/sendMessage/sendMessage.dart +++ b/lib/widgets/sendMessage/sendMessage.dart @@ -5,7 +5,6 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart' show kIsWeb; import 'package:papercups_flutter/utils/fileInteraction/nativeFilePicker.dart'; -import 'package:papercups_flutter/utils/fileInteraction/uploadFile.dart'; import 'package:papercups_flutter/utils/fileInteraction/webFilePicker.dart'; import '../../models/models.dart'; import '../../utils/utils.dart'; @@ -106,9 +105,8 @@ class _SendMessageState extends State { fileIds, true, ); - // TODO: Internationalize this. Alert.show( - widget.props.attachmentUploadedMessage, + widget.props.translations.attachmentUploadedText, context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, @@ -119,7 +117,6 @@ class _SendMessageState extends State { } // TODO: Separate this widget -// TODO: Internationalize alerts and popups Widget _getFilePicker() { if (kIsWeb) { return IconButton( @@ -166,8 +163,7 @@ class _SendMessageState extends State { foregroundColor: widget.textColor, child: Icon(Icons.insert_drive_file_outlined), ), - // TODO: Internationalize this - title: Text(widget.props.fileLabel), + title: Text(widget.props.translations.fileText), contentPadding: EdgeInsets.all(0), ), ), @@ -179,8 +175,7 @@ class _SendMessageState extends State { foregroundColor: widget.textColor, child: Icon(Icons.image_outlined), ), - // TODO: Internationalize this - title: Text(widget.props.imageLabel), + title: Text(widget.props.translations.imageText), contentPadding: EdgeInsets.all(0), ), ), @@ -220,7 +215,7 @@ class _SendMessageState extends State { child: TextField( decoration: InputDecoration( border: InputBorder.none, - hintText: widget.props.newMessagePlaceholder, + hintText: widget.props.translations.newMessagePlaceholder, hintStyle: const TextStyle( fontSize: 14, ), diff --git a/lib/widgets/widgets.dart b/lib/widgets/widgets.dart index b1fabf0..7e050e5 100644 --- a/lib/widgets/widgets.dart +++ b/lib/widgets/widgets.dart @@ -1,5 +1,5 @@ /// Exports for the widgets, new widgets should be added here and this is the file that should be imported instead of the specific widget. -export 'header/agentAvaiability.dart'; +export 'header/agentAvailability.dart'; export 'chat/chat.dart'; export 'header/header.dart'; export 'sendMessage/sendMessage.dart'; From 5fa1f7836230ab17d48788d44fa50acdd0a087b6 Mon Sep 17 00:00:00 2001 From: Charles Mangwa Date: Thu, 9 Dec 2021 12:04:24 +0100 Subject: [PATCH 14/17] BREAKING: Implement PapercupsIntl() --- README.md | 66 ++++--- example/lib/main.dart | 10 +- lib/models/classes.dart | 185 ++++++++++-------- lib/papercups_flutter.dart | 26 +-- .../fileInteraction/nativeFilePicker.dart | 12 +- lib/utils/fileInteraction/webFilePicker.dart | 6 +- lib/utils/socket/joinConversation.dart | 30 +-- lib/widgets/chat/chatBubble.dart | 19 +- lib/widgets/chat/chatMessage.dart | 6 +- lib/widgets/header/header.dart | 4 +- .../sendMessage/requireEmailUpfront.dart | 5 +- test/papercups_flutter_test.dart | 36 ++-- 12 files changed, 216 insertions(+), 189 deletions(-) diff --git a/README.md b/README.md index 927efa1..2bc55ae 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,9 @@ Integration with your app requires just a few lines of code, add the following w import 'package:papercups_flutter/papercups_flutter.dart'; PaperCupsWidget( - props: Props( + props: Props( accountId: "xxxxxxxx-xxxxxxx-xxxx-xxxxxx", //Your account id goes here. - ), + ), ), ``` @@ -31,40 +31,60 @@ That should get you up and running in just a few seconds ⚡️. ## Configuration -### Available PaperCupsWidget arguments +### Available `PaperCupsWidget` arguments | Parameter | Type | Value | Default | | :--- | :--- | :----- | :------ | -| **`props`** | `Props` | **Required**, here is where all of the config for the chat is contained.| N/A | | **`dateLocale`** | `String` |Locale for the date, use the locales from the `intl` package.| `"en-US"` | -| **`timeagoLocale`** | `dynamic` | Check https://github.com/andresaraujo/timeago.dart/tree/master/timeago/lib/src/messages for the available classes.| N/A | -| **`sendingText`** | `String` | Text to show while message is sending.| `Sending...` | -| **`sentText`** | `String` | Text to show when the messgae is sent.| `Sent` | | **`floatingSendMessage`** | `bool` | Wether to have the message box floating.| `false` | +| **`props`** | `Props` | **Required**, here is where all of the config for the chat is contained.| N/A | +| **`timeagoLocale`** | `dynamic` | Check [`timeago` messages](https://github.com/andresaraujo/timeago.dart/tree/master/timeago/lib/src/messages) for the available classes.| N/A | -### Available Props paramaters +### Available `Props` parameters | Prop | Type | Value | Default | | :--- | :--- | :----- | :------ | -| **`accountId`** | `string` | **Required**, your Papercups account token | N/A | -| **`title`** | `string` | The title in the header of your chat widget | Welcome! | -| **`subtitle`** | `string` | The subtitle in the header of your chat widget | How can we help you? | -| **`newMessagePlaceholder`** | `string` | The placeholder text in the new message input | Start typing... | -| **`primaryColor`** | `Color` | The theme color of your chat widget | `Theme.of(context).primaryColor` without alpha | -| **`primaryGradient`** | `Gradient` | Gradient to specify, should be used instead of primaryColor, DO NOT USE BOTH. | N/A | -| **`greeting`** | `string` | An optional initial message to greet your customers with | N/A | +| **`accountId`** | `String` | **Required**, your Papercups account token | N/A | +| **`baseUrl`** | `String` | The base URL of your API if you're self-hosting Papercups. Ensure you do not include the protocol (https) of a trailing dash (/) | app.papercups.io | | **`customer`** | `CustomerMetadata` | Identifying information for the customer, including `name`, `email`, `external_id`, and `metadata` (for any custom fields) | N/A | -| **`baseUrl`** | `string` | The base URL of your API if you're self-hosting Papercups. Ensure you do not include the protocol (https) of a trailing dash (/) | app.papercups.io | +| **`primaryColor`** | `Color` | The theme color of your chat widget | `Theme.of(context).primaryColor` without alpha | +| **`primaryGradient`** | `Gradient` | Gradient to specify, should be used instead of primaryColor, **DO NOT USE BOTH** | N/A | | **`requireEmailUpfront`** | `boolean` | If you want to require unidentified customers to provide their email before they can message you | `false` | -| **`companyName`** | `String` | Company name to show on greeting | `"Bot"` | -| **`enterEmailPlaceholer`** | `String` | This is the placeholder text in the email input section | `"Enter your email"` | +| **`translations`** | `PapercupsIntl` | If you want to override the default `EN` translations displayed by the widget | `PapercupsIntl()` | -### Available CustomerMetaData paramaters +### Available `CustomerMetaData` parameters | Parameters | Type | Value | Default | | :--- | :--- | :----- | :------ | -| **`email`** | `string` | The customer's email| N/A | -| **`externalId`** | `string` | The customer's external ID | N/A | -| **`name`** | `string` | The customer's name | N/A | -| **`otherMetadata`** | `Map` | Extra metadata to pass such as OS info. | N/A | +| **`email`** | `String` | The customer's email| N/A | +| **`externalId`** | `String` | The customer's external ID | N/A | +| **`name`** | `String` | The customer's name | N/A | +| **`otherMetadata`** | `Map` | Extra metadata to pass such as OS info | N/A | + +### Available `PapercupsIntl` parameters +| Parameters | Type | Value | Default | +| :--- | :--- | :----- | :------ | +| **`agentAvailableText`** | `String` | This text will be shown if the showAgentAvailability is true and you are online | `"We're available."` | +| **`agentUnavailableText`** | `String` | This text will be shown if the showAgentAvailability is true and you are online | `"We're away at the moment."` | +| **`attachmentNamePlaceholder`** | `String` | Text displayed when an attachment doesn't have a file name | `"No Name"` | +| **`attachmentUploadErrorText`** | `String` | Error message displayed when an attachment could not be uploaded | `"Failed to upload attachment"` | +| **`attachmentUploadedText`** | `String` | Text displayed when an attachment has been uploaded | `"Attachment uploaded"` | +| **`attachmentUploadingText`** | `String` | Text displayed when an attachment is been uploaded | `"Uploading..."` | +| **`companyName`** | `String` | Company name to show on greeting | `"Bot"` | +| **`enterEmailPlaceholder`** | `String` | This is the placeholder text in the email input section | `"Enter your email"` | +| **`fileText`** | `String` | Text displayed on the tile where the user decides to upload a file | `"File"` | +| **`greeting`** | `String` | An optional initial message to greet your customers with | N/A | +| **`historyFetchErrorText`** | `String` | Error message displayed when the customer history couldn't be fetched | `"There was an issue retrieving your details. Please try again!` | +| **`imageText`** | `String` | Text displayed on the tile where the user decides to upload an image | `"Image` | +| **`loadingText`** | `String` | Text displayed when the chat is loading | `"Loading..."` | +| **`newMessagePlaceholder`** | `String` | The placeholder text in the new message input | `"Start typing..."` | +| **`noConnectionText`** | `String` | The placeholder text in the new message input | `"No Connection"` | +| **`retryButtonLabel`** | `String` | Label used in the retry button when the chat history couldn't be fetched | `"Retry"` | +| **`sendingText`** | `String` | Text to show while message is sending | `"Sending..."` | +| **`sentText`** | `String` | Text to show when the message is sent | `"Sent"` | +| **`subtitle`** | `String` | The subtitle in the header of your chat widget | `"How can we help you?"` | +| **`textCopiedText`** | `String` | Text displayed when a text has been copied after long press on a chat bubble | `"Text copied to clipboard"` | +| **`title`** | `String` | The title in the header of your chat widget | `"Welcome!"` | +| **`uploadedText`** | `String` | Text displayed after the percentage value of an attachment being uploaded | `"uploaded"` | + ## Supporters [![Stargazers repo roster for @papercups-io/papercups_flutter](https://reporoster.com/stars/papercups-io/papercups_flutter)](https://github.com/papercups-io/papercups_flutter/stargazers) [![Forkers repo roster for @papercups-io/papercups_flutter](https://reporoster.com/forks/papercups-io/papercups_flutter)](https://github.com/papercups-io/papercups_flutter/network/members) diff --git a/example/lib/main.dart b/example/lib/main.dart index 2f6eb8e..c43868a 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -307,11 +307,13 @@ class _MyHomePageState extends State { }, props: Props( accountId: "843d8a14-8cbc-43c7-9dc9-3445d427ac4e", - title: titleController.text, + translations: PapercupsIntl( + title: titleController.text, + subtitle: subtitleController.text, + greeting: + "Hello, have any questions or feedback? Let me know below!", + ), primaryColor: color, - greeting: - "Hello, have any questions or feedback? Let me know below!", - subtitle: subtitleController.text, customer: CustomerMetadata( email: "admin@aguilaair.tech", externalId: "123", diff --git a/lib/models/classes.dart b/lib/models/classes.dart index 18c4845..d7f5ab1 100644 --- a/lib/models/classes.dart +++ b/lib/models/classes.dart @@ -40,51 +40,71 @@ class CustomerMetadata { } } -/// This contains all the possible configurations for the chat widget. -class Props { - //Declaration of variables. +/// This class contains all the text that can be displayed by the widget. +class PapercupsIntl { + /// Company name to show on greeting. + final String companyName; /// This is the top section of the widget, normally a welcome text. - String title; + final String title; - /// This is the top widget text style - TextStyle titleStyle; + /// This is a smaller piece of text under the title. + final String subtitle; - /// This is the top widget title alignment - TextAlign titleAlign; + /// This is the first message sent by you during the conversation. + final String? greeting; - /// This is a smaller piece of text under the title. - String subtitle; + /// Text to show while message is sending. + /// + /// Default is `"Sending..."` + final String sendingText; - /// This is the subtitle TextStyle - TextStyle? subtitleStyle; + /// Text to show when the message is sent + /// + /// Default is `"Sent"` (time will be added after). + final String sentText; - /// Color in which the header is going to be in, if not defined will be primary color used in app. - Color? primaryColor; + /// This is the placeholder text in the input section. + final String newMessagePlaceholder; - /// Gradient to specify, should be used instead of primaryColor, DO NOT USE BOTH. - Gradient? primaryGradient; + /// This is the placeholder text in the email input section. + final String enterEmailPlaceholder; - /// Required to create the widget, identifies the account. - String accountId; + /// Error message displayed when the customer history couldn't be fetched. + final String historyFetchErrorText; - /// If you are self-hosting papercups, this base URL should be changed. - String baseUrl; + /// Error message displayed when an attachment could not be uploaded. + final String attachmentUploadErrorText; - /// This is the first message sent by you during the conversation. - String? greeting; + /// Text displayed when an attachment has been uploaded. + final String attachmentUploadedText; - /// This is the data that you will see on your dashboard such as the email or the name of the person. - CustomerMetadata? customer; + /// Text displayed when an attachment is been uploaded. + final String attachmentUploadingText; - /// This is the placeholder text in the input section. - String newMessagePlaceholder; + /// Text displayed after the percentage value of an attachment being uploaded. + final String uploadedText; - /// This is the placeholder text in the email input section. - String enterEmailPlaceholder; + /// Text displayed when a text has been copied after long press on a chat bubble. + final String textCopiedText; - /// This is the close button in the header section. - Widget closeIcon; + /// Text displayed when the chat is loading. + final String loadingText; + + /// Text displayed when an attachment doesn't have a file name. + final String attachmentNamePlaceholder; + + /// Text displayed when there's no Internet connection. + final String noConnectionText; + + /// Label used in the retry button when the chat history couldn't be fetched. + final String retryButtonLabel; + + /// Text displayed on the tile where the user decides to upload a file. + final String fileText; + + /// Text displayed on the tile where the user decides to upload an image. + final String imageText; /// This text will be shown if the showAgentAvailability is true and you are online. //String agentAvailableText; @@ -92,44 +112,64 @@ class Props { /// This text will be shown if the showAgentAvailability is true and you are offline. //String agentUnavailableText; - /// This allows you to choose if you want to show your status. - //bool showAgentAvailability; - - /// Error message displayed when the customer history couldn't be fetched. - String historyFetchErrorMessage; - - /// Error message displayed when an attachment could not be uploaded. - String attachmentUploadErrorMessage; + const PapercupsIntl({ + this.historyFetchErrorText = + "There was an issue retrieving your details. Please try again!", + this.attachmentUploadErrorText = 'Failed to upload attachment', + // this.agentUnavailableText = "We're away at the moment.", + this.attachmentUploadedText = 'Attachment uploaded', + this.textCopiedText = "Text copied to clipboard", + this.attachmentUploadingText = 'Uploading...', + this.enterEmailPlaceholder = "Enter your email", + // this.agentAvailableText = "We're available.", + this.newMessagePlaceholder = "Start typing...", + this.noConnectionText = "No Connection", + this.attachmentNamePlaceholder = "No Name", + this.subtitle = "How can we help you?", + this.loadingText = "Loading...", + this.uploadedText = "uploaded", + this.retryButtonLabel = "Retry", + this.sendingText = "Sending...", + this.imageText = 'Image', + this.companyName = "Bot", + this.fileText = 'File', + this.sentText = "Sent", + this.title = "Welcome!", + this.greeting, + }); +} - /// Message displayed when an attachment has been uploaded. - String attachmentUploadedMessage; +/// This contains all the possible configurations for the chat widget. +class Props { + /// This is the top widget text style + TextStyle titleStyle; - /// Message displayed when an attachment is been uploaded. - String attachmentUploadingMessage; + /// This is the top widget title alignment + TextAlign titleAlign; - /// Message displayed after the percentage value of an attachment being uploaded. - String uploadedMessage; + /// This is the subtitle TextStyle + TextStyle? subtitleStyle; - /// Message displayed when a text has been copied after long press on a chat bubble. - String textCopiedMessage; + /// Color in which the header is going to be in, if not defined will be primary color used in app. + Color? primaryColor; - /// Message displayed when the chat is loading. - String loadingMessage; + /// Gradient to specify, should be used instead of primaryColor, DO NOT USE BOTH. + Gradient? primaryGradient; - /// Text displayed when an attachment doesn't have a file name. - String attachmentNamePlaceholder; + /// Required to create the widget, identifies the account. + String accountId; - /// Text displayed when there's no Internet connection. - String noConnectionMessage; + /// If you are self-hosting papercups, this base URL should be changed. + String baseUrl; - /// Text displayed in the retry button when the chat history couldn't be fetched. - String retryButtonLabel; + /// This is the data that you will see on your dashboard such as the email or the name of the person. + CustomerMetadata? customer; - /// Text displayed when the user decides to upload a file. - String fileLabel; + /// This is the close button in the header section. + Widget closeIcon; - /// Text displayed when the user decides to upload an image. - String imageLabel; + /// This allows you to choose if you want to show your status. + //bool showAgentAvailability; /// This Will allow you to require an email to chat. Not recommended for an app. bool requireEmailUpfront; @@ -137,9 +177,6 @@ class Props { /// Whether to allow scrolling. bool scrollEnabled; - /// Company name to show on greeting. - String companyName; - /// Header padding. EdgeInsetsGeometry headerPadding; @@ -149,22 +186,20 @@ class Props { /// Message send icon for the chat section. Widget? sendIcon; - //Class definition. + /// This contains all the texts that can be displayed by the widget. + PapercupsIntl translations; + + // Class definition. Props({ required this.accountId, - // this.agentAvailableText = "We're available.", - // this.agentUnavailableText = "We're away at the moment.", this.baseUrl = "app.papercups.io", + this.translations = const PapercupsIntl(), this.customer, this.closeIcon = const Icon(Icons.close_rounded), - this.greeting, - this.newMessagePlaceholder = "Start typing...", this.primaryColor, this.requireEmailUpfront = false, this.scrollEnabled = true, //this.showAgentAvailability = false, - this.subtitle = "How can we help you?", - this.title = "Welcome!", this.titleStyle = const TextStyle( color: Colors.white, fontSize: 21, @@ -179,21 +214,7 @@ class Props { left: 20, bottom: 12, ), - this.companyName = "Bot", this.primaryGradient, - this.enterEmailPlaceholder = "Enter your email", - this.historyFetchErrorMessage = "There was an issue retrieving your details. Please try again!", - this.attachmentUploadErrorMessage = 'Failed to upload attachment', - this.attachmentUploadedMessage = 'Attachment uploaded', - this.textCopiedMessage = "Text copied to clipboard", - this.attachmentUploadingMessage = 'Uploading...', - this.attachmentNamePlaceholder = "No Name", - this.noConnectionMessage = "No Connection", - this.loadingMessage = "Loading...", - this.uploadedMessage = "uploaded", - this.retryButtonLabel = "Retry", - this.imageLabel = 'Image', - this.fileLabel = 'File', this.sendIcon, }); } diff --git a/lib/papercups_flutter.dart b/lib/papercups_flutter.dart index 4cb7612..1ea12c5 100644 --- a/lib/papercups_flutter.dart +++ b/lib/papercups_flutter.dart @@ -26,12 +26,6 @@ class PaperCupsWidget extends StatefulWidget { /// for the available classes. final timeagoLocale; - /// Text to show while message is sending. Default `"Sending..."` - final String sendingText; - - /// Text to show when the messgae is sent. Default is `"Sent"` time will be added after. - final String sentText; - /// If not null, close button will be shown. final Function? closeAction; @@ -45,8 +39,6 @@ class PaperCupsWidget extends StatefulWidget { required this.props, this.dateLocale = "en-US", this.timeagoLocale, - this.sendingText = "Sending...", - this.sentText = "Sent", this.closeAction, this.floatingSendMessage = false, this.onMessageBubbleTap, @@ -80,18 +72,18 @@ class _PaperCupsWidgetState extends State { @override void didChangeDependencies() { - if (widget.props.greeting != null && + if (widget.props.translations.greeting != null && _conversation.messages .indexWhere((element) => element.id == "greeting") == -1) { _conversation.messages.add( PapercupsMessage( - body: widget.props.greeting, + body: widget.props.translations.greeting, sentAt: DateTime.now(), createdAt: DateTime.now(), accountId: widget.props.accountId, user: User( - displayName: widget.props.companyName, + displayName: widget.props.translations.companyName, ), userId: 0, id: "greeting", @@ -121,7 +113,7 @@ class _PaperCupsWidgetState extends State { ).then((failed) { if (failed) { Alert.show( - widget.props.historyFetchErrorMessage, + widget.props.translations.historyFetchErrorText, context, backgroundColor: Theme.of(context).bottomAppBarColor, textStyle: Theme.of(context).textTheme.bodyText2, @@ -242,8 +234,7 @@ class _PaperCupsWidgetState extends State { color: Colors.grey, ), Text( - // TODO: Internationalize this - widget.props.noConnectionMessage, + widget.props.translations.noConnectionText, style: Theme.of(context).textTheme.headline5!.copyWith( color: Colors.grey, ), @@ -282,8 +273,7 @@ class _PaperCupsWidgetState extends State { }); }, icon: Icon(Icons.refresh_rounded), - // TODO: Internationalize this - label: Text(widget.props.retryButtonLabel), + label: Text(widget.props.translations.retryButtonLabel), ) ], ), @@ -306,8 +296,8 @@ class _PaperCupsWidgetState extends State { _sending, widget.dateLocale, widget.timeagoLocale, - widget.sendingText, - widget.sentText, + widget.props.translations.sendingText, + widget.props.translations.sentText, textColor, widget.onMessageBubbleTap, ), diff --git a/lib/utils/fileInteraction/nativeFilePicker.dart b/lib/utils/fileInteraction/nativeFilePicker.dart index e3193e6..53ec39a 100644 --- a/lib/utils/fileInteraction/nativeFilePicker.dart +++ b/lib/utils/fileInteraction/nativeFilePicker.dart @@ -18,8 +18,7 @@ void nativeFilePicker({ ?.files; if (_paths != null && _paths.first.path != null) { Alert.show( - // TODO: Internationalize this - widget.props.attachmentUploadingMessage, + widget.props.translations.attachmentUploadingText, context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, @@ -31,8 +30,7 @@ void nativeFilePicker({ filePath: _paths.first.path, onUploadProgress: (sentBytes, totalBytes) { Alert.show( - // TODO: Internationalize this - "${(sentBytes * 100 / totalBytes).toStringAsFixed(2)}% ${widget.props.uploadedMessage}", + "${(sentBytes * 100 / totalBytes).toStringAsFixed(2)}% ${widget.props.translations.uploadedText}", context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, @@ -46,8 +44,7 @@ void nativeFilePicker({ } } on PlatformException catch (_) { Alert.show( - // TODO: Internationalize this - widget.props.attachmentUploadErrorMessage, + widget.props.translations.attachmentUploadErrorText, context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, @@ -57,8 +54,7 @@ void nativeFilePicker({ throw _; } catch (_) { Alert.show( - // TODO: Internationalize this - widget.props.attachmentUploadErrorMessage, + widget.props.translations.attachmentUploadErrorText, context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, diff --git a/lib/utils/fileInteraction/webFilePicker.dart b/lib/utils/fileInteraction/webFilePicker.dart index e0bbb49..59c5dab 100644 --- a/lib/utils/fileInteraction/webFilePicker.dart +++ b/lib/utils/fileInteraction/webFilePicker.dart @@ -15,8 +15,7 @@ Future webFilePicker({ if (picked != null && picked.files.first.bytes != null) { Alert.show( - // TODO: Internationalize this - widget.props.attachmentUploadingMessage, + widget.props.translations.attachmentUploadingText, context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, @@ -32,8 +31,7 @@ Future webFilePicker({ } } on Exception catch (_) { Alert.show( - // TODO: Internationalize this - widget.props.attachmentUploadErrorMessage, + widget.props.translations.attachmentUploadErrorText, context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, diff --git a/lib/utils/socket/joinConversation.dart b/lib/utils/socket/joinConversation.dart index 9e83838..f72305f 100644 --- a/lib/utils/socket/joinConversation.dart +++ b/lib/utils/socket/joinConversation.dart @@ -44,8 +44,7 @@ PhoenixChannel? joinConversationAndListen({ customerId: event.payload!["customer_id"], id: event.payload!["id"], attachments: (event.payload!["attachments"] != null) - ? (event.payload!["attachments"] as List) - .map((attachment) { + ? (event.payload!["attachments"] as List).map((attachment) { return PapercupsAttachment( contentType: attachment["content_type"], fileName: attachment["filename"], @@ -55,8 +54,7 @@ PhoenixChannel? joinConversationAndListen({ }).toList() : null, fileIds: (event.payload!["attachments"] != null) - ? (event.payload!["attachments"] as List) - .map((attachment) { + ? (event.payload!["attachments"] as List).map((attachment) { return attachment["id"] as String; }).toList() : null, @@ -65,13 +63,10 @@ PhoenixChannel? joinConversationAndListen({ email: event.payload!["user"]["email"], id: event.payload!["user"]["id"], role: event.payload!["user"]["role"], - displayName: - (event.payload!["user"]["display_name"] != null) - ? event.payload!["user"]["display_name"] - : null, - profilePhotoUrl: (event.payload!["user"] - ["profile_photo_url"] != - null) + displayName: (event.payload!["user"]["display_name"] != null) + ? event.payload!["user"]["display_name"] + : null, + profilePhotoUrl: (event.payload!["user"]["profile_photo_url"] != null) ? event.payload!["user"]["profile_photo_url"] : null, ) @@ -83,15 +78,10 @@ PhoenixChannel? joinConversationAndListen({ ) : null, userId: event.payload!["user_id"], - createdAt: event.payload!["created_at"] != null - ? parseDateFromUTC(event.payload!["created_at"]) - : null, - seenAt: event.payload!["seen_at"] != null - ? parseDateFromUTC(event.payload!["seen_at"]) - : null, - sentAt: event.payload!["sent_at"] != null - ? parseDateFromUTC(event.payload!["sent_at"]) - : null, + createdAt: + event.payload!["created_at"] != null ? parseDateFromUTC(event.payload!["created_at"]) : null, + seenAt: event.payload!["seen_at"] != null ? parseDateFromUTC(event.payload!["seen_at"]) : null, + sentAt: event.payload!["sent_at"] != null ? parseDateFromUTC(event.payload!["sent_at"]) : null, ), ); }, animate: true); diff --git a/lib/widgets/chat/chatBubble.dart b/lib/widgets/chat/chatBubble.dart index 5b00ad2..cd5a601 100644 --- a/lib/widgets/chat/chatBubble.dart +++ b/lib/widgets/chat/chatBubble.dart @@ -72,7 +72,8 @@ class ChatBubble extends StatelessWidget { : null, child: (msg.user!.profilePhotoUrl != null) ? null - : (msg.user != null && msg.user!.displayName == null) + : (msg.user != null && + msg.user!.displayName == null) ? Text( msg.user!.email! .substring(0, 1) @@ -127,8 +128,8 @@ class ChatBubble extends StatelessWidget { return Attachment( userSent: userSent, props: widget.props, - // TODO: Internationalize this - fileName: e.fileName ?? widget.props.attachmentNamePlaceholder, + fileName: e.fileName ?? + widget.props.translations.attachmentNamePlaceholder, textColor: widget.textColor, msgHasText: (msg.attachments!.length > 1 || msg.body != null), @@ -191,14 +192,22 @@ class ChatBubble extends StatelessWidget { ? Text( msg.user!.email!, style: TextStyle( - color: Theme.of(context).textTheme.bodyText1!.color!.withOpacity(0.5), + color: Theme.of(context) + .textTheme + .bodyText1! + .color! + .withOpacity(0.5), fontSize: 14, ), ) : Text( msg.user!.displayName!, style: TextStyle( - color: Theme.of(context).textTheme.bodyText1!.color!.withOpacity(0.5), + color: Theme.of(context) + .textTheme + .bodyText1! + .color! + .withOpacity(0.5), fontSize: 14, ), )), diff --git a/lib/widgets/chat/chatMessage.dart b/lib/widgets/chat/chatMessage.dart index b983abe..e24cf30 100644 --- a/lib/widgets/chat/chatMessage.dart +++ b/lib/widgets/chat/chatMessage.dart @@ -105,8 +105,7 @@ class _ChatMessageState extends State { longDay = DateFormat.yMMMMd(widget.locale).format(nextMsg.sentAt!); } catch (e) { print("ERROR: Error generating localized date!"); - // TODO: Internationalize this - longDay = widget.props.loadingMessage; + longDay = widget.props.translations.loadingText; } } if (userSent && isLast && widget.timeagoLocale != null) { @@ -131,9 +130,8 @@ class _ChatMessageState extends State { HapticFeedback.vibrate(); final data = ClipboardData(text: text); Clipboard.setData(data); - // TODO: Internationalize this Alert.show( - widget.props.textCopiedMessage, + widget.props.translations.textCopiedText, context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, diff --git a/lib/widgets/header/header.dart b/lib/widgets/header/header.dart index 7f52d1d..1f6847e 100644 --- a/lib/widgets/header/header.dart +++ b/lib/widgets/header/header.dart @@ -38,7 +38,7 @@ class Header extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Flexible( - child: Text(props.title, + child: Text(props.translations.title, style: props.titleStyle, textAlign: props.titleAlign), ), if (closeAction != null) @@ -58,7 +58,7 @@ class Header extends StatelessWidget { ), Flexible( child: Text( - props.subtitle, + props.translations.subtitle, style: props.subtitleStyle ?? TextStyle( color: props.titleStyle.color?.withOpacity(0.8), diff --git a/lib/widgets/sendMessage/requireEmailUpfront.dart b/lib/widgets/sendMessage/requireEmailUpfront.dart index 6fc9040..8cd20a4 100644 --- a/lib/widgets/sendMessage/requireEmailUpfront.dart +++ b/lib/widgets/sendMessage/requireEmailUpfront.dart @@ -86,7 +86,8 @@ class _RequireEmailUpfrontState extends State { style: BorderStyle.solid, ), ), - hintText: widget.props.enterEmailPlaceholder, + hintText: + widget.props.translations.enterEmailPlaceholder, hintStyle: const TextStyle( fontSize: 14, ), @@ -133,7 +134,7 @@ class _RequireEmailUpfrontState extends State { decoration: InputDecoration( enabled: false, border: InputBorder.none, - hintText: widget.props.newMessagePlaceholder, + hintText: widget.props.translations.newMessagePlaceholder, hintStyle: const TextStyle( fontSize: 14, ), diff --git a/test/papercups_flutter_test.dart b/test/papercups_flutter_test.dart index 1d5ea4d..6915e07 100644 --- a/test/papercups_flutter_test.dart +++ b/test/papercups_flutter_test.dart @@ -46,46 +46,48 @@ void main() { //expect(props.agentAvailableText, null); expect(props.baseUrl, "app.papercups.io"); //expect(props.agentUnavailableText, null); - expect(props.companyName, "Bot"); - expect(props.newMessagePlaceholder, "Start typing..."); + expect(props.translations.companyName, "Bot"); + expect(props.translations.newMessagePlaceholder, "Start typing..."); expect(props.primaryColor, null); expect(props.requireEmailUpfront, false); expect(props.scrollEnabled, true); expect(props.customer, null); expect(props.primaryGradient, null); - expect(props.subtitle, "How can we help you?"); - expect(props.title, "Welcome!"); - expect(props.greeting, null); + expect(props.translations.subtitle, "How can we help you?"); + expect(props.translations.title, "Welcome!"); + expect(props.translations.greeting, null); }); test('are loaded correctly', () { props = Props( accountId: "this-is-an-account-id", - //agentAvailableText: "test", - //agentUnavailableText: "unavailable", + translations: PapercupsIntl( + companyName: "name", + greeting: "greeting", + //agentAvailableText: "test", + newMessagePlaceholder: "placeHolder", + //agentUnavailableText: "unavailable", + ), baseUrl: "app.papercups.io", - companyName: "name", - greeting: "greeting", - newMessagePlaceholder: "placeHolder", primaryColor: Color(0xffffff), requireEmailUpfront: true, scrollEnabled: true, customer: CustomerMetadata()); expect(props.accountId, "this-is-an-account-id"); - //expect(props.agentAvailableText, "test"); + //expect(props.translations.agentAvailableText, "test"); expect(props.baseUrl, "app.papercups.io"); - //expect(props.agentUnavailableText, "unavailable"); - expect(props.companyName, "name"); - expect(props.newMessagePlaceholder, "placeHolder"); + //expect(props.translations.agentUnavailableText, "unavailable"); + expect(props.translations.companyName, "name"); + expect(props.translations.newMessagePlaceholder, "placeHolder"); expect(props.primaryColor, Color(0xffffff)); expect(props.requireEmailUpfront, true); expect(props.scrollEnabled, true); expect(props.customer!.toJsonString(), '{"name":null,"email":null,"external_id":null}'); expect(props.primaryGradient, null); - expect(props.subtitle, "How can we help you?"); - expect(props.title, "Welcome!"); - expect(props.greeting, "greeting"); + expect(props.translations.subtitle, "How can we help you?"); + expect(props.translations.title, "Welcome!"); + expect(props.translations.greeting, "greeting"); }); }); From fe977f1485bc45fb79a873d1ec4787da29020d58 Mon Sep 17 00:00:00 2001 From: Charles Mangwa Date: Thu, 9 Dec 2021 12:05:18 +0100 Subject: [PATCH 15/17] Revert 2b82a74 as Flutter 2.8.0 is now in stable --- lib/widgets/chat/chat.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/widgets/chat/chat.dart b/lib/widgets/chat/chat.dart index 14366ee..ec7bdef 100644 --- a/lib/widgets/chat/chat.dart +++ b/lib/widgets/chat/chat.dart @@ -36,9 +36,7 @@ class ChatMessages extends StatelessWidget { alignment: Alignment.topCenter, child: NotificationListener( onNotification: (OverscrollIndicatorNotification overscroll) { - // TODO: Replace when Flutter 2.6.0 stable will be out. - // overscroll.disallowIndicator(); - overscroll.disallowGlow(); + overscroll.disallowIndicator(); return false; }, child: ListView.builder( From b407b84bb6a2f98d71d8a6e08efc669e6025a9f1 Mon Sep 17 00:00:00 2001 From: Charles Mangwa Date: Thu, 9 Dec 2021 12:57:13 +0100 Subject: [PATCH 16/17] Minor change --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2bc55ae..a23b530 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,8 @@ That should get you up and running in just a few seconds ⚡️. ### Available `PapercupsIntl` parameters | Parameters | Type | Value | Default | | :--- | :--- | :----- | :------ | -| **`agentAvailableText`** | `String` | This text will be shown if the showAgentAvailability is true and you are online | `"We're available."` | -| **`agentUnavailableText`** | `String` | This text will be shown if the showAgentAvailability is true and you are online | `"We're away at the moment."` | + | **`attachmentNamePlaceholder`** | `String` | Text displayed when an attachment doesn't have a file name | `"No Name"` | | **`attachmentUploadErrorText`** | `String` | Error message displayed when an attachment could not be uploaded | `"Failed to upload attachment"` | | **`attachmentUploadedText`** | `String` | Text displayed when an attachment has been uploaded | `"Attachment uploaded"` | From c5690b9a6b96897f19d3d3cad7eecfe463a93c2e Mon Sep 17 00:00:00 2001 From: Charles Mangwa Date: Thu, 9 Dec 2021 13:02:53 +0100 Subject: [PATCH 17/17] Minor fix --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index a23b530..5fe06ff 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,6 @@ That should get you up and running in just a few seconds ⚡️. ### Available `PapercupsIntl` parameters | Parameters | Type | Value | Default | | :--- | :--- | :----- | :------ | - | **`attachmentNamePlaceholder`** | `String` | Text displayed when an attachment doesn't have a file name | `"No Name"` | | **`attachmentUploadErrorText`** | `String` | Error message displayed when an attachment could not be uploaded | `"Failed to upload attachment"` | | **`attachmentUploadedText`** | `String` | Text displayed when an attachment has been uploaded | `"Attachment uploaded"` |