You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MessageHandler.js 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. /* global $, APP */
  2. const logger = require('jitsi-meet-logger').getLogger(__filename);
  3. import jitsiLocalStorage from '../../util/JitsiLocalStorage';
  4. import {
  5. showErrorNotification,
  6. showNotification,
  7. showWarningNotification
  8. } from '../../../react/features/notifications';
  9. /**
  10. * Flag for enabling/disabling popups.
  11. * @type {boolean}
  12. */
  13. let popupEnabled = true;
  14. /**
  15. * Currently displayed two button dialog.
  16. * @type {null}
  17. */
  18. let twoButtonDialog = null;
  19. /**
  20. * Generates html for dont show again checkbox.
  21. * @param {object} options options
  22. * @param {string} options.id the id of the checkbox.
  23. * @param {string} options.textKey the key for the text displayed next to
  24. * checkbox
  25. * @param {boolean} options.checked if true the checkbox is foing to be checked
  26. * by default.
  27. * @returns {string}
  28. */
  29. function generateDontShowCheckbox(options) {
  30. if (!isDontShowAgainEnabled(options)) {
  31. return '';
  32. }
  33. const checked
  34. = options.checked === true ? 'checked' : '';
  35. return `<br />
  36. <label>
  37. <input type='checkbox' ${checked} id='${options.id}' />
  38. <span data-i18n='${options.textKey}'></span>
  39. </label>`;
  40. }
  41. /**
  42. * Checks whether the dont show again checkbox was checked before.
  43. * @param {object} options - options for dont show again checkbox.
  44. * @param {string} options.id the id of the checkbox.
  45. * @param {string} options.localStorageKey the key for the local storage. if
  46. * not provided options.id will be used.
  47. * @returns {boolean} true if the dialog mustn't be displayed and
  48. * false otherwise.
  49. */
  50. function dontShowTheDialog(options) {
  51. if (isDontShowAgainEnabled(options)) {
  52. if (jitsiLocalStorage.getItem(options.localStorageKey || options.id)
  53. === 'true') {
  54. return true;
  55. }
  56. }
  57. return false;
  58. }
  59. /**
  60. * Wraps the submit function to process the dont show again status and store
  61. * it.
  62. * @param {object} options - options for dont show again checkbox.
  63. * @param {string} options.id the id of the checkbox.
  64. * @param {Array} options.buttonValues The button values that will trigger
  65. * storing he checkbox value
  66. * @param {string} options.localStorageKey the key for the local storage. if
  67. * not provided options.id will be used.
  68. * @param {Function} submitFunction the submit function to be wrapped
  69. * @returns {Function} wrapped function
  70. */
  71. function dontShowAgainSubmitFunctionWrapper(options, submitFunction) {
  72. if (isDontShowAgainEnabled(options)) {
  73. return (...args) => {
  74. logger.debug(args, options.buttonValues);
  75. // args[1] is the value associated with the pressed button
  76. if (!options.buttonValues || options.buttonValues.length === 0
  77. || options.buttonValues.indexOf(args[1]) !== -1) {
  78. const checkbox = $(`#${options.id}`);
  79. if (checkbox.length) {
  80. jitsiLocalStorage.setItem(
  81. options.localStorageKey || options.id,
  82. checkbox.prop('checked'));
  83. }
  84. }
  85. submitFunction(...args);
  86. };
  87. }
  88. return submitFunction;
  89. }
  90. /**
  91. * Check whether dont show again checkbox is enabled or not.
  92. * @param {object} options - options for dont show again checkbox.
  93. * @returns {boolean} true if enabled and false if not.
  94. */
  95. function isDontShowAgainEnabled(options) {
  96. return typeof options === 'object';
  97. }
  98. const messageHandler = {
  99. OK: 'dialog.OK',
  100. CANCEL: 'dialog.Cancel',
  101. /**
  102. * Shows a message to the user.
  103. *
  104. * @param titleKey the key used to find the translation of the title of the
  105. * message, if a message title is not provided.
  106. * @param messageKey the key used to find the translation of the message
  107. * @param i18nOptions the i18n options (optional)
  108. * @param closeFunction function to be called after
  109. * the prompt is closed (optional)
  110. * @return the prompt that was created, or null
  111. */
  112. // eslint-disable-next-line max-params
  113. openMessageDialog(titleKey, messageKey, i18nOptions, closeFunction) {
  114. if (!popupEnabled) {
  115. return null;
  116. }
  117. const dialog = $.prompt(
  118. APP.translation.generateTranslationHTML(messageKey, i18nOptions),
  119. {
  120. title: this._getFormattedTitleString(titleKey),
  121. persistent: false,
  122. promptspeed: 0,
  123. classes: this._getDialogClasses(),
  124. // eslint-disable-next-line max-params
  125. close(e, v, m, f) {
  126. if (closeFunction) {
  127. closeFunction(e, v, m, f);
  128. }
  129. }
  130. });
  131. APP.translation.translateElement(dialog, i18nOptions);
  132. return $.prompt.getApi();
  133. },
  134. /**
  135. * Shows a message to the user with two buttons: first is given as a
  136. * parameter and the second is Cancel.
  137. *
  138. * @param titleKey the key for the title of the message
  139. * @param msgKey the key for the message
  140. * @param msgString the text of the message
  141. * @param persistent boolean value which determines whether the message is
  142. * persistent or not
  143. * @param leftButton the fist button's text
  144. * @param submitFunction function to be called on submit
  145. * @param loadedFunction function to be called after the prompt is fully
  146. * loaded
  147. * @param closeFunction function to be called after the prompt is closed
  148. * @param focus optional focus selector or button index to be focused after
  149. * the dialog is opened
  150. * @param defaultButton index of default button which will be activated when
  151. * the user press 'enter'. Indexed from 0.
  152. * @param {object} dontShowAgain - options for dont show again checkbox.
  153. * @param {string} dontShowAgain.id the id of the checkbox.
  154. * @param {string} dontShowAgain.textKey the key for the text displayed
  155. * next to checkbox
  156. * @param {boolean} dontShowAgain.checked if true the checkbox is foing to
  157. * be checked
  158. * @param {Array} dontShowAgain.buttonValues The button values that will
  159. * trigger storing the checkbox value
  160. * @param {string} dontShowAgain.localStorageKey the key for the local
  161. * storage. if not provided dontShowAgain.id will be used.
  162. * @return the prompt that was created, or null
  163. */
  164. openTwoButtonDialog(options) {
  165. const {
  166. titleKey,
  167. msgKey,
  168. msgString,
  169. leftButtonKey,
  170. submitFunction,
  171. loadedFunction,
  172. closeFunction,
  173. focus,
  174. size,
  175. defaultButton,
  176. wrapperClass,
  177. dontShowAgain
  178. } = options;
  179. let { classes } = options;
  180. if (!popupEnabled || twoButtonDialog) {
  181. return null;
  182. }
  183. if (dontShowTheDialog(dontShowAgain)) {
  184. // Maybe we should pass some parameters here? I'm not sure
  185. // and currently we don't need any parameters.
  186. submitFunction();
  187. return null;
  188. }
  189. const buttons = [];
  190. const leftButton = leftButtonKey
  191. ? APP.translation.generateTranslationHTML(leftButtonKey)
  192. : APP.translation.generateTranslationHTML('dialog.Submit');
  193. buttons.push({ title: leftButton,
  194. value: true });
  195. const cancelButton
  196. = APP.translation.generateTranslationHTML('dialog.Cancel');
  197. buttons.push({ title: cancelButton,
  198. value: false });
  199. let message = msgString;
  200. if (msgKey) {
  201. message = APP.translation.generateTranslationHTML(msgKey);
  202. }
  203. message += generateDontShowCheckbox(dontShowAgain);
  204. classes = classes || this._getDialogClasses(size);
  205. if (wrapperClass) {
  206. classes.prompt += ` ${wrapperClass}`;
  207. }
  208. twoButtonDialog = $.prompt(message, {
  209. title: this._getFormattedTitleString(titleKey),
  210. persistent: false,
  211. buttons,
  212. defaultButton,
  213. focus,
  214. loaded: loadedFunction,
  215. promptspeed: 0,
  216. classes,
  217. submit: dontShowAgainSubmitFunctionWrapper(dontShowAgain,
  218. (e, v, m, f) => { // eslint-disable-line max-params
  219. twoButtonDialog = null;
  220. if (v && submitFunction) {
  221. submitFunction(e, v, m, f);
  222. }
  223. }),
  224. close(e, v, m, f) { // eslint-disable-line max-params
  225. twoButtonDialog = null;
  226. if (closeFunction) {
  227. closeFunction(e, v, m, f);
  228. }
  229. }
  230. });
  231. APP.translation.translateElement(twoButtonDialog);
  232. return $.prompt.getApi();
  233. },
  234. /**
  235. * Shows a message to the user with two buttons: first is given as a
  236. * parameter and the second is Cancel.
  237. *
  238. * @param titleKey the key for the title of the message
  239. * @param msgString the text of the message
  240. * @param persistent boolean value which determines whether the message is
  241. * persistent or not
  242. * @param buttons object with the buttons. The keys must be the name of the
  243. * button and value is the value that will be passed to
  244. * submitFunction
  245. * @param submitFunction function to be called on submit
  246. * @param loadedFunction function to be called after the prompt is fully
  247. * loaded
  248. * @param closeFunction function to be called on dialog close
  249. * @param {object} dontShowAgain - options for dont show again checkbox.
  250. * @param {string} dontShowAgain.id the id of the checkbox.
  251. * @param {string} dontShowAgain.textKey the key for the text displayed
  252. * next to checkbox
  253. * @param {boolean} dontShowAgain.checked if true the checkbox is foing to
  254. * be checked
  255. * @param {Array} dontShowAgain.buttonValues The button values that will
  256. * trigger storing the checkbox value
  257. * @param {string} dontShowAgain.localStorageKey the key for the local
  258. * storage. if not provided dontShowAgain.id will be used.
  259. */
  260. openDialog(// eslint-disable-line max-params
  261. titleKey,
  262. msgString,
  263. persistent,
  264. buttons,
  265. submitFunction,
  266. loadedFunction,
  267. closeFunction,
  268. dontShowAgain) {
  269. if (!popupEnabled) {
  270. return;
  271. }
  272. if (dontShowTheDialog(dontShowAgain)) {
  273. // Maybe we should pass some parameters here? I'm not sure
  274. // and currently we don't need any parameters.
  275. submitFunction();
  276. return;
  277. }
  278. const args = {
  279. title: this._getFormattedTitleString(titleKey),
  280. persistent,
  281. buttons,
  282. defaultButton: 1,
  283. promptspeed: 0,
  284. loaded() {
  285. if (loadedFunction) {
  286. // eslint-disable-next-line prefer-rest-params
  287. loadedFunction.apply(this, arguments);
  288. }
  289. // Hide the close button
  290. if (persistent) {
  291. $('.jqiclose', this).hide();
  292. }
  293. },
  294. submit: dontShowAgainSubmitFunctionWrapper(
  295. dontShowAgain, submitFunction),
  296. close: closeFunction,
  297. classes: this._getDialogClasses()
  298. };
  299. if (persistent) {
  300. args.closeText = '';
  301. }
  302. const dialog = $.prompt(
  303. msgString + generateDontShowCheckbox(dontShowAgain), args);
  304. APP.translation.translateElement(dialog);
  305. return $.prompt.getApi();
  306. },
  307. /**
  308. * Returns the formatted title string.
  309. *
  310. * @return the title string formatted as a div.
  311. */
  312. _getFormattedTitleString(titleKey) {
  313. const $titleString = $('<h2>');
  314. $titleString.addClass('aui-dialog2-header-main');
  315. $titleString.attr('data-i18n', titleKey);
  316. return $('<div>').append($titleString)
  317. .html();
  318. },
  319. /**
  320. * Returns the dialog css classes.
  321. *
  322. * @return the dialog css classes
  323. */
  324. _getDialogClasses(size = 'small') {
  325. return {
  326. box: '',
  327. form: '',
  328. prompt: `dialog aui-layer aui-dialog2 aui-dialog2-${size}`,
  329. close: 'aui-icon aui-icon-small aui-iconfont-close-dialog',
  330. fade: 'aui-blanket',
  331. button: 'button-control',
  332. message: 'aui-dialog2-content',
  333. buttons: 'aui-dialog2-footer',
  334. defaultButton: 'button-control_primary',
  335. title: 'aui-dialog2-header'
  336. };
  337. },
  338. /**
  339. * Shows a dialog with different states to the user.
  340. *
  341. * @param statesObject object containing all the states of the dialog.
  342. * @param options impromptu options
  343. * @param translateOptions options passed to translation
  344. */
  345. openDialogWithStates(statesObject, options, translateOptions) {
  346. if (!popupEnabled) {
  347. return;
  348. }
  349. const { classes, size } = options;
  350. const defaultClasses = this._getDialogClasses(size);
  351. options.classes = Object.assign({}, defaultClasses, classes);
  352. options.promptspeed = options.promptspeed || 0;
  353. for (const state in statesObject) { // eslint-disable-line guard-for-in
  354. const currentState = statesObject[state];
  355. if (currentState.titleKey) {
  356. currentState.title
  357. = this._getFormattedTitleString(currentState.titleKey);
  358. }
  359. }
  360. const dialog = $.prompt(statesObject, options);
  361. APP.translation.translateElement(dialog, translateOptions);
  362. return $.prompt.getApi();
  363. },
  364. /**
  365. * Opens new popup window for given <tt>url</tt> centered over current
  366. * window.
  367. *
  368. * @param url the URL to be displayed in the popup window
  369. * @param w the width of the popup window
  370. * @param h the height of the popup window
  371. * @param onPopupClosed optional callback function called when popup window
  372. * has been closed.
  373. *
  374. * @returns {object} popup window object if opened successfully or undefined
  375. * in case we failed to open it(popup blocked)
  376. */
  377. // eslint-disable-next-line max-params
  378. openCenteredPopup(url, w, h, onPopupClosed) {
  379. if (!popupEnabled) {
  380. return;
  381. }
  382. const l = window.screenX + (window.innerWidth / 2) - (w / 2);
  383. const t = window.screenY + (window.innerHeight / 2) - (h / 2);
  384. const popup = window.open(
  385. url, '_blank',
  386. String(`top=${t}, left=${l}, width=${w}, height=${h}`));
  387. if (popup && onPopupClosed) {
  388. const pollTimer = window.setInterval(() => {
  389. if (popup.closed !== false) {
  390. window.clearInterval(pollTimer);
  391. onPopupClosed();
  392. }
  393. }, 200);
  394. }
  395. return popup;
  396. },
  397. /**
  398. * Shows an error dialog to the user.
  399. *
  400. * @param {object} props - The properties to pass to the
  401. * showErrorNotification action.
  402. */
  403. showError(props) {
  404. APP.store.dispatch(showErrorNotification(props));
  405. },
  406. /**
  407. * Shows a warning dialog to the user.
  408. *
  409. * @param {object} props - The properties to pass to the
  410. * showWarningNotification action.
  411. */
  412. showWarning(props) {
  413. APP.store.dispatch(showWarningNotification(props));
  414. },
  415. /**
  416. * Displays a notification about participant action.
  417. * @param displayName the display name of the participant that is
  418. * associated with the notification.
  419. * @param displayNameKey the key from the language file for the display
  420. * name. Only used if displayName is not provided.
  421. * @param cls css class for the notification
  422. * @param messageKey the key from the language file for the text of the
  423. * message.
  424. * @param messageArguments object with the arguments for the message.
  425. * @param optional configurations for the notification (e.g. timeout)
  426. */
  427. participantNotification( // eslint-disable-line max-params
  428. displayName,
  429. displayNameKey,
  430. cls,
  431. messageKey,
  432. messageArguments,
  433. timeout = 2500) {
  434. APP.store.dispatch(showNotification({
  435. descriptionArguments: messageArguments,
  436. descriptionKey: messageKey,
  437. titleKey: displayNameKey,
  438. title: displayName
  439. },
  440. timeout));
  441. },
  442. /**
  443. * Displays a notification.
  444. *
  445. * @param {string} titleKey - The key from the language file for the title
  446. * of the notification.
  447. * @param {string} messageKey - The key from the language file for the text
  448. * of the message.
  449. * @param {Object} messageArguments - The arguments for the message
  450. * translation.
  451. * @returns {void}
  452. */
  453. notify(titleKey, messageKey, messageArguments) {
  454. this.participantNotification(
  455. null, titleKey, null, messageKey, messageArguments);
  456. },
  457. enablePopups(enable) {
  458. popupEnabled = enable;
  459. },
  460. /**
  461. * Returns true if dialog is opened
  462. * false otherwise
  463. * @returns {boolean} isOpened
  464. */
  465. isDialogOpened() {
  466. return Boolean($.prompt.getCurrentStateName());
  467. }
  468. };
  469. export default messageHandler;