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/README.md b/README.md index 927efa1..5fe06ff 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,58 @@ 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 | +| :--- | :--- | :----- | :------ | +| **`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 485999e..d7f5ab1 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; @@ -40,22 +40,113 @@ class CustomerMetadata { } } -/// This contains all the possible configurations for the chat widget. -class Props { - //Decalration 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 a smaller piece of text under the title. + final String subtitle; + + /// This is the first message sent by you during the conversation. + final String? greeting; + + /// Text to show while message is sending. + /// + /// Default is `"Sending..."` + final String sendingText; + + /// Text to show when the message is sent + /// + /// Default is `"Sent"` (time will be added after). + final String sentText; + + /// This is the placeholder text in the input section. + final String newMessagePlaceholder; + + /// This is the placeholder text in the email input section. + final String enterEmailPlaceholder; + + /// Error message displayed when the customer history couldn't be fetched. + final String historyFetchErrorText; + + /// Error message displayed when an attachment could not be uploaded. + final String attachmentUploadErrorText; + + /// Text displayed when an attachment has been uploaded. + final String attachmentUploadedText; + + /// Text displayed when an attachment is been uploaded. + final String attachmentUploadingText; + + /// Text displayed after the percentage value of an attachment being uploaded. + final String uploadedText; + + /// Text displayed when a text has been copied after long press on a chat bubble. + final String textCopiedText; + + /// 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; + + /// This text will be shown if the showAgentAvailability is true and you are offline. + //String agentUnavailableText; + + 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, + }); +} +/// This contains all the possible configurations for the chat widget. +class Props { /// This is the top widget text style TextStyle titleStyle; /// This is the top widget title alignment TextAlign titleAlign; - /// This is a smaller piece of text under the title. - String subtitle; - /// This is the subtitle TextStyle TextStyle? subtitleStyle; @@ -65,70 +156,50 @@ 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. String baseUrl; - /// This is the first message sent by you during the conversation. - String? greeting; - /// This is the data that you will see on your dashboard such as the email or the name of the person. CustomerMetadata? customer; - /// This is the placeholder text in the input section. - String newMessagePlaceholder; - - /// This is the placeholder text in the email input section. - String enterEmailPlaceholer; - /// This is the close button in the header section. Widget closeIcon; - /// This text will be shown if the showAgentAvailability is true and you are online. - //String agentAvailableText; - - /// 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; - /// 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. bool scrollEnabled; - /// 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. + /// 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, - //this.agentUnavailableText, 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, @@ -143,9 +214,7 @@ class Props { left: 20, bottom: 12, ), - this.companyName = "Bot", this.primaryGradient, - this.enterEmailPlaceholer = "Enter your email", this.sendIcon, }); } 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/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 c4705c5..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( - fullName: widget.props.companyName, + displayName: widget.props.translations.companyName, ), userId: 0, id: "greeting", @@ -120,9 +112,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.translations.historyFetchErrorText, context, backgroundColor: Theme.of(context).bottomAppBarColor, textStyle: Theme.of(context).textTheme.bodyText2, @@ -243,7 +234,7 @@ class _PaperCupsWidgetState extends State { color: Colors.grey, ), Text( - "No Connection", + widget.props.translations.noConnectionText, style: Theme.of(context).textTheme.headline5!.copyWith( color: Colors.grey, ), @@ -282,7 +273,7 @@ class _PaperCupsWidgetState extends State { }); }, icon: Icon(Icons.refresh_rounded), - label: Text("Retry"), + label: Text(widget.props.translations.retryButtonLabel), ) ], ), @@ -305,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/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/fileInteraction/nativeFilePicker.dart b/lib/utils/fileInteraction/nativeFilePicker.dart index 52cf6f6..53ec39a 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,7 @@ void nativeFilePicker( ?.files; if (_paths != null && _paths.first.path != null) { Alert.show( - "Uploading...", + widget.props.translations.attachmentUploadingText, context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, @@ -29,7 +30,7 @@ void nativeFilePicker( filePath: _paths.first.path, onUploadProgress: (sentBytes, totalBytes) { Alert.show( - "${(sentBytes * 100 / totalBytes).toStringAsFixed(2)}% uploaded", + "${(sentBytes * 100 / totalBytes).toStringAsFixed(2)}% ${widget.props.translations.uploadedText}", context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, @@ -43,7 +44,7 @@ void nativeFilePicker( } } on PlatformException catch (_) { Alert.show( - "Failed to upload attachment", + widget.props.translations.attachmentUploadErrorText, context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, @@ -53,7 +54,7 @@ void nativeFilePicker( throw _; } catch (_) { Alert.show( - "Failed to upload attachment", + 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 4d4faa8..59c5dab 100644 --- a/lib/utils/fileInteraction/webFilePicker.dart +++ b/lib/utils/fileInteraction/webFilePicker.dart @@ -5,16 +5,17 @@ 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...", + widget.props.translations.attachmentUploadingText, context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, @@ -30,7 +31,7 @@ Future webFilePicker( } } on Exception catch (_) { Alert.show( - "Failed to upload attachment", + 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 6cac98a..f72305f 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) @@ -43,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"], @@ -54,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, @@ -64,13 +63,10 @@ PhoenixChannel? joinConversationAndListen({ email: event.payload!["user"]["email"], id: event.payload!["user"]["id"], role: event.payload!["user"]["role"], - fullName: - (event.payload!["user"]["full_name"] != null) - ? event.payload!["user"]["full_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, ) @@ -82,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 238c380..cd5a601 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) { @@ -72,7 +72,8 @@ 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 +81,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), @@ -122,12 +123,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", + fileName: e.fileName ?? + widget.props.translations.attachmentNamePlaceholder, textColor: widget.textColor, msgHasText: (msg.attachments!.length > 1 || msg.body != null), @@ -186,18 +188,26 @@ 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( - color: Theme.of(context).disabledColor.withOpacity(0.5), + color: Theme.of(context) + .textTheme + .bodyText1! + .color! + .withOpacity(0.5), fontSize: 14, ), ) : Text( - msg.user!.fullName!, + msg.user!.displayName!, 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..e24cf30 100644 --- a/lib/widgets/chat/chatMessage.dart +++ b/lib/widgets/chat/chatMessage.dart @@ -105,7 +105,7 @@ class _ChatMessageState extends State { longDay = DateFormat.yMMMMd(widget.locale).format(nextMsg.sentAt!); } catch (e) { print("ERROR: Error generating localized date!"); - longDay = "Loading..."; + longDay = widget.props.translations.loadingText; } } if (userSent && isLast && widget.timeagoLocale != null) { @@ -130,9 +130,8 @@ class _ChatMessageState extends State { HapticFeedback.vibrate(); final data = ClipboardData(text: text); Clipboard.setData(data); - // TODO: Internationalize this Alert.show( - "Text copied to clipboard", + widget.props.translations.textCopiedText, context, textStyle: Theme.of(context).textTheme.bodyText2, backgroundColor: Theme.of(context).bottomAppBarColor, @@ -166,7 +165,7 @@ class _ChatMessageState extends State { maxWidth: maxWidth, text: text, longDay: longDay, - conatinsAttachment: containsAttachment, + containsAttachment: containsAttachment, ), ), ); 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/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 db0e6b6..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.enterEmailPlaceholer, + 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/lib/widgets/sendMessage/sendMessage.dart b/lib/widgets/sendMessage/sendMessage.dart index 0329cf9..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( - "Attachment uploaded", + 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,7 +163,7 @@ class _SendMessageState extends State { foregroundColor: widget.textColor, child: Icon(Icons.insert_drive_file_outlined), ), - title: Text('File'), + title: Text(widget.props.translations.fileText), contentPadding: EdgeInsets.all(0), ), ), @@ -178,7 +175,7 @@ class _SendMessageState extends State { foregroundColor: widget.textColor, child: Icon(Icons.image_outlined), ), - title: Text('Image'), + title: Text(widget.props.translations.imageText), contentPadding: EdgeInsets.all(0), ), ), @@ -218,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'; 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"); }); });