Bläddra i källkod

Adds copy icon next to the meeting url in info dialog.

master
damencho 5 år sedan
förälder
incheckning
3e1a008399

+ 17
- 0
css/modals/invite/_info.scss Visa fil

@@ -63,6 +63,8 @@
63 63
         width: -webkit-max-content;
64 64
         word-break: break-all;
65 65
         max-width: 400px;
66
+        display: flex;
67
+        align-items: center;
66 68
     }
67 69
 
68 70
     .info-dialog-dial-in {
@@ -86,6 +88,15 @@
86 88
         cursor: inherit;
87 89
     }
88 90
 
91
+    .info-dialog-url-icon {
92
+        display: inline-block;
93
+        margin-left: 5px;
94
+
95
+        svg {
96
+            cursor: pointer;
97
+        }
98
+    }
99
+
89 100
     .info-dialog-title {
90 101
         font-weight: bold;
91 102
         margin-bottom: 10px;
@@ -214,4 +225,10 @@
214 225
         -moz-user-select: text;
215 226
         -webkit-user-select: text;
216 227
     }
228
+
229
+    .info-dialog-url-text-unselectable {
230
+        user-select: none;
231
+        -moz-user-select: none;
232
+        -webkit-user-select: none;
233
+    }
217 234
 }

+ 7
- 0
react/features/base/icons/components/Icon.js Visa fil

