Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c4cf843
Update index.js
ggazzo Jan 8, 2021
97b69f7
Fix Livechat.registerGuest method.
renatobecker Jan 8, 2021
a0f2498
Merge branch 'develop' into omnichannel/fix-register-guest-method
renatobecker Jan 8, 2021
90def2b
Merge branch 'develop' into omnichannel/fix-register-guest-method
renatobecker Jan 8, 2021
efad87f
pass only fields that need to be inserted
rafaelblink Jan 8, 2021
b411319
Merge remote-tracking branch 'origin/omnichannel/fix-register-guest-m…
rafaelblink Jan 8, 2021
d53a299
undo changes on Livechat, registerGuest method.
rafaelblink Jan 11, 2021
b4c18ea
refactor contacts using your own lib file.
rafaelblink Jan 11, 2021
3cd9e9d
remove unnecessary http header method
rafaelblink Jan 11, 2021
8741c80
refactor null value on server side.
rafaelblink Jan 11, 2021
5317135
check if customFields are valid and adding only valid fields.
rafaelblink Jan 11, 2021
7544559
check if customField is visitor and it has some value.
rafaelblink Jan 11, 2021
278930d
Improve code style.
renatobecker Jan 11, 2021
d68d5dc
Merge branch 'develop' into omnichannel/fix-register-guest-method
renatobecker Jan 11, 2021
af2354e
Implements search for fields check if either email or phone already e…
rafaelblink Jan 11, 2021
d2b55a5
validate if both email and phone already exist
rafaelblink Jan 12, 2021
b5a88ef
Merge branch 'develop' into omnichannel/fix-register-guest-method
renatobecker Jan 12, 2021
4d33bc3
code enhancements on api and field validations.
rafaelblink Jan 12, 2021
fbf27e8
Merge remote-tracking branch 'origin/omnichannel/fix-register-guest-m…
rafaelblink Jan 12, 2021
d972a01
undo unecessary field clean validation.
rafaelblink Jan 12, 2021
934dee4
remove email field icon to avoid weird error validation and clear err…
rafaelblink Jan 12, 2021
aa29aa1
Merge branch 'develop' into omnichannel/fix-register-guest-method
renatobecker Jan 12, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 33 additions & 7 deletions app/livechat/server/api/v1/contact.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { Match, check } from 'meteor/check';
import { Meteor } from 'meteor/meteor';

import { API } from '../../../../api/server';
import { Livechat } from '../../lib/Livechat';
import { Contacts } from '../../lib/Contacts';
import {
LivechatVisitors,
} from '../../../../models';


