[IMPROVE] Visibility of the “incoming chats” in queue (#4039)

* [IMPROVE] Visibility of the “incoming chats” in queue

* fix the custom icon for rtl

* fix thumb colors

* clean queue empty

* added alert to confirm enable omnichannel

* switch to normal

* fix storyshot because was added a new props to list item

* fix height container

* minor tweak

* minor tweak

* fix title
This commit is contained in:
Reinaldo Neto 2022-04-15 00:11:36 -03:00 committed by GitHub
parent 064920e61b
commit 0a67cb8096
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 178 additions and 128 deletions

View File

@ -11,6 +11,7 @@ interface IListIcon {
color?: string | null;
style?: StyleProp<ViewStyle>;
testID?: string;
size?: number;
}
const styles = StyleSheet.create({
@ -20,12 +21,12 @@ const styles = StyleSheet.create({
}
});
const ListIcon = React.memo(({ name, color, style, testID }: IListIcon) => {
const ListIcon = React.memo(({ name, color, style, testID, size }: IListIcon) => {
const { theme } = useTheme();
return (
<View style={[styles.icon, style]}>
<CustomIcon name={name} color={color ?? themes[theme].auxiliaryText} size={ICON_SIZE} testID={testID} />
<CustomIcon name={name} color={color ?? themes[theme].auxiliaryText} size={size ?? ICON_SIZE} testID={testID} />
</View>
);
});

View File

@ -1,5 +1,5 @@
import React from 'react';
import { I18nManager, StyleSheet, Text, View } from 'react-native';
import { I18nManager, StyleProp, StyleSheet, Text, TextStyle, View } from 'react-native';
import Touch from '../../utils/touch';
import { themes } from '../../lib/constants';
@ -66,6 +66,8 @@ interface IListItemContent {
translateSubtitle?: boolean;
showActionIndicator?: boolean;
alert?: boolean;
heightContainer?: number;
styleTitle?: StyleProp<TextStyle>;
}
const Content = React.memo(
@ -81,16 +83,20 @@ const Content = React.memo(
translateTitle = true,
translateSubtitle = true,
showActionIndicator = false,
theme
theme,
heightContainer,
styleTitle
}: IListItemContent) => {
const { fontScale } = useDimensions();
return (
<View style={[styles.container, disabled && styles.disabled, { height: BASE_HEIGHT * fontScale }]} testID={testID}>
<View
style={[styles.container, disabled && styles.disabled, { height: (heightContainer || BASE_HEIGHT) * fontScale }]}
testID={testID}>
{left ? <View style={styles.leftContainer}>{left()}</View> : null}
<View style={styles.textContainer}>
<View style={styles.textAlertContainer}>
<Text style={[styles.title, { color: color || themes[theme].titleText }]} numberOfLines={1}>
<Text style={[styles.title, styleTitle, { color: color || themes[theme].titleText }]} numberOfLines={1}>
{translateTitle && title ? I18n.t(title) : title}
</Text>
{alert ? (

View File

@ -1,5 +1,5 @@
import React from 'react';
import { StyleSheet, Text, View, ViewStyle } from 'react-native';
import { StyleProp, StyleSheet, Text, View, ViewStyle } from 'react-native';
import sharedStyles from '../../views/Styles';
import { getUnreadStyle } from './getUnreadStyle';
@ -33,7 +33,7 @@ export interface IUnreadBadge {
unread?: number;
userMentions?: number;
groupMentions?: number;
style?: ViewStyle;
style?: StyleProp<ViewStyle>;
tunread?: [];
tunreadUser?: [];
tunreadGroup?: [];

View File

@ -0,0 +1,46 @@
import React from 'react';
import { View, Text } from 'react-native';
import { useTheme } from '../../../../theme';
import { themes } from '../../../../lib/constants';
import { CustomIcon } from '../../../../lib/Icons';
import * as List from '../../../../containers/List';
import styles from './styles';
import UnreadBadge from '../../../../containers/UnreadBadge';
import i18n from '../../../../i18n';
interface IOmnichannelQueue {
queueSize?: number;
onPress(): void;
}
const OmnichannelQueue = ({ queueSize, onPress }: IOmnichannelQueue) => {
const { theme } = useTheme();
return (
<>
<List.Item
title='Omnichannel_queue'
heightContainer={50}
left={() => <List.Icon name='queue' size={24} color={themes[theme].auxiliaryTintColor} />}
color={themes[theme].bodyText}
onPress={queueSize ? onPress : undefined}
styleTitle={styles.titleOmnichannelQueue}
right={() => (
<View style={styles.omnichannelRightContainer}>
{queueSize ? (
<>
<UnreadBadge style={[styles.queueIcon, { backgroundColor: themes[theme].tintColor }]} unread={queueSize} />
<CustomIcon name='chevron-right' style={styles.actionIndicator} color={themes[theme].bodyText} size={24} />
</>
) : (
<Text style={[styles.emptyText, { color: themes[theme].auxiliaryTintColor }]}>{i18n.t('Empty')}</Text>
)}
</View>
)}
/>
<List.Separator />
</>
);
};
export default OmnichannelQueue;

View File

@ -0,0 +1,77 @@
import React, { memo, useEffect, useState } from 'react';
import { Switch, View } from 'react-native';
import * as List from '../../../../containers/List';
import styles from './styles';
import { SWITCH_TRACK_COLOR, themes } from '../../../../lib/constants';
import { useTheme } from '../../../../theme';
import RocketChat from '../../../../lib/rocketchat';
import { IUser } from '../../../../definitions/IUser';
import { showConfirmationAlert } from '../../../../utils/info';
import I18n from '../../../../i18n';
import { changeLivechatStatus, isOmnichannelStatusAvailable } from '../../lib';
import OmnichannelQueue from './OmnichannelQueue';
interface IOmnichannelStatus {
searching: boolean;
goQueue: () => void;
queueSize: number;
inquiryEnabled: boolean;
user: IUser;
}
const OmnichannelStatus = memo(({ searching, goQueue, queueSize, user }: IOmnichannelStatus) => {
const { theme } = useTheme();
const [status, setStatus] = useState(isOmnichannelStatusAvailable(user));
useEffect(() => {
setStatus(isOmnichannelStatusAvailable(user));
}, [user.statusLivechat]);
if (searching || !(RocketChat.isOmnichannelModuleAvailable() && user?.roles?.includes('livechat-agent'))) {
return null;
}
const toggleLivechat = async () => {
// if not-available, prompt to change to available
if (!isOmnichannelStatusAvailable(user)) {
showConfirmationAlert({
message: I18n.t('Omnichannel_enable_alert'),
confirmationText: I18n.t('Yes'),
onPress: async () => {
try {
await changeLivechatStatus();
} catch {
// Do nothing
}
}
});
} else {
try {
setStatus(v => !v);
await changeLivechatStatus();
} catch {
setStatus(v => !v);
}
}
};
return (
<>
<List.Item
title='Omnichannel'
color={themes[theme].bodyText}
onPress={toggleLivechat}
right={() => (
<View style={styles.omnichannelRightContainer}>
<Switch value={status} trackColor={SWITCH_TRACK_COLOR} onValueChange={toggleLivechat} />
</View>
)}
/>
<List.Separator />
{status ? <OmnichannelQueue queueSize={queueSize} onPress={goQueue} /> : null}
</>
);
});
export default OmnichannelStatus;

View File

@ -0,0 +1,23 @@
import { I18nManager, StyleSheet } from 'react-native';
import sharedStyles from '../../../../views/Styles';
export default StyleSheet.create({
queueIcon: {
marginHorizontal: 10
},
omnichannelRightContainer: {
flexDirection: 'row',
alignItems: 'center'
},
titleOmnichannelQueue: {
...sharedStyles.textMedium
},
emptyText: {
...sharedStyles.textRegular,
fontSize: 12
},
actionIndicator: {
...(I18nManager.isRTL ? { transform: [{ rotate: '180deg' }] } : {})
}
});

View File

@ -1,67 +0,0 @@
import React, { memo, useEffect, useState } from 'react';
import { Switch, View } from 'react-native';
import * as List from '../../../containers/List';
import styles from '../../../views/RoomsListView/styles';
import { SWITCH_TRACK_COLOR, themes } from '../../../lib/constants';
import { useTheme } from '../../../theme';
import UnreadBadge from '../../../containers/UnreadBadge';
import RocketChat from '../../../lib/rocketchat';
import { changeLivechatStatus, isOmnichannelStatusAvailable } from '../lib';
import { IUser } from '../../../definitions/IUser';
import Touch from '../../../utils/touch';
interface IOmnichannelStatus {
searching: boolean;
goQueue: () => void;
queueSize: number;
inquiryEnabled: boolean;
user: IUser;
}
const OmnichannelStatus = memo(({ searching, goQueue, queueSize, inquiryEnabled, user }: IOmnichannelStatus) => {
const { theme } = useTheme();
const [status, setStatus] = useState<boolean>(false);
const canUseOmnichannel = RocketChat.isOmnichannelModuleAvailable() && user?.roles?.includes('livechat-agent');
useEffect(() => {
if (canUseOmnichannel) {
setStatus(isOmnichannelStatusAvailable(user));
}
}, [user.statusLivechat]);
if (searching || !canUseOmnichannel) {
return null;
}
const toggleLivechat = async () => {
try {
setStatus(v => !v);
await changeLivechatStatus();
} catch {
setStatus(v => !v);
}
};
return (
<>
<List.Item
title='Omnichannel'
left={() => <List.Icon name='omnichannel' />}
color={themes[theme].auxiliaryText}
onPress={goQueue}
right={() => (
<View style={styles.omnichannelRightContainer}>
{inquiryEnabled ? <UnreadBadge style={styles.queueIcon} unread={queueSize} /> : null}
<Touch theme={theme} onPress={toggleLivechat}>
<Switch value={status} trackColor={SWITCH_TRACK_COLOR} onValueChange={toggleLivechat} />
</Touch>
</View>
)}
/>
<List.Separator />
</>
);
});
export default OmnichannelStatus;

View File

@ -650,7 +650,6 @@
"After_seconds_set_by_admin": "بعد {{seconds}} ثوان (حددها المدير)",
"Dont_activate": "لا تقم بالتفعيل الآن",
"Queued_chats": "محادثات في قائمى الانتظار",
"Queue_is_empty": "قائمة الانتظار فارغة",
"Logout_from_other_logged_in_locations": "تسجيل الخروج من الأماكن الأخرى",
"You_will_be_logged_out_from_other_locations": "سيتم تسجيل خروج من الأماكن الأخرى",
"Logged_out_of_other_clients_successfully": "تم تسجيل الخروج من الأماكن الأخرى بنجاح",

View File

@ -656,7 +656,6 @@
"After_seconds_set_by_admin": "Nach {{seconds}} Sekunden (durch den Admin gesetzt)",
"Dont_activate": "Jetzt nicht aktivieren",
"Queued_chats": "Chats in der Warteschlange",
"Queue_is_empty": "Warteschlange leer",
"Logout_from_other_logged_in_locations": "Auf anderen angemeldeten Geräte abmelden",
"You_will_be_logged_out_from_other_locations": "Sie werden auf anderen Geräten abgemeldet.",
"Logged_out_of_other_clients_successfully": "Erfolgreich von anderen Geräten abgemeldet.",

View File

@ -657,7 +657,6 @@
"After_seconds_set_by_admin": "After {{seconds}} seconds (set by admin)",
"Dont_activate": "Don't activate now",
"Queued_chats": "Queued chats",
"Queue_is_empty": "Queue is empty",
"Logout_from_other_logged_in_locations": "Logout from other logged in locations",
"You_will_be_logged_out_from_other_locations": "You'll be logged out from other locations.",
"Logged_out_of_other_clients_successfully": "Logged out of other clients successfully",
@ -808,5 +807,7 @@
"Removed__roomName__from_this_team": "removed #{{roomName}} from this Team",
"Removed__username__from_team": "removed @{{user_removed}} from this Team",
"User_joined_team": "joined this Team",
"User_left_team": "left this Team"
"User_left_team": "left this Team",
"Omnichannel_queue": "Omnichannel queue",
"Empty": "Empty"
}

View File

@ -656,7 +656,6 @@
"After_seconds_set_by_admin": "Après {{seconds}} secondes (défini par l'administrateur)",
"Dont_activate": "Ne pas activer maintenant",
"Queued_chats": "Discussions en file d'attente",
"Queue_is_empty": "La file d'attente est vide",
"Logout_from_other_logged_in_locations": "Déconnexion des autres emplacements connectés",
"You_will_be_logged_out_from_other_locations": "Vous serez déconnecté des autres emplacements.",
"Logged_out_of_other_clients_successfully": "Déconnexion réussie des autres clients",

View File

@ -644,7 +644,6 @@
"After_seconds_set_by_admin": "Dopo {{seconds}} secondi (impostati dall'admin)",
"Dont_activate": "Non attivare ora",
"Queued_chats": "Chat in coda",
"Queue_is_empty": "La coda è vuota",
"Logout_from_other_logged_in_locations": "Disconnetti da altre postazioni",
"You_will_be_logged_out_from_other_locations": "Verrai disconnesso dalle altre postazioni.",
"Logged_out_of_other_clients_successfully": "Disconnesso dalle altre postazioni con successo",

View File

@ -656,7 +656,6 @@
"After_seconds_set_by_admin": "Na {{seconds}} seconden (ingesteld door beheerder)",
"Dont_activate": "Nu niet activeren",
"Queued_chats": "Chats in de wachtrij",
"Queue_is_empty": "Wachtrij is leeg",
"Logout_from_other_logged_in_locations": "Afmelden bij andere ingelogde locaties",
"You_will_be_logged_out_from_other_locations": "Je wordt uitgelogd van andere locaties.",
"Logged_out_of_other_clients_successfully": "Succesvol uitgelogd bij andere klanten",

View File

@ -615,7 +615,6 @@
"After_seconds_set_by_admin": "Após {{seconds}} segundos (Configurado pelo adm)",
"Dont_activate": "Não ativar agora",
"Queued_chats": "Bate-papos na fila",
"Queue_is_empty": "A fila está vazia",
"Logout_from_other_logged_in_locations": "Sair de outros locais logados",
"You_will_be_logged_out_from_other_locations": "Você perderá a sessão de outros clientes",
"Logged_out_of_other_clients_successfully": "Desconectado de outros clientes com sucesso",

View File

@ -656,7 +656,6 @@
"After_seconds_set_by_admin": "Через {{seconds}} секунд (установлено администратором сервера)",
"Dont_activate": "Не активировать сейчас",
"Queued_chats": "Чаты в очереди",
"Queue_is_empty": "Очередь пуста",
"Logout_from_other_logged_in_locations": "Выйти из всех других подключенных расположений",
"You_will_be_logged_out_from_other_locations": "Будет произведен ваш выход из всех других подключенных расположений.",
"Logged_out_of_other_clients_successfully": "Выход из других клиентских подключений выполнен успешно",

View File

@ -644,7 +644,6 @@
"After_seconds_set_by_admin": "{{seconds}} saniye sonra (yönetici tarafından belirlenir)",
"Dont_activate": "Şimdi etkinleştirme",
"Queued_chats": "Sıralı sohbetler",
"Queue_is_empty": "Sıra boş",
"Logout_from_other_logged_in_locations": "Giriş yapılan diğer konumlardan çıkış yap",
"You_will_be_logged_out_from_other_locations": "Diğer konumlardan çıkış yapacaksınız.",
"Logged_out_of_other_clients_successfully": "Diğer istemcilerden başarıyla çıkış yapıldı",

View File

@ -642,7 +642,6 @@
"After_seconds_set_by_admin": "{{seconds}} 秒 (管理员设定)",
"Dont_activate": "現在不要激活",
"Queued_chats": "聊天队列",
"Queue_is_empty": "队列是空的",
"Logout_from_other_logged_in_locations": "注销其他已登陆的设备",
"You_will_be_logged_out_from_other_locations": "您将于其他设备上注销",
"Logged_out_of_other_clients_successfully": "成功登出其他用户端",

View File

@ -644,7 +644,6 @@
"After_seconds_set_by_admin": "{{seconds}} 秒 (管理員設定)",
"Dont_activate": "現在不要啟用",
"Queued_chats": "聊天佇列",
"Queue_is_empty": "佇列是空的",
"Logout_from_other_logged_in_locations": "登出其他已登入的設備",
"You_will_be_logged_out_from_other_locations": "您將於其他設備上登出",
"Logged_out_of_other_clients_successfully": "成功登出其他用戶端",

View File

@ -2,7 +2,7 @@ import React from 'react';
import { useTheme } from '../../../theme';
import * as List from '../../../containers/List';
import OmnichannelStatus from '../../../ee/omnichannel/containers/OmnichannelStatus';
import OmnichannelStatus from '../../../ee/omnichannel/containers/OmnichannelHeader';
import { IUser } from '../../../definitions';
import { E2E_BANNER_TYPE, themes } from '../../../lib/constants';

View File

@ -40,9 +40,7 @@ import { goRoom } from '../../utils/goRoom';
import SafeAreaView from '../../containers/SafeAreaView';
import Header, { getHeaderTitlePosition } from '../../containers/Header';
import { withDimensions } from '../../dimensions';
import { showConfirmationAlert, showErrorAlert } from '../../utils/info';
import { getInquiryQueueSelector } from '../../ee/omnichannel/selectors/inquiry';
import { changeLivechatStatus, isOmnichannelStatusAvailable } from '../../ee/omnichannel/lib';
import { IApplicationState, IBaseScreen, ISubscription, IUser, RootEnum, TSubscriptionModel } from '../../definitions';
import styles from './styles';
import ServerDropdown from './ServerDropdown';
@ -745,30 +743,12 @@ class RoomsListView extends React.Component<IRoomsListViewProps, IRoomsListViewS
goQueue = () => {
logEvent(events.RL_GO_QUEUE);
const { navigation, isMasterDetail, queueSize, inquiryEnabled, user } = this.props;
// if not-available, prompt to change to available
if (!isOmnichannelStatusAvailable(user)) {
showConfirmationAlert({
message: I18n.t('Omnichannel_enable_alert'),
confirmationText: I18n.t('Yes'),
onPress: async () => {
try {
await changeLivechatStatus();
} catch {
// Do nothing
}
}
});
}
const { navigation, isMasterDetail, inquiryEnabled } = this.props;
if (!inquiryEnabled) {
return;
}
// prevent navigation to empty list
if (!queueSize) {
return showErrorAlert(I18n.t('Queue_is_empty'), I18n.t('Oops'));
}
if (isMasterDetail) {
navigation.navigate('ModalStackNavigator', { screen: 'QueueListView' });
} else {

View File

@ -24,13 +24,6 @@ export default StyleSheet.create({
backdrop: {
...StyleSheet.absoluteFillObject
},
queueIcon: {
marginHorizontal: 12
},
omnichannelRightContainer: {
flexDirection: 'row',
alignItems: 'center'
},
groupTitleContainer: {
paddingHorizontal: 12,
paddingTop: 17,

File diff suppressed because one or more lines are too long