diff --git a/android/app/build.gradle b/android/app/build.gradle index af82eacbc..9cd5c4c32 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -139,8 +139,8 @@ android { applicationId "com.chatwoot.app" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 5124 - versionName "1.8.7" + versionCode 5125 + versionName "1.8.8" buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() multiDexEnabled true diff --git a/ios/Chatwoot.xcodeproj/project.pbxproj b/ios/Chatwoot.xcodeproj/project.pbxproj index f1f09be2a..1348b61d1 100644 --- a/ios/Chatwoot.xcodeproj/project.pbxproj +++ b/ios/Chatwoot.xcodeproj/project.pbxproj @@ -559,7 +559,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Chatwoot/Chatwoot.entitlements; - CURRENT_PROJECT_VERSION = 254; + CURRENT_PROJECT_VERSION = 255; DEVELOPMENT_TEAM = L7YLMN4634; ENABLE_BITCODE = NO; INFOPLIST_FILE = Chatwoot/Info.plist; @@ -568,7 +568,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.8.7; + MARKETING_VERSION = 1.8.8; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -593,7 +593,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Chatwoot/Chatwoot.entitlements; - CURRENT_PROJECT_VERSION = 254; + CURRENT_PROJECT_VERSION = 255; DEVELOPMENT_TEAM = L7YLMN4634; INFOPLIST_FILE = Chatwoot/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Chatwoot; @@ -601,7 +601,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.8.7; + MARKETING_VERSION = 1.8.8; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", diff --git a/package.json b/package.json index 18b888cb8..04a5484b4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@chatwoot/mobile-app", - "version": "1.8.7", + "version": "1.8.8", "private": true, "scripts": { "android": "react-native run-android", diff --git a/src/components/ConversationDetailsItem.js b/src/components/ConversationDetailsItem.js index 01c520713..37537c831 100644 --- a/src/components/ConversationDetailsItem.js +++ b/src/components/ConversationDetailsItem.js @@ -1,56 +1,52 @@ -import React from 'react'; -import { View } from 'react-native'; +import React, { useMemo } from 'react'; +import { useTheme } from '@react-navigation/native'; +import { View, StyleSheet } from 'react-native'; import PropTypes from 'prop-types'; -import { withStyles } from '@ui-kitten/components'; import { openURL } from 'src/helpers/UrlHelper'; +import { Text } from 'components'; -import CustomText from './Text'; +const createStyles = theme => { + const { spacing } = theme; -const styles = theme => ({ - itemTitleView: { - flexDirection: 'row', - alignItems: 'center', - paddingBottom: 8, - paddingTop: 16, - }, - itemTitle: { - fontSize: theme['font-size-small'], - fontWeight: theme['font-medium'], - }, - itemValue: { - color: theme['text-light-color'], - fontSize: theme['font-size-small'], - fontWeight: theme['font-regular'], - }, -}); + return StyleSheet.create({ + itemTitleView: { + flexDirection: 'row', + alignItems: 'center', + paddingBottom: spacing.micro, + paddingTop: spacing.small, + }, + }); +}; const propTypes = { - eva: PropTypes.shape({ - style: PropTypes.object, - theme: PropTypes.object, - }).isRequired, title: PropTypes.string, value: PropTypes.string, type: PropTypes.string, }; -const ConversationDetailsItem = ({ value, title, type, eva: { style, theme } }) => { +const ConversationDetailsItem = ({ value, title, type }) => { + const theme = useTheme(); + const { colors } = theme; + const styles = useMemo(() => createStyles(theme), [theme]); + const link = type === 'referer'; return ( - - {title} + + + {title} + {link ? ( - openURL({ URL: value })}> + openURL({ URL: value })}> {value} - + ) : ( - {value} + + {value} + )} @@ -58,4 +54,4 @@ const ConversationDetailsItem = ({ value, title, type, eva: { style, theme } }) ); }; ConversationDetailsItem.propTypes = propTypes; -export default withStyles(ConversationDetailsItem, styles); +export default ConversationDetailsItem; diff --git a/src/components/NotificationItem.js b/src/components/NotificationItem.js index eba2d08b7..1e6f34ea6 100644 --- a/src/components/NotificationItem.js +++ b/src/components/NotificationItem.js @@ -38,12 +38,7 @@ const NotificationItemComponent = ({ eva, item, onSelectNotification }) => { push_message_title, read_at, created_at, - primary_actor: { - meta: { - sender: { name, thumbnail }, - }, - channel, - }, + primary_actor: { meta: { sender: { name = null, thumbnail = null } = {} } = {}, channel }, } = item; return ( { +const ConversationDetailsScreen = ({ navigation, route }) => { + const theme = useTheme(); + const { colors } = theme; + const styles = useMemo(() => createStyles(theme), [theme]); const dispatch = useDispatch(); const onBackPress = () => { @@ -39,6 +37,7 @@ const ConversationDetailsScreen = ({ eva: { style }, navigation, route }) => { name, thumbnail, email, + identifier, phone_number: phoneNumber, additional_attributes: senderAdditionalInfo = {}, }, @@ -60,22 +59,22 @@ const ConversationDetailsScreen = ({ eva: { style }, navigation, route }) => { { key: 'facebook', value: facebook, - iconName: 'facebook-outline', + iconName: 'brand-facebook', }, { key: 'twitter', value: twitter, - iconName: 'twitter-outline', + iconName: 'brand-twitter', }, { key: 'linkedin', value: linkedin, - iconName: 'linkedin-outline', + iconName: 'brand-linkedin', }, { key: 'github', value: github, - iconName: 'github-outline', + iconName: 'brand-github', }, ]; @@ -89,17 +88,22 @@ const ConversationDetailsScreen = ({ eva: { style }, navigation, route }) => { { key: 'email', value: email, - iconName: 'email-outline', + iconName: 'mail-outline', }, { key: 'phoneNumber', value: phoneNumber, - iconName: 'phone-call-outline', + iconName: 'call-outline', + }, + { + key: 'identifier', + value: identifier, + iconName: 'contact-identify-outline', }, { key: 'company', value: companyName, - iconName: 'home-outline', + iconName: 'building-bank-outline', }, { key: 'location', @@ -118,34 +122,43 @@ const ConversationDetailsScreen = ({ eva: { style }, navigation, route }) => { .filter(details => !!details); return ( - - - - - - - - {name} + +
+ + + + + + + + {name} + + + {senderAdditionalInfo.description ? ( + + + {senderAdditionalInfo.description} + + + ) : null} + {getSocialProfileValue} + {getContactDetails} - {senderAdditionalInfo.description ? ( - - {senderAdditionalInfo.description} + + + + {i18n.t('CONVERSATION_LABELS.TITLE')} + + + + - ) : null} - {getSocialProfileValue} - {getContactDetails} - - - - {i18n.t('CONVERSATION_LABELS.TITLE')} - - @@ -155,9 +168,6 @@ const ConversationDetailsScreen = ({ eva: { style }, navigation, route }) => { }; const propTypes = { - eva: PropTypes.shape({ - style: PropTypes.object, - }).isRequired, navigation: PropTypes.shape({ navigate: PropTypes.func.isRequired, goBack: PropTypes.func.isRequired, @@ -170,5 +180,4 @@ const propTypes = { theme: PropTypes.object, }; ConversationDetailsScreen.propTypes = propTypes; -const ConversationDetails = withStyles(ConversationDetailsScreen, styles); -export default ConversationDetails; +export default ConversationDetailsScreen; diff --git a/src/screens/ConversationDetails/ConversationDetailsScreen.style.js b/src/screens/ConversationDetails/ConversationDetailsScreen.style.js index 17174bfd2..a14ffbdd8 100644 --- a/src/screens/ConversationDetails/ConversationDetailsScreen.style.js +++ b/src/screens/ConversationDetails/ConversationDetailsScreen.style.js @@ -1,65 +1,49 @@ -const styles = theme => ({ - container: { - flex: 1, - backgroundColor: theme['background-basic-color-1'], - }, - - wrapper: { - paddingHorizontal: 20, - }, - - avatarContainer: { - flexDirection: 'row', - paddingVertical: 8, - justifyContent: 'flex-start', - alignItems: 'center', - }, - - userNameContainer: { - flexDirection: 'row', - justifyContent: 'flex-start', - alignItems: 'center', - }, - - descriptionContainer: { - flexDirection: 'row', - justifyContent: 'flex-start', - alignItems: 'center', - paddingVertical: 8, - }, - - description: { - fontSize: theme['font-size-small'], - color: theme['text-light-color'], - lineHeight: 20, - }, - - socialIconsContainer: { - flexDirection: 'row', - }, - - separationView: { - padding: 8, - borderBottomWidth: 1, - borderBottomColor: theme['color-border'], - }, - - separationViewLabels: { - padding: 6, - borderBottomWidth: 1, - borderBottomColor: theme['color-border'], - }, - - nameLabel: { - textTransform: 'capitalize', - fontWeight: theme['font-semi-bold'], - fontSize: theme['font-size-large'], - }, - - itemListViewTitle: { - paddingTop: 12, - fontWeight: theme['font-semi-bold'], - }, -}); - -export default styles; +import { StyleSheet } from 'react-native'; + +export default theme => { + const { colors, spacing } = theme; + + return StyleSheet.create({ + container: { + flex: 1, + backgroundColor: colors.background, + }, + avatarContainer: { + paddingTop: spacing.smaller, + paddingBottom: spacing.small, + }, + detailsWrap: { + paddingBottom: spacing.small, + paddingLeft: spacing.small, + paddingRight: spacing.small, + }, + descriptionContainer: { + paddingVertical: spacing.micro, + }, + description: { + lineHeight: 20, + }, + socialIconsContainer: { + flexDirection: 'row', + }, + separator: { + backgroundColor: colors.backgroundLight, + borderWidth: 0.4, + borderColor: colors.borderLight, + width: '100%', + paddingVertical: spacing.smaller, + paddingLeft: spacing.small, + }, + separatorView: { + width: '100%', + }, + accordionItemWrapper: { + flexDirection: 'column', + paddingTop: spacing.small, + paddingBottom: 10, + paddingLeft: spacing.small, + paddingRight: spacing.small, + width: '100%', + }, + }); +}; diff --git a/src/screens/ConversationDetails/components/ContactAttributes.js b/src/screens/ConversationDetails/components/ContactAttributes.js index f16f99d44..344f65d5d 100644 --- a/src/screens/ConversationDetails/components/ContactAttributes.js +++ b/src/screens/ConversationDetails/components/ContactAttributes.js @@ -1,32 +1,43 @@ -import React from 'react'; -import { withStyles } from '@ui-kitten/components'; +import React, { useMemo } from 'react'; +import { useTheme } from '@react-navigation/native'; import PropTypes from 'prop-types'; -import { View } from 'react-native'; -import ConversationDetailsItem from '../../../components/ConversationDetailsItem'; -import CustomText from 'components/Text'; +import { View, StyleSheet } from 'react-native'; +import ConversationDetailsItem from 'components/ConversationDetailsItem.js'; +import { Text } from 'components'; import i18n from 'i18n'; -const styles = theme => ({ - itemListViewTitle: { - paddingTop: 12, - fontWeight: theme['font-semi-bold'], - }, - separationView: { - padding: 8, - borderBottomWidth: 1, - borderBottomColor: theme['color-border'], - }, -}); +const createStyles = theme => { + const { colors, spacing } = theme; + + return StyleSheet.create({ + separator: { + backgroundColor: colors.backgroundLight, + width: '100%', + paddingVertical: spacing.smaller, + paddingLeft: spacing.small, + }, + separatorView: { + width: '100%', + }, + accordionItemWrapper: { + flexDirection: 'column', + paddingBottom: spacing.medium, + paddingLeft: spacing.small, + paddingRight: spacing.small, + width: '100%', + }, + }); +}; const propTypes = { - eva: PropTypes.shape({ - style: PropTypes.object, - theme: PropTypes.object, - }).isRequired, conversationDetails: PropTypes.object.isRequired, }; -const ContactAttributes = ({ conversationDetails, eva: { style, theme } }) => { +const ContactAttributes = ({ conversationDetails }) => { + const theme = useTheme(); + const { colors } = theme; + const styles = useMemo(() => createStyles(theme), [theme]); + const { meta } = conversationDetails; const { sender } = meta; const { custom_attributes: contactAttributes } = sender; @@ -40,21 +51,24 @@ const ContactAttributes = ({ conversationDetails, eva: { style, theme } }) => { {contactAttributesHasValue ? ( - - - {i18n.t('CONTACT_ATTRIBUTES.TITLE')} - - - {Object.keys(contactAttributes).map(key => { - return ( - - ); - })} + + + + {i18n.t('CONTACT_ATTRIBUTES.TITLE')} + + + + {Object.keys(contactAttributes).map(key => { + return ( + + ); + })} + ) : null} @@ -64,4 +78,4 @@ const ContactAttributes = ({ conversationDetails, eva: { style, theme } }) => { ContactAttributes.propTypes = propTypes; -export default withStyles(ContactAttributes, styles); +export default ContactAttributes; diff --git a/src/screens/ConversationDetails/components/ContactDetails.js b/src/screens/ConversationDetails/components/ContactDetails.js index 12072bd24..c1ccc7c16 100644 --- a/src/screens/ConversationDetails/components/ContactDetails.js +++ b/src/screens/ConversationDetails/components/ContactDetails.js @@ -1,38 +1,38 @@ -import React from 'react'; -import { TouchableOpacity } from 'react-native'; +import React, { useMemo } from 'react'; +import { StyleSheet } from 'react-native'; +import { useTheme } from '@react-navigation/native'; import PropTypes from 'prop-types'; -import { Icon, withStyles } from '@ui-kitten/components'; -import CustomText from 'src/components/Text'; +import { Icon, Text, Pressable } from 'components'; import { openNumber } from 'src/helpers/UrlHelper'; -import { View } from 'react-native-animatable'; -const styles = theme => ({ - detailsContainer: { - flexDirection: 'row', - justifyContent: 'flex-start', - alignItems: 'center', - paddingTop: 10, - }, - - label: { - paddingLeft: 8, - fontSize: theme['font-size-small'], - color: theme['text-light-color'], - }, -}); +const createStyles = theme => { + const { spacing } = theme; + return StyleSheet.create({ + detailsContainer: { + flexDirection: 'row', + justifyContent: 'flex-start', + alignItems: 'center', + marginTop: spacing.smaller, + marginBottom: spacing.micro, + }, + detailText: { + marginLeft: spacing.micro, + }, + }); +}; const propTypes = { - eva: PropTypes.shape({ - style: PropTypes.object, - theme: PropTypes.object, - }).isRequired, type: PropTypes.string, value: PropTypes.string, iconName: PropTypes.string, }; -const ContactDetails = ({ type, value, iconName, eva: { style, theme } }) => { +const ContactDetails = ({ type, value, iconName }) => { + const theme = useTheme(); + const { colors } = theme; + const styles = useMemo(() => createStyles(theme), [theme]); + const onClickOpen = () => { if (type === 'phoneNumber') { openNumber({ phoneNumber: value }); @@ -41,25 +41,14 @@ const ContactDetails = ({ type, value, iconName, eva: { style, theme } }) => { } }; return ( - - - - onClickOpen()} - /> - onClickOpen()}> - {value} - - - - + onClickOpen()}> + + + {value} + + ); }; ContactDetails.propTypes = propTypes; - -export default withStyles(ContactDetails, styles); +export default ContactDetails; diff --git a/src/screens/ConversationDetails/components/ConversationAttributes.js b/src/screens/ConversationDetails/components/ConversationAttributes.js index 49d0716b8..de3956829 100644 --- a/src/screens/ConversationDetails/components/ConversationAttributes.js +++ b/src/screens/ConversationDetails/components/ConversationAttributes.js @@ -1,85 +1,47 @@ -import React from 'react'; -import { withStyles } from '@ui-kitten/components'; +import React, { useMemo } from 'react'; +import { useTheme } from '@react-navigation/native'; import { useSelector } from 'react-redux'; import PropTypes from 'prop-types'; -import { View } from 'react-native'; +import { View, StyleSheet } from 'react-native'; +import { Text } from 'components'; import ConversationDetailsItem from '../../../components/ConversationDetailsItem'; -import CustomText from 'components/Text'; import i18n from 'i18n'; import { customAttributeSelector } from 'reducer/customAttributeSlice'; -const styles = theme => ({ - container: { - flex: 1, - backgroundColor: theme['background-basic-color-1'], - }, - wrapper: { - paddingHorizontal: 20, - }, +const createStyles = theme => { + const { colors, spacing } = theme; - avatarContainer: { - flexDirection: 'row', - paddingVertical: 8, - justifyContent: 'flex-start', - alignItems: 'center', - }, - - userNameContainer: { - flexDirection: 'row', - justifyContent: 'flex-start', - alignItems: 'center', - }, - - descriptionContainer: { - flexDirection: 'row', - justifyContent: 'flex-start', - alignItems: 'center', - paddingVertical: 8, - }, - - description: { - fontSize: theme['font-size-small'], - color: theme['text-light-color'], - lineHeight: 20, - }, - - socialIconsContainer: { - flexDirection: 'row', - }, - - separationView: { - padding: 8, - borderBottomWidth: 1, - borderBottomColor: theme['color-border'], - }, - - a: { - padding: 6, - borderBottomWidth: 1, - borderBottomColor: theme['color-border'], - }, + return StyleSheet.create({ + separator: { + backgroundColor: colors.backgroundLight, + width: '100%', + paddingVertical: spacing.smaller, + paddingLeft: spacing.small, + }, - nameLabel: { - textTransform: 'capitalize', - fontWeight: theme['font-semi-bold'], - fontSize: theme['font-size-large'], - }, + separatorView: { + width: '100%', + }, - itemListViewTitle: { - paddingTop: 12, - fontWeight: theme['font-semi-bold'], - }, -}); + accordionItemWrapper: { + flexDirection: 'column', + paddingBottom: spacing.small, + paddingLeft: spacing.small, + paddingRight: spacing.small, + width: '100%', + }, + }); +}; const propTypes = { - eva: PropTypes.shape({ - style: PropTypes.object, - theme: PropTypes.object, - }).isRequired, conversationDetails: PropTypes.object.isRequired, }; -const ConversationAttributes = ({ conversationDetails, eva: { style, theme } }) => { +const ConversationAttributes = ({ conversationDetails }) => { + const theme = useTheme(); + const { colors } = theme; + const styles = useMemo(() => createStyles(theme), [theme]); + const { additional_attributes: additionalAttributes } = conversationDetails; const attributes = useSelector(customAttributeSelector.selectAll); const { meta } = conversationDetails; @@ -161,13 +123,16 @@ const ConversationAttributes = ({ conversationDetails, eva: { style, theme } }) {displayItems.length > 0 || conversationAttributesHasValue ? ( - - - {i18n.t('CONVERSATION_DETAILS.TITLE')} - - - {displayItems} - {getConversationAttributes()} + + + + {i18n.t('CONVERSATION_DETAILS.TITLE')} + + + + {displayItems} + {getConversationAttributes()} + ) : null} @@ -176,5 +141,4 @@ const ConversationAttributes = ({ conversationDetails, eva: { style, theme } }) }; ConversationAttributes.propTypes = propTypes; - -export default withStyles(ConversationAttributes, styles); +export default ConversationAttributes; diff --git a/src/screens/ConversationDetails/components/LabelView.js b/src/screens/ConversationDetails/components/LabelView.js index 489fb7ffd..957307512 100644 --- a/src/screens/ConversationDetails/components/LabelView.js +++ b/src/screens/ConversationDetails/components/LabelView.js @@ -23,7 +23,6 @@ const styles = theme => ({ labelWrapper: { flexDirection: 'row', alignItems: 'center', - paddingTop: 10, }, labelViews: { flexDirection: 'row', diff --git a/src/screens/ConversationDetails/components/SocialProfileIcons.js b/src/screens/ConversationDetails/components/SocialProfileIcons.js index 36500509a..67df1592e 100644 --- a/src/screens/ConversationDetails/components/SocialProfileIcons.js +++ b/src/screens/ConversationDetails/components/SocialProfileIcons.js @@ -1,34 +1,34 @@ -import React from 'react'; -import { TouchableOpacity } from 'react-native'; +import React, { useMemo } from 'react'; +import { View, StyleSheet } from 'react-native'; +import { useTheme } from '@react-navigation/native'; import PropTypes from 'prop-types'; -import { Icon, withStyles } from '@ui-kitten/components'; +import { Icon, Pressable } from 'components'; import { openURL } from 'src/helpers/UrlHelper'; -import { View } from 'react-native-animatable'; - -const styles = theme => ({ - container: { - paddingVertical: 8, - }, - socialIconWrap: { - marginRight: 10, - backgroundColor: theme['color-secondary-100'], - padding: 4, - borderRadius: 20, - }, -}); +const createStyles = theme => { + const { spacing, borderRadius } = theme; + return StyleSheet.create({ + container: { + paddingVertical: spacing.smaller, + }, + socialIconWrap: { + marginRight: spacing.small, + borderRadius: borderRadius.larger, + }, + }); +}; const propTypes = { - eva: PropTypes.shape({ - style: PropTypes.object, - theme: PropTypes.object, - }).isRequired, type: PropTypes.string, value: PropTypes.string, iconName: PropTypes.string, }; -const SocialProfileIcons = ({ type, value, iconName, eva: { style, theme } }) => { +const SocialProfileIcons = ({ type, value, iconName }) => { + const theme = useTheme(); + const { colors } = theme; + const styles = useMemo(() => createStyles(theme), [theme]); + const generateSocialProfileLink = () => { switch (type) { case 'facebook': @@ -48,23 +48,14 @@ const SocialProfileIcons = ({ type, value, iconName, eva: { style, theme } }) => return ( - - - - openURL({ URL: url })} - /> - - - + + openURL({ URL: url })}> + + + ); }; SocialProfileIcons.propTypes = propTypes; - -export default withStyles(SocialProfileIcons, styles); +export default SocialProfileIcons;