API.v1.addRoute('omnichannel/contact', { authRequired: true }, {
post() {
try {
Expand All @@ -15,15 +17,11 @@ API.v1.addRoute('omnichannel/contact', { authRequired: true }, {
name: String,
email: Match.Maybe(String),
phone: Match.Maybe(String),
livechatData: Match.Maybe(Object),
customFields: Match.Maybe(Object),
contactManager: Match.Maybe(Object),
});

const contactParams = this.bodyParams;
if (this.bodyParams.phone) {
contactParams.phone = { number: this.bodyParams.phone };
}
const contact = Livechat.registerGuest(contactParams);
const contact = Contacts.registerContact(this.bodyParams);

return API.v1.success({ contact });
} catch (e) {
Expand All @@ -40,3 +38,31 @@ API.v1.addRoute('omnichannel/contact', { authRequired: true }, {
return API.v1.success({ contact });
},
});


API.v1.addRoute('omnichannel/contact.search', { authRequired: true }, {
get() {
try {
check(this.queryParams, {
email: Match.Maybe(String),
phone: Match.Maybe(String),
});

const { email, phone } = this.queryParams;

if (!email && !phone) {
throw new Meteor.Error('error-invalid-params');
}

const query = Object.assign({}, {
...email && { visitorEmails: { address: email } },
...phone && { phone: { phoneNumber: phone } },
});

const contact = Promise.await(LivechatVisitors.findOne(query));
return API.v1.success({ contact });
} catch (e) {
return API.v1.failure(e);
}
},
});
65 changes: 65 additions & 0 deletions app/livechat/server/lib/Contacts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { check } from 'meteor/check';
import s from 'underscore.string';

import {
LivechatVisitors,
LivechatCustomField,
} from '../../../models';


export const Contacts = {

registerContact({ token, name, email, phone, username, customFields = {}, contactManager = {} } = {}) {
check(token, String);

let contactId;
const updateUser = {
$set: {
token,
},
};

const user = LivechatVisitors.getVisitorByToken(token, { fields: { _id: 1 } });

if (user) {
contactId = user._id;
} else {
if (!username) {
username = LivechatVisitors.getNextVisitorUsername();
}

let existingUser = null;

if (s.trim(email) !== '' && (existingUser = LivechatVisitors.findOneGuestByEmailAddress(email))) {
contactId = existingUser._id;
} else {
const userData = {
username,
ts: new Date(),
};

contactId = LivechatVisitors.insert(userData);
}
}

updateUser.$set.name = name;
updateUser.$set.phone = (phone && [{ phoneNumber: phone }]) || null;
updateUser.$set.visitorEmails = (email && [{ address: email }]) || null;

const allowedCF = LivechatCustomField.find({ scope: 'visitor' }).map(({ _id }) => _id);

const livechatData = Object.keys(customFields)
.filter((key) => allowedCF.includes(key) && customFields[key] !== '' && customFields[key] !== undefined)
.reduce((obj, key) => {
obj[key] = customFields[key];
return obj;
}, {});

updateUser.$set.livechatData = livechatData;
updateUser.$set.contactManager = (contactManager?.username && { username: contactManager.username }) || null;

LivechatVisitors.updateById(contactId, updateUser);

return contactId;
},
};
27 changes: 5 additions & 22 deletions app/livechat/server/lib/Livechat.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,15 +192,14 @@ export const Livechat = {
return true;
},

registerGuest({ token, name, email, department, phone, username, livechatData, contactManager, connectionData } = {}) {
registerGuest({ token, name, email, department, phone, username, connectionData } = {}) {
check(token, String);

let userId;
const updateUser = {
$set: {
token,
},
$unset: { },
};

const user = LivechatVisitors.getVisitorByToken(token, { fields: { _id: 1 } });
Expand Down Expand Up @@ -235,45 +234,29 @@ export const Livechat = {
}
}

if (name) {
updateUser.$set.name = name;
}

if (phone) {
updateUser.$set.phone = [
{ phoneNumber: phone.number },
];
} else {
updateUser.$unset.phone = 1;
}

if (email && email.trim() !== '') {
updateUser.$set.visitorEmails = [
{ address: email },
];
} else {
updateUser.$unset.visitorEmails = 1;
}

if (livechatData) {
updateUser.$set.livechatData = livechatData;
} else {
updateUser.$unset.livechatData = 1;
}

if (contactManager) {
updateUser.$set.contactManager = contactManager;
} else {
updateUser.$unset.contactManager = 1;
if (name) {
updateUser.$set.name = name;
}

if (!department) {
updateUser.$unset.department = 1;
Object.assign(updateUser, { $unset: { department: 1 } });
} else {
const dep = LivechatDepartment.findOneByIdOrName(department);
updateUser.$set.department = dep && dep._id;
}
if (_.isEmpty(updateUser.$unset)) { delete updateUser.$unset; }

LivechatVisitors.updateById(userId, updateUser);

return userId;
Expand Down
44 changes: 34 additions & 10 deletions client/omnichannel/directory/ContactForm.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState, useMemo } from 'react';
import { Field, TextInput, Icon, ButtonGroup, Button, Box } from '@rocket.chat/fuselage';
import { Field, TextInput, ButtonGroup, Button, Box } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { useSubscription } from 'use-subscription';

Expand Down Expand Up @@ -95,7 +95,7 @@ export function ContactNewEdit({ id, data, reload, close }) {

const [nameError, setNameError] = useState();
const [emailError, setEmailError] = useState();
const [phoneError] = useState();
const [phoneError, setPhoneError] = useState();

const { value: allCustomFields, phase: state } = useEndpointData('livechat/custom-fields');

Expand All @@ -118,15 +118,39 @@ export function ContactNewEdit({ id, data, reload, close }) {
? jsonConverterToValidFormat(allCustomFields.customFields) : {}), [allCustomFields]);

const saveContact = useEndpointAction('POST', 'omnichannel/contact');
const emailAlreadyExistsAction = useEndpointAction('GET', `omnichannel/contact.search?email=${ email }`);
const phoneAlreadyExistsAction = useEndpointAction('GET', `omnichannel/contact.search?phone=${ phone }`);

const checkEmailExists = useMutableCallback(async () => {
if (!isEmail(email)) { return; }
const { contact } = await emailAlreadyExistsAction();
if (!contact || (id && contact._id === id)) {
return setEmailError(null);
}
setEmailError(t('Email_already_exists'));
});

const checkPhoneExists = useMutableCallback(async () => {
if (!phone) { return; }
const { contact } = await phoneAlreadyExistsAction();
if (!contact || (id && contact._id === id)) {
return setPhoneError(null);
}
setPhoneError(t('Phone_already_exists'));
});


const dispatchToastMessage = useToastMessageDispatch();

useComponentDidUpdate(() => {
setNameError(!name ? t('The_field_is_required', t('Name')) : '');
}, [t, name]);
useComponentDidUpdate(() => {
setEmailError(email && !isEmail(email) ? t('Validate_email_address') : undefined);
setEmailError(email && !isEmail(email) ? t('Validate_email_address') : null);
}, [t, email]);
useComponentDidUpdate(() => {
!phone && setPhoneError(null);
}, [phone]);

const handleSave = useMutableCallback(async (e) => {
e.preventDefault();
Expand All @@ -146,18 +170,18 @@ export function ContactNewEdit({ id, data, reload, close }) {

const payload = {
name,
email,
phone,
};
payload.phone = phone;
payload.email = email;
payload.customFields = livechatData || {};
payload.contactManager = username ? { username } : {};

if (id) {
payload._id = id;
payload.token = token;
} else {
payload.token = createToken();
}
if (livechatData) { payload.livechatData = livechatData; }
if (username) { payload.contactManager = { username }; }

try {
await saveContact(payload);
Expand All @@ -169,7 +193,7 @@ export function ContactNewEdit({ id, data, reload, close }) {
}
});

const formIsValid = name && !emailError;
const formIsValid = name && !emailError && !phoneError;


if ([state].includes(AsyncStatePhase.LOADING)) {
Expand All @@ -190,7 +214,7 @@ export function ContactNewEdit({ id, data, reload, close }) {
<Field>
<Field.Label>{t('Email')}</Field.Label>
<Field.Row>
<TextInput error={emailError} flexGrow={1} value={email} onChange={handleEmail} addon={<Icon name='mail' size='x20'/>}/>
<TextInput onBlur={checkEmailExists} error={emailError} flexGrow={1} value={email} onChange={handleEmail} />
</Field.Row>
<Field.Error>
{t(emailError)}
Expand All @@ -199,7 +223,7 @@ export function ContactNewEdit({ id, data, reload, close }) {
<Field>
<Field.Label>{t('Phone')}</Field.Label>
<Field.Row>
<TextInput error={phoneError} flexGrow={1} value={phone} onChange={handlePhone} />
<TextInput onBlur={checkPhoneExists} error={phoneError} flexGrow={1} value={phone} onChange={handlePhone} />
</Field.Row>
<Field.Error>
{t(phoneError)}
Expand Down
2 changes: 2 additions & 0 deletions packages/rocketchat-i18n/i18n/en.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -1539,6 +1539,7 @@
"error-invalid-method": "Invalid method",
"error-invalid-name": "Invalid name",
"error-invalid-password": "Invalid password",
"error-invalid-params": "Invalid params",
"error-invalid-permission": "Invalid permission",
"error-invalid-priority": "Invalid priority",
"error-invalid-redirectUri": "Invalid redirectUri",
Expand Down Expand Up @@ -2921,6 +2922,7 @@
"Permissions": "Permissions",
"Personal_Access_Tokens": "Personal Access Tokens",
"Phone": "Phone",
"Phone_already_exists": "Phone already exists",
"Phone_number": "Phone number",
"Pin": "Pin",
"Pin_Message": "Pin Message",
Expand Down
1 change: 1 addition & 0 deletions packages/rocketchat-i18n/i18n/pt-BR.i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -2519,6 +2519,7 @@
"Permissions": "Permissões",
"Personal_Access_Tokens": "Tokens de acesso pessoal",
"Phone": "Telefone",
"Phone_already_exists": "Telefone já cadastrado",
"Phone_number": "Telefone",
"Pin": "Fixar",
"Pin_Message": "Fixar Mensagem",
Expand Down