@@ -23,6 +23,11 @@ type Props = {
23 23
      */
24 24
     id?: string,
25 25
 
26
+    /**
27
+     * Function to invoke on click.
28
+     */
29
+    onClick?: Function,
30
+
26 31
     /**
27 32
      * The size of the icon (if not provided by the style object).
28 33
      */
@@ -53,6 +58,7 @@ export default function Icon(props: Props) {
53 58
         className,
54 59
         color,
55 60
         id,
61
+        onClick,
56 62
         size,
57 63
         src: IconComponent,
58 64
         style
@@ -69,6 +75,7 @@ export default function Icon(props: Props) {
69 75
     return (
70 76
         <Container
71 77
             className = { `jitsi-icon ${className}` }
78
+            onClick = { onClick }
72 79
             style = { restStyle }>
73 80
             <IconComponent
74 81
                 fill = { calculatedColor }

+ 3
- 0
react/features/base/icons/svg/copy.svg Visa fil

@@ -0,0 +1,3 @@
1
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+<path fill-rule="evenodd" clip-rule="evenodd" d="M4 4C4 2.89543 4.89543 2 6 2H14C15.1046 2 16 2.89543 16 4H6V18C4.89543 18 4 17.1046 4 16V4ZM10 8V20H18V8H10ZM10 6H18C19.1046 6 20 6.89543 20 8V20C20 21.1046 19.1046 22 18 22H10C8.89543 22 8 21.1046 8 20V8C8 6.89543 8.89543 6 10 6Z" fill="#5E6D7A"/>
3
+</svg>

+ 1
- 0
react/features/base/icons/svg/index.js Visa fil

@@ -18,6 +18,7 @@ export { default as IconClose } from './close.svg';
18 18
 export { default as IconClosedCaption } from './closed_caption.svg';
19 19
 export { default as IconConnectionActive } from './gsm-bars.svg';
20 20
 export { default as IconConnectionInactive } from './ninja.svg';
21
+export { default as IconCopy } from './copy.svg';
21 22
 export { default as IconDeviceBluetooth } from './bluetooth.svg';
22 23
 export { default as IconDeviceEarpiece } from './phone-talk.svg';
23 24
 export { default as IconDeviceHeadphone } from './headset.svg';

+ 58
- 6
react/features/invite/components/info-dialog/web/InfoDialog.js Visa fil

@@ -7,7 +7,7 @@ import { setPassword } from '../../../../base/conference';
7 7
 import { getInviteURL } from '../../../../base/connection';
8 8
 import { Dialog } from '../../../../base/dialog';
9 9
 import { translate } from '../../../../base/i18n';
10
-import { Icon, IconInfo } from '../../../../base/icons';
10
+import { Icon, IconInfo, IconCopy } from '../../../../base/icons';
11 11
 import { connect } from '../../../../base/redux';
12 12
 import {
13 13
     isLocalParticipantModerator,
@@ -136,6 +136,7 @@ type State = {
136 136
  */
137 137
 class InfoDialog extends Component<Props, State> {
138 138
     _copyElement: ?Object;
139
+    _copyUrlElement: ?Object;
139 140
 
140 141
     /**
141 142
      * Implements React's {@link Component#getDerivedStateFromProps()}.
@@ -197,12 +198,14 @@ class InfoDialog extends Component<Props, State> {
197 198
 
198 199
         // Bind event handlers so they are only bound once for every instance.
199 200
         this._onClickURLText = this._onClickURLText.bind(this);
200
-        this._onCopyInviteURL = this._onCopyInviteURL.bind(this);
201
+        this._onCopyInviteInfo = this._onCopyInviteInfo.bind(this);
202
+        this._onCopyInviteUrl = this._onCopyInviteUrl.bind(this);
201 203
         this._onPasswordRemove = this._onPasswordRemove.bind(this);
202 204
         this._onPasswordSubmit = this._onPasswordSubmit.bind(this);
203 205
         this._onTogglePasswordEditState
204 206
             = this._onTogglePasswordEditState.bind(this);
205 207
         this._setCopyElement = this._setCopyElement.bind(this);
208
+        this._setCopyUrlElement = this._setCopyUrlElement.bind(this);
206 209
     }
207 210
 
208 211
     /**
@@ -239,12 +242,18 @@ class InfoDialog extends Component<Props, State> {
239 242
                         <span className = 'spacer'>&nbsp;</span>
240 243
                         <span className = 'info-value'>
241 244
                             <a
242
-                                className = 'info-dialog-url-text'
245
+                                className = 'info-dialog-url-text info-dialog-url-text-unselectable'
243 246
                                 href = { this.props._inviteURL }
244 247
                                 onClick = { this._onClickURLText } >
245 248
                                 { decodeURI(this._getURLToDisplay()) }
246 249
                             </a>
247 250
                         </span>
251
+                        <span className = 'info-dialog-url-icon'>
252
+                            <Icon
253
+                                onClick = { this._onCopyInviteUrl }
254
+                                size = { 18 }
255
+                                src = { IconCopy } />
256
+                        </span>
248 257
                     </div>
249 258
                     <div className = 'info-dialog-dial-in'>
250 259
                         { this._renderDialInDisplay() }
@@ -262,7 +271,7 @@ class InfoDialog extends Component<Props, State> {
262 271
                         <div className = 'info-dialog-action-link'>
263 272
                             <a
264 273
                                 className = 'info-copy'
265
-                                onClick = { this._onCopyInviteURL }>
274
+                                onClick = { this._onCopyInviteInfo }>
266 275
                                 { t('dialog.copy') }
267 276
                             </a>
268 277
                         </div>
@@ -275,6 +284,12 @@ class InfoDialog extends Component<Props, State> {
275 284
                     ref = { this._setCopyElement }
276 285
                     tabIndex = '-1'
277 286
                     value = { this._getTextToCopy() } />
287
+                <textarea
288
+                    className = 'info-dialog-copy-element'
289
+                    readOnly = { true }
290
+                    ref = { this._setCopyUrlElement }
291
+                    tabIndex = '-1'
292
+                    value = { this.props._inviteURL } />
278 293
             </div>
279 294
         );
280 295
 
@@ -365,7 +380,7 @@ class InfoDialog extends Component<Props, State> {
365 380
         event.preventDefault();
366 381
     }
367 382
 
368
-    _onCopyInviteURL: () => void;
383
+    _onCopyInviteInfo: () => void;
369 384
 
370 385
     /**
371 386
      * Callback invoked to copy the contents of {@code this._copyElement} to the
@@ -374,7 +389,7 @@ class InfoDialog extends Component<Props, State> {
374 389
      * @private
375 390
      * @returns {void}
376 391
      */
377
-    _onCopyInviteURL() {
392
+    _onCopyInviteInfo() {
378 393
         try {
379 394
             if (!this._copyElement) {
380 395
                 throw new Error('No element to copy from.');
@@ -388,6 +403,28 @@ class InfoDialog extends Component<Props, State> {
388 403
         }
389 404
     }
390 405
 
406
+    _onCopyInviteUrl: () => void;
407
+
408
+    /**
409
+     * Callback invoked to copy the contents of {@code this._copyUrlElement} to the clipboard.
410
+     *
411
+     * @private
412
+     * @returns {void}
413
+     */
414
+    _onCopyInviteUrl() {
415
+        try {
416
+            if (!this._copyUrlElement) {
417
+                throw new Error('No element to copy from.');
418
+            }
419
+
420
+            this._copyUrlElement && this._copyUrlElement.select();
421
+            document.execCommand('copy');
422
+            this._copyUrlElement && this._copyUrlElement.blur();
423
+        } catch (err) {
424
+            logger.error('error when copying the text', err);
425
+        }
426
+    }
427
+
391 428
     _onPasswordRemove: () => void;
392 429
 
393 430
     /**
@@ -565,6 +602,21 @@ class InfoDialog extends Component<Props, State> {
565 602
     _setCopyElement(element: Object) {
566 603
         this._copyElement = element;
567 604
     }
605
+
606
+    _setCopyUrlElement: () => void;
607
+
608
+    /**
609
+     * Sets the internal reference to the DOM/HTML element backing the React
610
+     * {@code Component} input.
611
+     *
612
+     * @param {HTMLInputElement} element - The DOM/HTML element for this
613
+     * {@code Component}'s input.
614
+     * @private
615
+     * @returns {void}
616
+     */
617
+    _setCopyUrlElement(element: Object) {
618
+        this._copyUrlElement = element;
619
+    }
568 620
 }
569 621
 
570 622
 /**

Laddar…
Avbryt
Spara