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

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