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 16KB

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