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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. /* global $, APP, toastr */
  2. import UIUtil from './UIUtil';
  3. import jitsiLocalStorage from '../../util/JitsiLocalStorage';
  4. /**
  5. * Flag for enable/disable of the notifications.
  6. * @type {boolean}
  7. */
  8. let notificationsEnabled = true;
  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. let 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. console.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. let 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. } else {
  88. return submitFunction;
  89. }
  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. var messageHandler = {
  100. OK: "dialog.OK",
  101. CANCEL: "dialog.Cancel",
  102. /**
  103. * Shows a message to the user.
  104. *
  105. * @param titleKey the key used to find the translation of the title of the
  106. * message, if a message title is not provided.
  107. * @param messageKey the key used to find the translation of the message
  108. * @param i18nOptions the i18n options (optional)
  109. * @param closeFunction function to be called after
  110. * the prompt is closed (optional)
  111. * @return the prompt that was created, or null
  112. */
  113. openMessageDialog:
  114. function(titleKey, messageKey, i18nOptions, closeFunction) {
  115. if (!popupEnabled)
  116. return null;
  117. let 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. close: function (e, v, m, f) {
  125. if(closeFunction)
  126. closeFunction(e, v, m, f);
  127. }
  128. });
  129. APP.translation.translateElement(dialog, i18nOptions);
  130. return $.prompt.getApi();
  131. },
  132. /**
  133. * Shows a message to the user with two buttons: first is given as a
  134. * parameter and the second is Cancel.
  135. *
  136. * @param titleKey the key for the title of the message
  137. * @param msgKey the key for the message
  138. * @param msgString the text of the message
  139. * @param persistent boolean value which determines whether the message is
  140. * persistent or not
  141. * @param leftButton the fist button's text
  142. * @param submitFunction function to be called on submit
  143. * @param loadedFunction function to be called after the prompt is fully
  144. * loaded
  145. * @param closeFunction function to be called after the prompt is closed
  146. * @param focus optional focus selector or button index to be focused after
  147. * the dialog is opened
  148. * @param defaultButton index of default button which will be activated when
  149. * the user press 'enter'. Indexed from 0.
  150. * @param {object} dontShowAgain - options for dont show again checkbox.
  151. * @param {string} dontShowAgain.id the id of the checkbox.
  152. * @param {string} dontShowAgain.textKey the key for the text displayed
  153. * next to checkbox
  154. * @param {boolean} dontShowAgain.checked if true the checkbox is foing to
  155. * be checked
  156. * @param {Array} dontShowAgain.buttonValues The button values that will
  157. * trigger storing the checkbox value
  158. * @param {string} dontShowAgain.localStorageKey the key for the local
  159. * storage. if not provided dontShowAgain.id will be used.
  160. * @return the prompt that was created, or null
  161. */
  162. openTwoButtonDialog: function(options) {
  163. let {
  164. titleKey,
  165. msgKey,
  166. msgString,
  167. leftButtonKey,
  168. submitFunction,
  169. loadedFunction,
  170. closeFunction,
  171. focus,
  172. size,
  173. defaultButton,
  174. wrapperClass,
  175. classes,
  176. dontShowAgain
  177. } = options;
  178. if (!popupEnabled || twoButtonDialog)
  179. return null;
  180. if(dontShowTheDialog(dontShowAgain)) {
  181. // Maybe we should pass some parameters here? I'm not sure
  182. // and currently we don't need any parameters.
  183. submitFunction();
  184. return null;
  185. }
  186. var buttons = [];
  187. var leftButton = leftButtonKey ?
  188. APP.translation.generateTranslationHTML(leftButtonKey) :
  189. APP.translation.generateTranslationHTML('dialog.Submit');
  190. buttons.push({ title: leftButton, value: true});
  191. var cancelButton
  192. = APP.translation.generateTranslationHTML("dialog.Cancel");
  193. buttons.push({title: cancelButton, value: false});
  194. var message = msgString;
  195. if (msgKey) {
  196. message = APP.translation.generateTranslationHTML(msgKey);
  197. }
  198. message += generateDontShowCheckbox(dontShowAgain);
  199. classes = classes || this._getDialogClasses(size);
  200. if (wrapperClass) {
  201. classes.prompt += ` ${wrapperClass}`;
  202. }
  203. twoButtonDialog = $.prompt(message, {
  204. title: this._getFormattedTitleString(titleKey),
  205. persistent: false,
  206. buttons: buttons,
  207. defaultButton: defaultButton,
  208. focus: focus,
  209. loaded: loadedFunction,
  210. promptspeed: 0,
  211. classes,
  212. submit: dontShowAgainSubmitFunctionWrapper(dontShowAgain,
  213. function (e, v, m, f) {
  214. twoButtonDialog = null;
  215. if (v && submitFunction) {
  216. submitFunction(e, v, m, f);
  217. }
  218. }),
  219. close: function (e, v, m, f) {
  220. twoButtonDialog = null;
  221. if (closeFunction) {
  222. closeFunction(e, v, m, f);
  223. }
  224. }
  225. });
  226. APP.translation.translateElement(twoButtonDialog);
  227. return $.prompt.getApi();
  228. },
  229. /**
  230. * Shows a message to the user with two buttons: first is given as a
  231. * parameter and the second is Cancel.
  232. *
  233. * @param titleKey the key for the title of the message
  234. * @param msgString the text of the message
  235. * @param persistent boolean value which determines whether the message is
  236. * persistent or not
  237. * @param buttons object with the buttons. The keys must be the name of the
  238. * button and value is the value that will be passed to
  239. * submitFunction
  240. * @param submitFunction function to be called on submit
  241. * @param loadedFunction function to be called after the prompt is fully
  242. * loaded
  243. * @param closeFunction function to be called on dialog close
  244. * @param {object} dontShowAgain - options for dont show again checkbox.
  245. * @param {string} dontShowAgain.id the id of the checkbox.
  246. * @param {string} dontShowAgain.textKey the key for the text displayed
  247. * next to checkbox
  248. * @param {boolean} dontShowAgain.checked if true the checkbox is foing to
  249. * be checked
  250. * @param {Array} dontShowAgain.buttonValues The button values that will
  251. * trigger storing the checkbox value
  252. * @param {string} dontShowAgain.localStorageKey the key for the local
  253. * storage. if not provided dontShowAgain.id will be used.
  254. */
  255. openDialog: function (titleKey, msgString, persistent, buttons,
  256. submitFunction, loadedFunction, closeFunction, dontShowAgain) {
  257. if (!popupEnabled)
  258. return;
  259. if(dontShowTheDialog(dontShowAgain)) {
  260. // Maybe we should pass some parameters here? I'm not sure
  261. // and currently we don't need any parameters.
  262. submitFunction();
  263. return;
  264. }
  265. let args = {
  266. title: this._getFormattedTitleString(titleKey),
  267. persistent: persistent,
  268. buttons: buttons,
  269. defaultButton: 1,
  270. promptspeed: 0,
  271. loaded: loadedFunction,
  272. submit: dontShowAgainSubmitFunctionWrapper(
  273. dontShowAgain, submitFunction),
  274. close: closeFunction,
  275. classes: this._getDialogClasses()
  276. };
  277. if (persistent) {
  278. args.closeText = '';
  279. }
  280. let dialog = $.prompt(
  281. msgString + generateDontShowCheckbox(dontShowAgain), args);
  282. APP.translation.translateElement(dialog);
  283. return $.prompt.getApi();
  284. },
  285. /**
  286. * Returns the formatted title string.
  287. *
  288. * @return the title string formatted as a div.
  289. */
  290. _getFormattedTitleString(titleKey) {
  291. let $titleString = $('<h2>');
  292. $titleString.addClass('aui-dialog2-header-main');
  293. $titleString.attr('data-i18n',titleKey);
  294. return $('<div>').append($titleString).html();
  295. },
  296. /**
  297. * Returns the dialog css classes.
  298. *
  299. * @return the dialog css classes
  300. */
  301. _getDialogClasses(size = 'small') {
  302. return {
  303. box: '',
  304. form: '',
  305. prompt: `dialog aui-layer aui-dialog2 aui-dialog2-${size}`,
  306. close: 'aui-icon aui-icon-small aui-iconfont-close-dialog',
  307. fade: 'aui-blanket',
  308. button: 'button-control',
  309. message: 'aui-dialog2-content',
  310. buttons: 'aui-dialog2-footer',
  311. defaultButton: 'button-control_primary',
  312. title: 'aui-dialog2-header'
  313. };
  314. },
  315. /**
  316. * Shows a dialog with different states to the user.
  317. *
  318. * @param statesObject object containing all the states of the dialog.
  319. * @param options impromptu options
  320. * @param translateOptions options passed to translation
  321. */
  322. openDialogWithStates: function (statesObject, options, translateOptions) {
  323. if (!popupEnabled)
  324. return;
  325. let { classes, size } = options;
  326. let defaultClasses = this._getDialogClasses(size);
  327. options.classes = Object.assign({}, defaultClasses, classes);
  328. options.promptspeed = options.promptspeed || 0;
  329. for (let state in statesObject) {
  330. let currentState = statesObject[state];
  331. if(currentState.titleKey) {
  332. currentState.title
  333. = this._getFormattedTitleString(currentState.titleKey);
  334. }
  335. }
  336. let dialog = $.prompt(statesObject, options);
  337. APP.translation.translateElement(dialog, translateOptions);
  338. return $.prompt.getApi();
  339. },
  340. /**
  341. * Opens new popup window for given <tt>url</tt> centered over current
  342. * window.
  343. *
  344. * @param url the URL to be displayed in the popup window
  345. * @param w the width of the popup window
  346. * @param h the height of the popup window
  347. * @param onPopupClosed optional callback function called when popup window
  348. * has been closed.
  349. *
  350. * @returns {object} popup window object if opened successfully or undefined
  351. * in case we failed to open it(popup blocked)
  352. */
  353. openCenteredPopup: function (url, w, h, onPopupClosed) {
  354. if (!popupEnabled)
  355. return;
  356. var l = window.screenX + (window.innerWidth / 2) - (w / 2);
  357. var t = window.screenY + (window.innerHeight / 2) - (h / 2);
  358. var popup = window.open(
  359. url, '_blank',
  360. 'top=' + t + ', left=' + l + ', width=' + w + ', height=' + h + '');
  361. if (popup && onPopupClosed) {
  362. var pollTimer = window.setInterval(function () {
  363. if (popup.closed !== false) {
  364. window.clearInterval(pollTimer);
  365. onPopupClosed();
  366. }
  367. }, 200);
  368. }
  369. return popup;
  370. },
  371. /**
  372. * Shows a dialog prompting the user to send an error report.
  373. *
  374. * @param titleKey the title of the message
  375. * @param msgKey the text of the message
  376. * @param error the error that is being reported
  377. */
  378. openReportDialog: function(titleKey, msgKey, error) {
  379. this.openMessageDialog(titleKey, msgKey);
  380. console.log(error);
  381. //FIXME send the error to the server
  382. },
  383. /**
  384. * Shows an error dialog to the user.
  385. * @param titleKey the title of the message.
  386. * @param msgKey the text of the message.
  387. */
  388. showError: function(titleKey, msgKey) {
  389. if (!titleKey) {
  390. titleKey = "dialog.oops";
  391. }
  392. if (!msgKey) {
  393. msgKey = "dialog.defaultError";
  394. }
  395. messageHandler.openMessageDialog(titleKey, msgKey);
  396. },
  397. /**
  398. * Displays a notification.
  399. * @param displayName the display name of the participant that is
  400. * associated with the notification.
  401. * @param displayNameKey the key from the language file for the display
  402. * name. Only used if displayName i not provided.
  403. * @param cls css class for the notification
  404. * @param messageKey the key from the language file for the text of the
  405. * message.
  406. * @param messageArguments object with the arguments for the message.
  407. * @param options object with language options.
  408. */
  409. notify: function(displayName, displayNameKey, cls, messageKey,
  410. messageArguments, options) {
  411. // If we're in ringing state we skip all toaster notifications.
  412. if(!notificationsEnabled || APP.UI.isOverlayVisible())
  413. return;
  414. var displayNameSpan = '<span class="nickname" ';
  415. if (displayName) {
  416. displayNameSpan += ">" + UIUtil.escapeHtml(displayName);
  417. } else {
  418. displayNameSpan += "data-i18n='" + displayNameKey + "'>";
  419. }
  420. displayNameSpan += "</span>";
  421. let element = toastr.info(
  422. displayNameSpan + '<br>' +
  423. '<span class=' + cls + ' data-i18n="' + messageKey + '"' +
  424. (messageArguments?
  425. " data-i18n-options='"
  426. + JSON.stringify(messageArguments) + "'"
  427. : "") + "></span>", null, options);
  428. APP.translation.translateElement(element);
  429. return element;
  430. },
  431. /**
  432. * Removes the toaster.
  433. * @param toasterElement
  434. */
  435. remove: function(toasterElement) {
  436. toasterElement.remove();
  437. },
  438. /**
  439. * Enables / disables notifications.
  440. */
  441. enableNotifications: function (enable) {
  442. notificationsEnabled = enable;
  443. },
  444. enablePopups: function (enable) {
  445. popupEnabled = enable;
  446. },
  447. /**
  448. * Returns true if dialog is opened
  449. * false otherwise
  450. * @returns {boolean} isOpened
  451. */
  452. isDialogOpened: function () {
  453. return !!$.prompt.getCurrentStateName();
  454. }
  455. };
  456. module.exports = messageHandler;