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

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