Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

InviteDialogView.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. /* global $, APP, JitsiMeetJS */
  2. /**
  3. * Substate for password
  4. * @type {{LOCKED: string, UNLOCKED: string}}
  5. */
  6. const States = {
  7. LOCKED: 'locked',
  8. UNLOCKED: 'unlocked'
  9. };
  10. /**
  11. * Class representing view for Invite dialog
  12. * @class InviteDialogView
  13. */
  14. export default class InviteDialogView {
  15. constructor(model) {
  16. let inviteAttributesKey = 'inviteUrlDefaultMsg';
  17. let title = APP.translation.translateString(inviteAttributesKey);
  18. this.unlockHint = "unlockHint";
  19. this.lockHint = "lockHint";
  20. this.model = model;
  21. if (this.model.inviteUrl === null) {
  22. this.inviteAttributes = (
  23. `data-i18n="[value]${inviteAttributesKey}" value="${title}"`
  24. );
  25. } else {
  26. let encodedInviteUrl = this.model.getEncodedInviteUrl();
  27. this.inviteAttributes = `value="${encodedInviteUrl}"`;
  28. }
  29. this.initDialog();
  30. }
  31. /**
  32. * Initialization of dialog property
  33. */
  34. initDialog() {
  35. let dialog = {};
  36. dialog.closeFunction = this.closeFunction.bind(this);
  37. dialog.submitFunction = this.submitFunction.bind(this);
  38. dialog.loadedFunction = this.loadedFunction.bind(this);
  39. dialog.titleKey = "dialog.shareLink";
  40. this.dialog = dialog;
  41. this.dialog.states = this.getStates();
  42. }
  43. /**
  44. * Event handler for submitting dialog
  45. * @param e
  46. * @param v
  47. */
  48. submitFunction(e, v) {
  49. if (v && this.model.inviteUrl) {
  50. JitsiMeetJS.analytics.sendEvent('toolbar.invite.button');
  51. } else {
  52. JitsiMeetJS.analytics.sendEvent('toolbar.invite.cancel');
  53. }
  54. }
  55. /**
  56. * Event handler for load dialog
  57. * @param event
  58. */
  59. loadedFunction(event) {
  60. if (this.model.inviteUrl) {
  61. document.getElementById('inviteLinkRef').select();
  62. } else {
  63. if (event && event.target) {
  64. $(event.target).find('button[value=true]')
  65. .prop('disabled', true);
  66. }
  67. }
  68. }
  69. /**
  70. * Event handler for closing dialog
  71. * @param e
  72. * @param v
  73. * @param m
  74. * @param f
  75. */
  76. closeFunction(e, v, m, f) {
  77. $(document).off('click', '.copyInviteLink', this.copyToClipboard);
  78. if(!v && !m && !f)
  79. JitsiMeetJS.analytics.sendEvent('toolbar.invite.close');
  80. }
  81. /**
  82. * Returns all states of the dialog
  83. * @returns {{}}
  84. */
  85. getStates() {
  86. let {
  87. titleKey
  88. } = this.dialog;
  89. let doneKey = 'dialog.done';
  90. let doneMsg = APP.translation.translateString(doneKey);
  91. let states = {};
  92. let buttons = {};
  93. buttons[`${doneMsg}`] = true;
  94. states[States.UNLOCKED] = {
  95. titleKey,
  96. html: this.getShareLinkBlock() + this.getAddPasswordBlock(),
  97. buttons
  98. };
  99. states[States.LOCKED] = {
  100. titleKey,
  101. html: this.getShareLinkBlock() + this.getPasswordBlock(),
  102. buttons
  103. };
  104. return states;
  105. }
  106. /**
  107. * Layout for invite link input
  108. * @returns {string}
  109. */
  110. getShareLinkBlock() {
  111. let copyKey = 'dialog.copy';
  112. let copyText = APP.translation.translateString(copyKey);
  113. let roomLockDescKey = 'dialog.roomLocked';
  114. let roomLockDesc = APP.translation.translateString(roomLockDescKey);
  115. let roomUnlockKey = 'roomUnlocked';
  116. let roomUnlock = APP.translation.translateString(roomUnlockKey);
  117. let classes = 'button-control button-control_light copyInviteLink';
  118. let title = APP.translation.translateString(this.dialog.titleKey);
  119. return (
  120. `<div class="input-control">
  121. <label class="input-control__label" for="inviteLinkRef"
  122. data-i18n="${this.dialog.titleKey}">
  123. ${title}
  124. </label>
  125. <div class="input-control__container">
  126. <input class="input-control__input inviteLink"
  127. id="inviteLinkRef" type="text"
  128. ${this.inviteAttributes} readonly>
  129. <button data-i18n="${copyKey}"
  130. class="${classes}">
  131. ${copyText}
  132. </button>
  133. </div>
  134. <p class="input-control__hint ${this.lockHint}">
  135. <span class="icon-security-locked"></span>
  136. <span data-i18n="${roomLockDescKey}">${roomLockDesc}</span>
  137. </p>
  138. <p class="input-control__hint ${this.unlockHint}">
  139. <span class="icon-security"></span>
  140. <span data-i18n="${roomUnlockKey}">${roomUnlock}</span>
  141. </p>
  142. </div>`
  143. );
  144. }
  145. /**
  146. * Layout for adding password input
  147. * @returns {string}
  148. */
  149. getAddPasswordBlock() {
  150. let addPassKey = 'dialog.addPassword';
  151. let addPassText = APP.translation.translateString(addPassKey);
  152. let addKey = 'dialog.add';
  153. let addText = APP.translation.translateString(addKey);
  154. let hintKey = 'dialog.createPassword';
  155. let hintMsg = APP.translation.translateString(hintKey);
  156. let html;
  157. if (this.model.isModerator) {
  158. html = (`
  159. <div class="input-control">
  160. <label class="input-control__label
  161. for="newPasswordInput"
  162. data-i18n="${addPassKey}">${addPassText}</label>
  163. <div class="input-control__container">
  164. <input class="input-control__input" id="newPasswordInput"
  165. type="text" placeholder="${hintMsg}">
  166. <button id="addPasswordBtn" id="inviteDialogAddPassword"
  167. disabled data-i18n="${addKey}"
  168. class="button-control button-control_light">
  169. ${addText}
  170. </button>
  171. </div>
  172. </div>
  173. `);
  174. } else {
  175. html = '';
  176. }
  177. return html;
  178. }
  179. /**
  180. * Layout for password (when room is locked)
  181. * @returns {string}
  182. */
  183. getPasswordBlock() {
  184. let { password, isModerator } = this.model;
  185. let removePassKey = 'dialog.removePassword';
  186. let removePassText = APP.translation.translateString(removePassKey);
  187. let currentPassKey = 'dialog.currentPassword';
  188. let currentPassText = APP.translation.translateString(currentPassKey);
  189. let passwordKey = "dialog.passwordLabel";
  190. let passwordText = APP.translation.translateString(passwordKey);
  191. if (isModerator) {
  192. return (`
  193. <div class="input-control">
  194. <label class="input-control__label"
  195. data-i18n="${passwordKey}">${passwordText}</label>
  196. <div class="input-control__container">
  197. <p class="input-control__text"
  198. data-i18n="${currentPassKey}">
  199. ${currentPassText}
  200. <span id="inviteDialogPassword"
  201. class="input-control__em">
  202. ${password}
  203. </span>
  204. </p>
  205. <a class="link input-control__right"
  206. id="inviteDialogRemovePassword"
  207. data-i18n="${removePassKey}">
  208. ${removePassText}
  209. </a>
  210. </div>
  211. </div>
  212. `);
  213. } else {
  214. return (`
  215. <div class="input-control">
  216. <p>A participant protected this call with a password.</p>
  217. </div>
  218. `);
  219. }
  220. }
  221. /**
  222. * Opening the dialog
  223. */
  224. open() {
  225. let {
  226. submitFunction,
  227. loadedFunction,
  228. closeFunction
  229. } = this.dialog;
  230. let states = this.getStates();
  231. let initial = this.model.roomLocked ? States.LOCKED : States.UNLOCKED;
  232. APP.UI.messageHandler.openDialogWithStates(states, {
  233. submit: submitFunction,
  234. loaded: loadedFunction,
  235. close: closeFunction,
  236. size: 'medium'
  237. });
  238. $.prompt.goToState(initial);
  239. this.registerListeners();
  240. this.updateView();
  241. }
  242. /**
  243. * Setting event handlers
  244. * used in dialog
  245. */
  246. registerListeners() {
  247. const ENTER_KEY = 13;
  248. let addPasswordBtn = '#addPasswordBtn';
  249. let copyInviteLink = '.copyInviteLink';
  250. let newPasswordInput = '#newPasswordInput';
  251. let removePassword = '#inviteDialogRemovePassword';
  252. $(document).on('click', copyInviteLink, this.copyToClipboard);
  253. $(removePassword).on('click', () => {
  254. this.model.setRoomUnlocked();
  255. });
  256. let boundSetPassword = this.setPassword.bind(this);
  257. $(document).on('click', addPasswordBtn, boundSetPassword);
  258. let boundDisablePass = this.disableAddPassIfInputEmpty.bind(this);
  259. $(document).on('keypress', newPasswordInput, boundDisablePass);
  260. // We need to handle keydown event because impromptu
  261. // is listening to it too for closing the dialog
  262. $(newPasswordInput).on('keydown', (e) => {
  263. if (e.keyCode === ENTER_KEY) {
  264. e.stopPropagation();
  265. this.setPassword();
  266. }
  267. });
  268. }
  269. setPassword() {
  270. let $passInput = $('#newPasswordInput');
  271. let newPass = $passInput.val();
  272. if(newPass) {
  273. this.model.setRoomLocked(newPass);
  274. }
  275. }
  276. /**
  277. * Checking input and if it's empty then
  278. * disable add pass button
  279. */
  280. disableAddPassIfInputEmpty() {
  281. let $passInput = $('#newPasswordInput');
  282. let $addPassBtn = $('#addPasswordBtn');
  283. if(!$passInput.val()) {
  284. $addPassBtn.prop('disabled', true);
  285. } else {
  286. $addPassBtn.prop('disabled', false);
  287. }
  288. }
  289. /**
  290. * Copying text to clipboard
  291. */
  292. copyToClipboard() {
  293. $('.inviteLink').each(function () {
  294. let $el = $(this).closest('.jqistate');
  295. // TOFIX: We can select only visible elements
  296. if($el.css('display') === 'block') {
  297. this.select();
  298. try {
  299. document.execCommand('copy');
  300. this.blur();
  301. }
  302. catch (err) {
  303. console.error('error when copy the text');
  304. }
  305. }
  306. });
  307. }
  308. /**
  309. * Method syncing the view and the model
  310. */
  311. updateView() {
  312. let pass = this.model.getPassword();
  313. if (!pass)
  314. pass = APP.translation.translateString("passwordSetRemotely");
  315. $('#inviteDialogPassword').attr("data-i18n", "passwordSetRemotely");
  316. $('#inviteDialogPassword').text(pass);
  317. $('#newPasswordInput').val('');
  318. this.disableAddPassIfInputEmpty();
  319. this.updateInviteLink();
  320. $.prompt.goToState(
  321. (this.model.isLocked())
  322. ? States.LOCKED
  323. : States.UNLOCKED);
  324. let roomLocked = `.${this.lockHint}`;
  325. let roomUnlocked = `.${this.unlockHint}`;
  326. let showDesc = this.model.isLocked() ? roomLocked : roomUnlocked;
  327. let hideDesc = !this.model.isLocked() ? roomLocked : roomUnlocked;
  328. $(showDesc).show();
  329. $(hideDesc).hide();
  330. }
  331. /**
  332. * Updates invite link
  333. */
  334. updateInviteLink() {
  335. // If the invite dialog has been already opened we update the
  336. // information.
  337. let inviteLink = document.querySelectorAll('.inviteLink');
  338. let list = Array.from(inviteLink);
  339. list.forEach((inviteLink) => {
  340. inviteLink.value = this.model.inviteUrl;
  341. inviteLink.select();
  342. });
  343. $('#inviteLinkRef').parent()
  344. .find('button[value=true]').prop('disabled', false);
  345. }
  346. }