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

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