ringcentral-widgets
Version:
RingCentral Integration Widget Library
399 lines (354 loc) • 10.8 kB
text/typescript
import formatMessage from 'format-message';
import { RcVDialInNumberObj } from 'ringcentral-integration/interfaces/Rcv.model';
import {
MEETING_URI_REGEXP,
rcvAttTeleconference,
rcvTeleconference,
} from './config';
import { formatMeetingId } from './formatMeetingId';
import i18n from './i18n';
import {
CommonBrand,
FormatToHtmlOptions,
RcmMainParams,
RcvMainParams,
TplResult,
} from './index.interface';
function formatSmartphones(
dialInNumber: string | RcVDialInNumberObj[],
pinNumber: string,
showMeetingPasswordPSTN: boolean,
meetingPasswordPSTN: string,
) {
if (!dialInNumber || dialInNumber.length === 0) {
return '';
}
if (typeof dialInNumber === 'string') {
return `${dialInNumber},,${pinNumber}#${
showMeetingPasswordPSTN ? `,,${meetingPasswordPSTN}#` : ''
}`;
}
return dialInNumber
.map((obj) => {
const passwordField = showMeetingPasswordPSTN
? `,,${meetingPasswordPSTN}#`
: '';
const locationField =
obj?.country?.name && obj.location
? `${obj.country.name} (${obj.location})`
: obj?.country?.name || '';
return `${obj.phoneNumber},,${pinNumber}#${passwordField} ${locationField}`;
})
.join('\n\t');
}
function formatDialInNumber(dialInNumber: string | RcVDialInNumberObj[]) {
if (!dialInNumber || dialInNumber.length === 0) {
return '';
}
if (typeof dialInNumber === 'string') {
return dialInNumber;
}
return dialInNumber
.map((obj) => {
const locationField =
obj?.country?.name && obj.location
? `${obj.country.name} (${obj.location})`
: obj?.country?.name || '';
return `${obj.phoneNumber} ${locationField}`;
})
.join('\n\t');
}
function getPasswordTpl(
meetingPassword: string,
currentLocale: string,
): string {
const passwordLiteral = i18n.getString('password', currentLocale);
return meetingPassword ? `${passwordLiteral}: ${meetingPassword}` : '';
}
/**
* replace all text link into anchor link
* Should match: http://www.example.com
* Should not match: <a href="http://www.example.com">http://www.example.com </a>
* Then replace it into <a href="http://www.example.com">http://www.example.com </a>
* @param input
*/
function replaceTextLinksToAnchors(input: string): string {
/**
* [^<>\]]+ means should match any characters except < or > or ]
* (?!\s*<\/a>) means url should not be followed by either "</a>" or " </a>"
* (?!"[^>]*>) means url should not followed by ">
* further explanation: origin string: <a href="http://www.example.com">http://www.example.com </a> should not match
* (?=[\s!,?\]]|$) means url can be followed by punctuations or whitespace or nothing
*/
// https://stackoverflow.com/questions/19060460/url-replace-with-anchor-not-replacing-existing-anchors
const pattern: RegExp = /(?:(?:ht|f)tps?:\/\/|www)[^<>\]]+(?!\s*<\/a>)(?!"[^>]*>)(?=[\s!,?\]<]|$)/gim;
return input.replace(pattern, ($0: string): string => {
return `<a target="_blank" href="${$0}">${$0}</a>`;
});
}
const htmlNewLine: string = '<br>';
const htmlIndentation: string = ' ';
const htmlTabIndentation: string = htmlIndentation.repeat(4);
function formatTextToHtml(
plantText: string,
options: FormatToHtmlOptions = {},
): string {
const {
links = [],
searchLinks = false,
newLine = htmlNewLine,
indentation = htmlIndentation,
tabIndentation = htmlTabIndentation,
} = options;
let htmlContent = plantText
.replace(/\r\n|\n|\r/g, '\n') // formalize newline
.split('\n') // split with formalized newline
.map((line) => {
return line
.replace(/\t/g, tabIndentation) // replace all Tab with 4 indentations
.replace(/^\s*/, ($0) => indentation.repeat($0.length)); // replace leading whtespaces with indentations
})
.join(newLine);
links.forEach((uri) => {
if (uri) {
htmlContent = htmlContent.replace(
uri,
`<a target="_blank" href="${uri}">${uri}</a>`,
);
}
});
if (searchLinks) {
htmlContent = replaceTextLinksToAnchors(htmlContent);
}
return htmlContent;
}
/**
* Dial-in password: ${passwordPstn}
*/
function getRcvPstnPasswordTpl(
meetingPasswordPSTN: string,
currentLocale: string,
): string {
const passwordPstnLiteral = i18n.getString('passwordPstn', currentLocale);
return `${passwordPstnLiteral} ${meetingPasswordPSTN}`;
}
function getBaseRcmTpl(
{ meeting, serviceInfo, extensionInfo, invitationInfo }: RcmMainParams,
brand: CommonBrand,
currentLocale: string,
): TplResult {
const accountName = extensionInfo.name;
const meetingId = meeting.id;
const joinUri = meeting.links.joinUri;
const password = meeting.password;
const mobileDialingNumberTpl = serviceInfo.mobileDialingNumberTpl;
const phoneDialingNumberTpl = serviceInfo.phoneDialingNumberTpl;
const passwordTpl = getPasswordTpl(password, currentLocale);
const teleconference = brand.brandConfig.teleconference;
let formattedMsg = invitationInfo?.invitation;
if (!formattedMsg) {
formattedMsg = formatMessage(
i18n.getString('inviteMeetingContent', currentLocale),
{
accountName,
brandName: i18n.getString(brand.name),
joinUri,
passwordTpl,
mobileDialingNumberTpl,
phoneDialingNumberTpl,
meetingId: formatMeetingId(meetingId),
teleconference,
},
);
}
return {
formattedMsg,
links: {
joinUri,
teleconference,
},
};
}
function getRcmEventTpl(
mainInfo: RcmMainParams,
brand: CommonBrand,
currentLocale: string,
): string {
const tplResult = getBaseRcmTpl(mainInfo, brand, currentLocale);
return tplResult.formattedMsg;
}
function getRcmHtmlEventTpl(
mainInfo: RcmMainParams,
brand: CommonBrand,
currentLocale: string,
): string {
const tplResult = getBaseRcmTpl(mainInfo, brand, currentLocale);
return formatTextToHtml(tplResult.formattedMsg, {
links: [tplResult.links.joinUri, tplResult.links.teleconference],
});
}
function getBaseRcvTpl(
{ meeting, extensionInfo, dialInNumber, invitationInfo }: RcvMainParams,
brand: CommonBrand,
currentLocale: string,
enableRcvConnector = false,
): TplResult {
const joinUri = meeting.joinUri;
const isATT = brand.code === 'att';
const teleconference = isATT ? rcvAttTeleconference : rcvTeleconference;
if (invitationInfo?.body) {
return {
formattedMsg: invitationInfo.body,
links: {
joinUri,
teleconference,
},
};
}
const accountName = extensionInfo.name;
const { meetingPassword, meetingPasswordPSTN, isMeetingSecret } = meeting;
let productName;
const meetingContent: Array<string> = [];
const showMeetingPasswordPSTN = !!(isMeetingSecret && meetingPasswordPSTN);
if (brand.name === 'RingCentral') {
productName = 'RingCentral Video';
meetingContent.push(
i18n.getString('rcvRCBrandInviteMeetingContent', currentLocale),
);
} else {
productName = brand.name;
meetingContent.push(
i18n.getString('rcvInviteMeetingContent', currentLocale),
);
}
if (dialInNumber && dialInNumber.length > 0) {
/* TODO: after get the translation, remove rcvInviteMeetingContentDial
* rcvInviteMeetingContentCountryDial is the correct one
*/
meetingContent.push(
i18n.getString(
typeof dialInNumber === 'string'
? 'rcvInviteMeetingContentDial'
: 'rcvInviteMeetingContentCountryDial',
currentLocale,
),
);
if (showMeetingPasswordPSTN) {
meetingContent.push(
getRcvPstnPasswordTpl(meetingPasswordPSTN, currentLocale),
);
}
}
meetingContent.push(`${i18n.getString('rcvTeleconference', currentLocale)}`);
const passwordTpl = isMeetingSecret
? getPasswordTpl(meetingPassword, currentLocale)
: '';
if (enableRcvConnector) {
meetingContent.push(`${i18n.getString('rcvSipHeader', currentLocale)}`);
const rcvSipContent = isMeetingSecret
? 'rcvSipContentWithPwd'
: 'rcvSipContentNoPwd';
meetingContent.push(`${i18n.getString(rcvSipContent, currentLocale)}`);
}
const shortId = meeting.shortId;
const meetingId = meeting.id;
const formattedMsg = formatMessage(meetingContent.join(''), {
accountName,
brandName: isATT ? `AT&T ${brand.name}` : i18n.getString(brand.name),
joinUri,
passwordTpl,
meetingPasswordPSTN,
meetingId,
pinNumber: formatMeetingId(meeting.shortId),
teleconference,
productName,
dialNumber: formatDialInNumber(dialInNumber),
smartphones: formatSmartphones(
dialInNumber,
shortId,
showMeetingPasswordPSTN,
meetingPasswordPSTN,
),
});
return {
formattedMsg,
links: {
joinUri,
teleconference,
},
};
}
function getRcvEventTpl(
mainInfo: RcvMainParams,
brand: CommonBrand,
currentLocale: string,
enableRcvConnector = false,
): string {
const tplResult = getBaseRcvTpl(
mainInfo,
brand,
currentLocale,
enableRcvConnector,
);
return tplResult.formattedMsg;
}
function getRcvHtmlEventTpl(
mainInfo: RcvMainParams,
brand: CommonBrand,
currentLocale: string,
): string {
const tplResult = getBaseRcvTpl(mainInfo, brand, currentLocale);
return formatTextToHtml(tplResult.formattedMsg, {
links: [tplResult.links.joinUri, tplResult.links.teleconference],
});
}
function getMeetingId(meetingUri: string): string {
if (meetingUri) {
const regs = [MEETING_URI_REGEXP.RCM, MEETING_URI_REGEXP.RCV];
for (let i = 0; i < regs.length; i += 1) {
const matches = regs[i].exec(meetingUri);
if (matches && matches.length > 0) {
const match0 = matches[0];
const link = (match0.indexOf('?') > -1
? matches[0].substring(0, matches[0].indexOf('?'))
: match0
).split('/');
const id = link[link.length - 1];
return id;
}
}
}
return null;
}
function stripMeetingLinks(text: string): string {
let result = text;
[MEETING_URI_REGEXP.RCM, MEETING_URI_REGEXP.RCV].forEach((reg) => {
while (reg.test(result)) {
result = result.replace(reg, '');
}
});
return result;
}
function meetingLinkContains(
text?: string,
): { hasRCM: boolean; hasRCV: boolean } {
return {
hasRCM: MEETING_URI_REGEXP.RCM.test(text ?? ''),
hasRCV: MEETING_URI_REGEXP.RCV.test(text ?? ''),
};
}
export {
formatMeetingId,
stripMeetingLinks,
meetingLinkContains,
replaceTextLinksToAnchors,
htmlNewLine,
htmlIndentation,
htmlTabIndentation,
formatTextToHtml,
getRcmEventTpl,
getRcmHtmlEventTpl,
getRcvEventTpl,
getRcvHtmlEventTpl,
getMeetingId,
};