diff --git a/.babelrc b/.babelrc index 9805504795..aca0c79068 100644 --- a/.babelrc +++ b/.babelrc @@ -13,7 +13,6 @@ "env": { "development": { "plugins": [ - "react-hot-loader/babel", "@babel/transform-react-display-name", [ "@babel/plugin-proposal-decorators", diff --git a/.eslintignore b/.eslintignore index 1d3ce86a24..f03da7833e 100644 --- a/.eslintignore +++ b/.eslintignore @@ -21,7 +21,7 @@ karma.conf.js nacl-fast.js pdf.worker.js js/cmsSnapshot.js -js/chat/bundle.js +js/chat/bundle*.js js/transfers/meths/firefox-extension.js contrib/Electron/main.js *.sh @@ -29,3 +29,4 @@ contrib/Electron/main.js js/chat/emojidata/emojis.json eslint-local-rules.js Gruntfile.js +webpack.config.js diff --git a/.eslintrc.json b/.eslintrc.json index 1f6ff91c30..9058fb77c4 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -297,6 +297,7 @@ ], "rules": { "complexity": [2, 28], + "es/no-dynamic-import": "off", "es/no-optional-chaining": "off", "local-rules/classes": "off", "no-negated-condition": "off", diff --git a/.jscpd.json b/.jscpd.json index 6417525d58..33e7aacd5c 100644 --- a/.jscpd.json +++ b/.jscpd.json @@ -5,7 +5,7 @@ "maxSize": "340kb", "format": ["javascript", "jsx"], "reporters": ["console", "badge"], - "ignore": ["**/node_modules/**", "**/tests/**", "**/vendor/**", "**/**/bundle.js"], + "ignore": ["**/node_modules/**", "**/tests/**", "**/vendor/**", "**/**/bundle.js", "**/bundle.*.js"], "tokensToSkip": ["comment", "empty", "new_line", "ignore"], "gitignore": true } diff --git a/INSTALL.md b/INSTALL.md index 2fbc1e5180..bbbc2b52aa 100755 --- a/INSTALL.md +++ b/INSTALL.md @@ -35,20 +35,23 @@ Development Environment ----------------------- Since adding React to our toolkit (because of the needs and -requirements added by the MEGAchat), there is a new file -(``js/chat/bundle.js``) to be generated to run the code (it is not -available in the repository). +requirements added by the MEGAchat), there are new files +(``js/chat/bundle.js`` and ``js/chat/bundle.*.js`` chunks) to be +generated to run the code (they are not available in the repository). -This file is generated by ``webpack``, and depending on the +These files are generated by ``webpack``, and depending on the environment you may run the webclient in development in two different methods. A simple script watches for changes in React UI related files, -generates required artifacts automatically (in memory, no files -generated), serves them (via HTTP), and updates the UI with changes -live (including relevant code reloads). This process is very fast, -easy and efficient. You will get this convenience just by running the -following: +generates required artifacts automatically (in memory, with +no files generated) and serves them (via HTTP). + +**Note:** Hot Module Replacement (HMR) is intentionally disabled to ensure +the development environment strictly mirrors the production security model +(`secureboot`). You will need to refresh the page to see your changes. + +You can run the development server with: ``` scripts/dev_server.sh diff --git a/css/chat-bundle.css b/css/chat-bundle.css index 2383510095..7a0f6fbed8 100644 --- a/css/chat-bundle.css +++ b/css/chat-bundle.css @@ -1600,6 +1600,33 @@ body.theme-dark .lhp-container .lhp-filter-tag { margin-top: 24px; } +.meetings-error { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + border: 1px solid tomato; +} +.meetings-error--content { + text-align: center; + color: var(--text-color-medium); +} +.meetings-error--content i { + display: block; + margin: 32px auto; + width: 195px; + height: 65px; +} +.meetings-error--content a { + text-decoration: underline; + color: var(--text-color-high); +} +.meetings-error--details { + margin: 58px 0; + font: var(--text-code1); +} + .meetings-call { position: fixed; top: 0; @@ -1609,9 +1636,6 @@ body.theme-dark .lhp-container .lhp-filter-tag { background: #222; z-index: 120; transition: width 200ms ease-in-out; - /** - * Sidebar controls - */ } .meetings-call.minimized { display: none; @@ -1812,7 +1836,6 @@ body.theme-dark .lhp-container .lhp-filter-tag { grid-template-columns: 1fr 1fr; grid-gap: 8px; width: 100%; - /* TODO: look into `.video-node` re: replicate and unify */ } @media only screen and (max-height: 760px) { .meetings-call .stream .stream-participants-block .participants-container .participants-grid { @@ -1854,6 +1877,9 @@ body.theme-dark .lhp-container .lhp-filter-tag { .meetings-call .stream .stream-participants-block .participants-container .participants-grid .local-stream-node .node-menu-content button span { padding: 0 0 0 10px; } +.meetings-call .stream .stream-participants-block .participants-container .participants-grid { + /* TODO: look into `.video-node` re: replicate and unify */ +} .meetings-call .stream .stream-participants-block .participants-container .participants-grid .video-node { width: 100%; height: 100%; @@ -2346,6 +2372,11 @@ body.theme-dark .lhp-container .lhp-filter-tag { padding: 0; margin-inline-start: 4px; } +.meetings-call { + /** + * Sidebar controls + */ +} .meetings-call .sidebar-controls { display: flex; justify-content: flex-end; @@ -2771,11 +2802,13 @@ body.theme-dark .lhp-container .lhp-filter-tag { .meetings-call .sidebar-wrapper .sidebar .participants-empty { margin: 20px; text-align: center; - /* TODO: move to sprite sheet */ } .meetings-call .sidebar-wrapper .sidebar .participants-empty h3 { margin: 8px 0 0 0; } +.meetings-call .sidebar-wrapper .sidebar .participants-empty { + /* TODO: move to sprite sheet */ +} .meetings-call .sidebar-wrapper .sidebar .participants-empty span.empty-check-icon { display: block; width: 84px; @@ -3895,9 +3928,6 @@ body.theme-dark .start-meeting-preview .preview-meeting { height: 100%; background: var(--surface-grey-1); z-index: 141; - /* --- */ - /* TODO: unify w/ `Join` */ - /* --- */ } .waiting-room--await, .waiting-room--redirect { justify-content: center; @@ -3909,6 +3939,10 @@ body.theme-dark .start-meeting-preview .preview-meeting { .waiting-room--chatlink-landing .waiting-room-head { margin: 80px 0 0; } +.waiting-room { + /* --- */ + /* TODO: unify w/ `Join` */ +} .waiting-room-head { /* [...] */ } @@ -3932,6 +3966,9 @@ body.theme-dark .start-meeting-preview .preview-meeting { margin-bottom: 18px; } } +.waiting-room { + /* --- */ +} .waiting-room .card { /* [...] */ } @@ -4576,8 +4613,6 @@ body.theme-dark .meetings-recurring-navigation .mega-button.recurring-nav-button } .meetings-recurring-content { /* [...] */ - /* --- */ - /* --- */ } .meetings-recurring-content .mega-button.recurring-toggle-button { width: 36px; @@ -4592,6 +4627,9 @@ body.theme-dark .meetings-recurring-navigation .mega-button.recurring-nav-button body.theme-dark .meetings-recurring-content .mega-button.recurring-toggle-button.active { color: black; } +.meetings-recurring-content { + /* --- */ +} .meetings-recurring-content .recurring-field-row { display: flex; align-items: center; @@ -4645,6 +4683,9 @@ body.theme-dark .meetings-recurring-content .mega-button.recurring-toggle-button .meetings-recurring-content .recurring-field-row .recurring-radio-buttons .meetings-datepicker .datepicker-input { margin-top: -8px; } +.meetings-recurring-content { + /* --- */ +} .meetings-recurring-content .meetings-recurring-daily { /* [...] */ } diff --git a/css/chat-bundle.scss b/css/chat-bundle.scss index dd4efb9aed..e8512517c5 100644 --- a/css/chat-bundle.scss +++ b/css/chat-bundle.scss @@ -8,6 +8,7 @@ @use 'meetings/_leftPanel.scss'; @use 'meetings/_inviteParticipantsPanel.scss'; @use 'meetings/_chatOverlay.scss'; +@use 'meetings/_errorBoundary.scss'; // The main `Meetings` in-call container @use 'meetings/_call.scss'; diff --git a/css/meetings/_errorBoundary.scss b/css/meetings/_errorBoundary.scss new file mode 100644 index 0000000000..d42aaa694f --- /dev/null +++ b/css/meetings/_errorBoundary.scss @@ -0,0 +1,30 @@ +.meetings-error { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + border: 1px solid tomato; + + &--content { + text-align: center; + color: var(--text-color-medium); + + i { + display: block; + margin: 32px auto; + width: 195px; + height: 65px; + } + + a { + text-decoration: underline; + color: var(--text-color-high); + } + } + + &--details { + margin: 58px 0; + font: var(--text-code1); + } +} diff --git a/js/chat/README.md b/js/chat/README.md index 8bd1f44dd5..7b2f4e1a63 100644 --- a/js/chat/README.md +++ b/js/chat/README.md @@ -1,10 +1,16 @@ ### Generated files used by the MEGA WebClient chat application. - --- -bundle.js: The libraries and custom JavaScript/JSX files (specified in webpack.config.js) necessary for MEGA chat. +**bundle.js** / **bundle.*.js**: The libraries and custom JavaScript/JSX files necessary for MEGAchat. + +The architecture uses **Lazy Loading** (via React Suspense) to split the application into: +1. `bundle.js`: The lightweight entry point. +2. `bundle.*.js`: Lazy-loaded chunks (e.g., `bundle.call.js`, `bundle.contacts-panel.js`) loaded on-demand. + +Lazy chunk loading is intercepted by `megaChunkLoader` to route requests through `secureboot`'s `M.require()`; this +ensures all chunks undergo strict XHR + SHA-256 hash verification before execution. -> NB: This unobfuscated, unminified file is created with Webpack using: +> NB: These unobfuscated, unminified files are created with Webpack using: > > ```bash > npm update && npm install --production && ./scripts/build.sh diff --git a/js/chat/bundle.call.js b/js/chat/bundle.call.js new file mode 100644 index 0000000000..24ab8adc53 --- /dev/null +++ b/js/chat/bundle.call.js @@ -0,0 +1,7020 @@ +/** @file automatically generated, do not edit it. */ +"use strict"; +(self.webpackChunk_meganz_webclient = self.webpackChunk_meganz_webclient || []).push([[987],{ + + 8402 +(_, EXP_, REQ_) { + +// ESM COMPAT FLAG +REQ_.r(EXP_); + +// EXPORTS +REQ_.d(EXP_, { + "default": () => Call +}); + +// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/extends.js +const esm_extends = REQ_(8168); +// EXTERNAL MODULE: external "React" +const external_React_ = REQ_(1594); +const REaCt = REQ_.n(external_React_); +// EXTERNAL MODULE: ./js/chat/mixins.js +const mixins = REQ_(8264); +// EXTERNAL MODULE: ./js/chat/chatGlobalEventManager.jsx +const chatGlobalEventManager = REQ_(8676); +// EXTERNAL MODULE: ./js/chat/ui/meetings/utils.jsx +const utils = REQ_(3901); +// EXTERNAL MODULE: ./js/chat/ui/contacts.jsx +const ui_contacts = REQ_(8022); +// EXTERNAL MODULE: ./js/ui/utils.jsx +const ui_utils = REQ_(6411); +;// ./js/chat/ui/meetings/videoNode.jsx + + + + + +class VideoNode extends mixins.w9 { + constructor(props, source) { + super(props); + this.domRef = REaCt().createRef(); + this.contRef = REaCt().createRef(); + this.audioLevelRef = REaCt().createRef(); + this.statsHudRef = REaCt().createRef(); + this.raisedHandListener = undefined; + this.state = { + raisedHandPeers: [] + }; + this.source = source; + this.state.raisedHandPeers = this.props.raisedHandPeers || []; + } + componentDidMount() { + let _this$props$didMount, _this$props, _this$domRef; + super.componentDidMount(); + this.source.registerConsumer(this); + (_this$props$didMount = (_this$props = this.props).didMount) == null || _this$props$didMount.call(_this$props, (_this$domRef = this.domRef) == null ? void 0 : _this$domRef.current); + this.requestVideo(true); + this.raisedHandListener = mBroadcaster.addListener('meetings:raisedHand', raisedHandPeers => this.setState({ + raisedHandPeers + }, () => this.safeForceUpdate())); + } + onVisibilityChange(isVisible) { + this.requestVideo(isVisible); + } + componentDidUpdate() { + super.componentDidUpdate(); + if (this.props.didUpdate) { + let _this$domRef2; + this.props.didUpdate((_this$domRef2 = this.domRef) == null ? void 0 : _this$domRef2.current); + } + this.requestVideo(); + } + onAvChange() { + this.safeForceUpdate(); + } + displayVideoElement(video, container) { + this.attachVideoElemHandlers(video); + this.video = video; + container.replaceChildren(video); + } + attachVideoElemHandlers(video) { + if (video._snSetup) { + return; + } + video.autoplay = true; + video.controls = false; + video.muted = true; + video.ondblclick = e => { + const { + onDoubleClick, + toggleFullScreen + } = this.props; + onDoubleClick == null || onDoubleClick(this.source, e); + if (toggleFullScreen && !document.fullscreenElement && this.domRef.current) { + if (typeof toggleFullScreen === 'function') { + toggleFullScreen(this); + } + this.domRef.current.requestFullscreen({ + navigationUI: 'hide' + }); + } + }; + video.onloadeddata = ev => { + if (this.props.onLoadedData) { + this.props.onLoadedData(ev); + } + }; + video._snSetup = true; + } + componentWillUnmount() { + super.componentWillUnmount(); + delete this.video; + this.detachVideoElemHandlers(); + this.source.deregisterConsumer(this); + mBroadcaster.removeListener(this.raisedHandListener); + if (this.props.willUnmount) { + this.props.willUnmount(); + } + } + detachVideoElemHandlers() { + let _this$contRef$current; + const video = (_this$contRef$current = this.contRef.current) == null ? void 0 : _this$contRef$current.firstChild; + if (!video || !video._snSetup) { + return; + } + video.onloadeddata = null; + video.ondblclick = null; + delete video._snSetup; + } + isVideoCropped() { + let _this$video; + return (_this$video = this.video) == null ? void 0 : _this$video.classList.contains("video-crop"); + } + cropVideo() { + let _this$video2; + (_this$video2 = this.video) == null || _this$video2.classList.add("video-crop"); + } + uncropVideo() { + let _this$video3; + (_this$video3 = this.video) == null || _this$video3.classList.remove("video-crop"); + } + displayStats(stats) { + const elem = this.statsHudRef.current; + if (!elem) { + return; + } + elem.textContent = stats ? `${stats} (${this.ownVideo ? "cloned" : "ref"})` : ""; + } + renderVideoDebugMode() { + if (this.source.isFake) { + return null; + } + let className = "video-rtc-stats"; + let title; + if (this.isLocal) { + if (window.sfuClient) { + title = new URL(window.sfuClient.url).host; + } + if (this.props.isSelfOverlay) { + className += " video-rtc-stats-ralign"; + } + } + if (!title) { + title = ""; + } + return JSX_("div", { + ref: this.statsHudRef, + className, + title + }); + } + renderContent() { + const { + source, + contRef + } = this; + if (this.props.isPresenterNode || source.av & Av.Camera) { + return JSX_("div", { + ref: contRef, + className: "video-node-holder video-node-loading" + }); + } + delete this._lastResizeHeight; + return JSX_(ui_contacts.eu, { + contact: M.u[source.userHandle] + }); + } + getStatusIcon(icon, label) { + return JSX_("span", { + className: "simpletip", + "data-simpletip-class": "theme-dark-forced", + "data-simpletipposition": "top", + "data-simpletipoffset": "5", + "data-simpletip": label + }, JSX_("i", { + className: icon + })); + } + renderStatus() { + const { + chatRoom, + isPresenterNode, + minimized + } = this.props; + const { + raisedHandPeers + } = this.state; + const { + source + } = this; + const { + sfuClient + } = chatRoom.call; + const { + userHandle, + isOnHold + } = source; + const $$CONTAINER = ({ + children + }) => JSX_("div", { + className: "video-node-status theme-dark-forced" + }, children); + const name = JSX_("div", { + className: "video-status-name" + }, isPresenterNode ? JSX_(ui_utils.zT, null, l.presenter_nail.replace('%s', M.getNameByHandle(userHandle))) : JSX_(ui_contacts.uA, { + contact: M.u[userHandle], + emoji: true + })); + if (isOnHold) { + return JSX_($$CONTAINER, null, name, this.getStatusIcon('sprite-fm-mono icon-pause', l[23542].replace('%s', M.getNameByHandle(userHandle) || megaChat.plugins.userHelper.SIMPLETIP_USER_LOADER))); + } + return JSX_(REaCt().Fragment, null, !minimized && JSX_("div", { + className: "stream-signifiers" + }, raisedHandPeers && raisedHandPeers.length && raisedHandPeers.includes(userHandle) ? this.getStatusIcon('sprite-fm-uni stream-signifier-icon icon-raise-hand') : null), JSX_($$CONTAINER, null, name, JSX_(AudioLevelIndicator, { + source + }), sfuClient.haveBadNetwork ? this.getStatusIcon('sprite-fm-mono icon-call-offline', l.poor_connection) : null)); + } + render() { + const { + mode, + chatRoom, + simpletip, + className, + children, + onClick + } = this.props; + const { + domRef, + source, + isLocal, + isLocalScreen + } = this; + if (!chatRoom.call) { + return null; + } + const { + call + } = chatRoom; + const isActiveSpeaker = !source.audioMuted && call.speakerCid === source.clientId; + return JSX_("div", { + ref: domRef, + className: ` + video-node + ${onClick ? 'clickable' : ''} + ${className || ''} + ${isLocal && !isLocalScreen ? ' local-stream-mirrored' : ''} + ${simpletip ? 'simpletip' : ''} + ${isActiveSpeaker && mode === utils.g.THUMBNAIL ? 'active-speaker' : ''} + `, + "data-simpletip": simpletip == null ? void 0 : simpletip.label, + "data-simpletipposition": simpletip == null ? void 0 : simpletip.position, + "data-simpletipoffset": simpletip == null ? void 0 : simpletip.offset, + "data-simpletip-class": simpletip == null ? void 0 : simpletip.className, + onClick: evt => onClick == null ? void 0 : onClick(source, evt) + }, source && JSX_(REaCt().Fragment, null, children || null, JSX_("div", { + className: "video-node-content" + }, CallManager2.Call.VIDEO_DEBUG_MODE ? this.renderVideoDebugMode() : null, this.renderContent(), this.renderStatus()))); + } +} +class DynVideo extends VideoNode { + onAvChange() { + this._lastResizeHeight = null; + super.onAvChange(); + } + dynRequestVideo() { + const { + source, + domRef + } = this; + if (source.isFake || source.isDestroyed) { + return; + } + if (source.isStreaming() && this.isMounted()) { + const node = domRef == null ? void 0 : domRef.current; + this.dynRequestVideoBySize(node.offsetHeight); + } else { + this.dynRequestVideoBySize(0); + this.displayStats(null); + } + } + dynRequestVideoQuality(quality) { + this.requestedQ = quality && CallManager2.FORCE_LOWQ ? 1 : quality; + if (!this.source.dynUpdateVideoQuality()) { + this.dynUpdateVideoElem(); + } + } + dynRequestVideoBySize(h) { + if (h === 0) { + this._lastResizeHeight = 0; + this.dynRequestVideoQuality(CallManager2.VIDEO_QUALITY.NO_VIDEO); + return; + } + if (this.contRef.current) { + if (this._lastResizeHeight === h) { + return; + } + this._lastResizeHeight = h; + } else { + this._lastResizeHeight = null; + } + let newQ; + if (h > 360) { + newQ = CallManager2.VIDEO_QUALITY.HIGH; + } else if (h > 180) { + newQ = CallManager2.VIDEO_QUALITY.MEDIUM; + } else if (h > 90 || this.noThumb) { + newQ = CallManager2.VIDEO_QUALITY.LOW; + } else { + newQ = CallManager2.VIDEO_QUALITY.THUMB; + } + this.dynRequestVideoQuality(newQ); + } + dynUpdateVideoElem() { + let _this$source$hiResPla; + const vidCont = this.contRef.current; + if (!this.isMounted() || !vidCont) { + return; + } + const player = this.noThumb ? (_this$source$hiResPla = this.source.hiResPlayer) == null || (_this$source$hiResPla = _this$source$hiResPla.gui) == null ? void 0 : _this$source$hiResPla.video : this.source.player; + if (!player) { + vidCont.replaceChildren(); + return; + } + this.dynSetVideoSource(player, vidCont); + } +} +class DynVideoDirect extends DynVideo { + constructor(props, source) { + super(props, source); + this.isDirect = true; + this.requestVideo = this.dynRequestVideo; + } + dynSetVideoSource(srcPlayer, vidCont) { + if (vidCont.firstChild !== srcPlayer) { + this.displayVideoElement(srcPlayer, vidCont); + } + if (srcPlayer.paused) { + srcPlayer.play().catch(nop); + } + } +} +class PeerVideoHiRes extends DynVideoDirect { + constructor(props) { + super(props, props.source); + } +} +class DynVideoCloned extends DynVideo { + constructor(props, source) { + super(props, source); + this.ownVideo = CallManager2.createVideoElement(); + } + dynSetVideoSource(srcPlayer, vidCont) { + const cloned = this.ownVideo; + const currVideo = vidCont.firstChild; + if (!currVideo) { + this.displayVideoElement(cloned, vidCont); + } else { + assert(currVideo === cloned); + } + if (cloned.paused || cloned.srcObject !== srcPlayer.srcObject) { + cloned.srcObject = srcPlayer.srcObject; + Promise.resolve(cloned.play()).catch(nop); + } + } +} +class PeerVideoThumb extends DynVideoCloned { + constructor(props) { + super(props, props.source); + this.requestVideo = this.dynRequestVideo; + } +} +class PeerVideoThumbFixed extends VideoNode { + constructor(props) { + super(props, props.source); + assert(props.source.hasScreenAndCam); + this.ownVideo = CallManager2.createVideoElement(); + if (CallManager2.Call.VIDEO_DEBUG_MODE) { + this.onRxStats = this._onRxStats; + } + } + addVideo() { + assert(this.source.hasScreenAndCam); + const vidCont = this.contRef.current; + assert(vidCont); + if (vidCont.firstChild !== this.ownVideo) { + this.displayVideoElement(this.ownVideo, vidCont); + } + } + delVideo() { + SfuClient.playerStop(this.ownVideo); + const vidCont = this.contRef.current; + if (!vidCont) { + return; + } + vidCont.replaceChildren(); + } + requestVideo(forceVisible) { + if (!this.isComponentVisible() && !forceVisible) { + return; + } + if (this.player) { + this.playVideo(); + } else { + this.addVideo(); + this.player = this.source.sfuPeer.getThumbVideo(() => { + return this; + }); + } + } + playVideo() { + let _this$player$slot; + const track = (_this$player$slot = this.player.slot) == null ? void 0 : _this$player$slot.inTrack; + if (!track) { + return; + } + SfuClient.playerPlay(this.ownVideo, track, true); + } + attachToTrack(track) { + if (!this.source.hasScreenAndCam) { + return; + } + SfuClient.playerPlay(this.ownVideo, track); + } + detachFromTrack() { + this.delVideo(); + } + onPlayerDestroy() { + delete this.player; + } + componentWillUnmount() { + if (this.player) { + this.player.destroy(); + } + super.componentWillUnmount(); + } + _onRxStats(track, info, raw) { + if (this.player) { + this.displayStats(CallManager2.Call.rxStatsToText(track, info, raw)); + } + } +} +class PeerVideoHiResCloned extends DynVideoCloned { + constructor(props) { + super(props, props.source); + this.noThumb = true; + this.requestVideo = this.dynRequestVideo; + } +} +class LocalVideoHiResCloned extends VideoNode { + constructor(props) { + super(props, props.chatRoom.call.getLocalStream()); + this.isLocal = true; + this.ownVideo = CallManager2.createVideoElement(); + } + get isLocalScreen() { + return this.source.av & Av.Screen; + } + requestVideo(forceVisible) { + if (d > 1 && forceVisible) { + console.debug('ignoring forceVisible'); + } + const vidCont = this.contRef.current; + if (!vidCont) { + return; + } + const track = this.source.sfuClient.localScreenTrack(); + if (!track) { + vidCont.replaceChildren(); + } else { + if (vidCont.firstChild !== this.ownVideo) { + this.displayVideoElement(this.ownVideo, vidCont); + } + SfuClient.playerPlay(this.ownVideo, track, true); + } + } +} +class LocalVideoHiRes extends DynVideoDirect { + constructor(props) { + super(props, props.chatRoom.call.getLocalStream()); + this.isLocal = true; + } + get isLocalScreen() { + return this.source.av & Av.Screen; + } +} +class LocalVideoThumb extends VideoNode { + constructor(props) { + const source = props.chatRoom.call.getLocalStream(); + super(props, source); + this.isLocal = true; + this.isLocalScreen = source.av & Av.Screen && !(source.av & Av.Camera); + this.sfuClient = props.chatRoom.call.sfuClient; + this.ownVideo = CallManager2.createVideoElement(); + } + requestVideo() { + const vidCont = this.contRef.current; + if (!vidCont) { + return; + } + const currVideo = vidCont.firstChild; + const track = this.isLocalScreen ? this.sfuClient.localScreenTrack() : this.sfuClient.localCameraTrack(); + if (!track) { + if (currVideo) { + vidCont.replaceChildren(); + } + } else { + if (!currVideo) { + this.displayVideoElement(this.ownVideo, vidCont); + } else { + assert(currVideo === this.ownVideo); + } + SfuClient.playerPlay(this.ownVideo, track, true); + } + } + onAvChange() { + const av = this.sfuClient.availAv; + this.isLocalScreen = av & Av.Screen && !(av & Av.Camera); + super.onAvChange(); + } +} +class AudioLevelIndicator extends REaCt().Component { + constructor(props) { + super(props); + this.source = props.source; + this.indicatorRef = REaCt().createRef(); + this.updateAudioLevel = this.updateAudioLevel.bind(this); + } + componentDidMount() { + this.source.registerVuLevelConsumer(this); + } + componentWillUnmount() { + this.source.unregisterVuLevelConsumer(this); + } + updateAudioLevel(level) { + const levelInd = this.indicatorRef.current; + if (!levelInd) { + return; + } + level = Math.round(level * 400); + if (level > 90) { + level = 90; + } + levelInd.style.height = `${level + 10}%`; + } + render() { + const { + audioMuted + } = this.source; + return JSX_("span", { + className: "simpletip", + "data-simpletip-class": "theme-dark-forced", + "data-simpletipposition": "top", + "data-simpletipoffset": "5", + "data-simpletip": audioMuted ? l.muted : '' + }, JSX_("i", { + className: ` + sprite-fm-mono + ${audioMuted ? 'icon-mic-off-thin-outline inactive' : 'icon-mic-thin-outline speaker-indicator'} + ` + }, audioMuted ? null : JSX_("div", { + ref: this.indicatorRef, + className: "mic-fill" + }))); + } +} +// EXTERNAL MODULE: ./js/chat/ui/meetings/button.jsx +const meetings_button = REQ_(6740); +// EXTERNAL MODULE: ./js/chat/ui/inviteParticipantsPanel.jsx +const inviteParticipantsPanel = REQ_(8956); +;// ./js/chat/ui/meetings/participantsNotice.jsx + + + + + + +class ParticipantsNotice extends mixins.w9 { + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.renderUserAlone = () => JSX_("div", { + className: ` + ${ParticipantsNotice.NAMESPACE} + theme-dark-forced + user-alone + ` + }, this.props.stayOnEnd ? JSX_("div", { + className: `${ParticipantsNotice.NAMESPACE}-heading` + }, JSX_("h1", null, this.props.everHadPeers ? l.only_one_here : l.waiting_for_others)) : JSX_("div", { + className: `${ParticipantsNotice.NAMESPACE}-content user-alone` + }, JSX_("h3", null, l.only_one_here), JSX_("p", { + className: "theme-dark-forced" + }, JSX_(ui_utils.P9, null, l.empty_call_dlg_text.replace('%s', '2'))), JSX_("div", { + className: "notice-footer" + }, JSX_(meetings_button.A, { + className: "mega-button large stay-on-call", + onClick: this.props.onStayConfirm + }, JSX_("span", null, l.empty_call_stay_button)), JSX_(meetings_button.A, { + className: "mega-button positive large stay-on-call", + onClick: this.props.onCallEnd + }, JSX_("span", null, l.empty_call_dlg_end))))); + this.renderUserWaiting = () => { + const { + chatRoom, + onInviteToggle + } = this.props; + return JSX_("div", { + className: ` + ${ParticipantsNotice.NAMESPACE} + ${chatRoom.isMeeting ? '' : 'user-alone'} + theme-dark-forced + ` + }, JSX_("div", { + className: `${ParticipantsNotice.NAMESPACE}-heading` + }, chatRoom.type === 'private' ? JSX_("h1", null, JSX_(ui_utils.zT, null, l.waiting_for_peer.replace('%NAME', chatRoom.getRoomTitle()))) : JSX_("h1", null, l.waiting_for_others)), chatRoom.isMeeting && chatRoom.publicLink && JSX_("div", { + className: `${ParticipantsNotice.NAMESPACE}-content-invite` + }, JSX_(inviteParticipantsPanel.Q, { + chatRoom, + disableLinkToggle: true, + onAddParticipants: () => { + this.setState({ + inviteDialog: false + }, () => onInviteToggle()); + } + }))); + }; + this.av = this.props.call.sfuClient.availAv; + } + specShouldComponentUpdate(newProps) { + const { + stayOnEnd, + hasLeft, + isOnHold, + call + } = this.props; + const currAv = this.av; + this.av = call.sfuClient.availAv; + return newProps.stayOnEnd !== stayOnEnd || newProps.hasLeft !== hasLeft || newProps.isOnHold !== isOnHold || this.av !== currAv; + } + render() { + const { + call, + hasLeft, + streamContainer, + chatRoom + } = this.props; + if (call.isDestroyed) { + return null; + } + return JSX_("div", { + ref: this.domRef, + className: `${ParticipantsNotice.NAMESPACE}-container` + }, call.isSharingScreen() ? null : JSX_(LocalVideoHiRes, { + className: "local-stream-mirrored", + chatRoom, + source: call.getLocalStream() + }), streamContainer(hasLeft ? this.renderUserAlone() : this.renderUserWaiting())); + } +} +ParticipantsNotice.NAMESPACE = 'participants-notice'; +// EXTERNAL MODULE: ./js/chat/ui/chatToaster.jsx +const chatToaster = REQ_(8491); +;// ./js/chat/ui/meetings/participantsBlock.jsx + + + + + + +const MAX_STREAMS_PER_PAGE = 10; +const SIMPLE_TIP = { + position: 'top', + offset: 5, + className: 'theme-dark-forced' +}; +class ParticipantsBlock extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + this.nodeMenuRef = REaCt().createRef(); + this.dupNodeMenuRef = REaCt().createRef(); + this.state = { + page: 0 + }; + this.movePage = direction => this.setState(state => ({ + page: direction === utils.Bq.NEXT ? state.page + 1 : state.page - 1 + })); + this.renderLocalNode = isPresenterNode => { + const { + call, + peers, + mode, + raisedHandPeers, + chatRoom, + forcedLocal, + presenterThumbSelected, + onSeparate, + onSpeakerChange, + onModeChange + } = this.props; + const localStream = call.getLocalStream(); + if (localStream) { + const IS_SPEAKER_VIEW = mode === utils.g.MAIN && forcedLocal; + const VideoClass = isPresenterNode ? LocalVideoHiResCloned : LocalVideoThumb; + let isActive = false; + if (isPresenterNode) { + isActive = forcedLocal && !presenterThumbSelected; + } else if (call.pinnedCid === 0 || forcedLocal) { + if (presenterThumbSelected) { + isActive = !isPresenterNode; + } else if (localStream.hasScreen) { + isActive = isPresenterNode; + } else { + isActive = true; + } + } + return JSX_(VideoClass, { + key: `${u_handle}${isPresenterNode ? '_block' : ''}`, + className: ` + local-stream-node + ${call.isSharingScreen() ? '' : 'local-stream-mirrored'} + ${isActive ? 'active' : ''} + ${call.speakerCid === 0 ? 'active-speaker' : ''} + `, + simpletip: { + ...SIMPLE_TIP, + label: l[8885] + }, + mode, + raisedHandPeers, + chatRoom, + source: localStream, + localAudioMuted: !(call.av & SfuClient.Av.Audio), + isPresenterNode, + onClick: (source, ev) => { + const nodeMenuRef = isPresenterNode ? this.nodeMenuRef && this.nodeMenuRef.current : this.dupNodeMenuRef && this.dupNodeMenuRef.current; + if (nodeMenuRef && nodeMenuRef.contains(ev.target)) { + ev.preventDefault(); + ev.stopPropagation(); + return; + } + return onSpeakerChange(localStream, !isPresenterNode); + } + }, (peers == null ? void 0 : peers.length) && JSX_("div", { + ref: isPresenterNode ? this.nodeMenuRef : this.dupNodeMenuRef, + className: "node-menu theme-dark-forced" + }, JSX_("div", { + className: "node-menu-toggle" + }, JSX_("i", { + className: "sprite-fm-mono icon-more-horizontal-thin-outline" + })), JSX_("div", { + className: "node-menu-content" + }, JSX_("ul", null, JSX_("li", null, JSX_(meetings_button.A, { + icon: ` + sprite-fm-mono + ${IS_SPEAKER_VIEW ? 'grid-9' : 'grid-main'} + `, + onClick: () => { + if (IS_SPEAKER_VIEW) { + return onModeChange(utils.g.THUMBNAIL); + } + return onSpeakerChange(localStream); + } + }, JSX_("span", null, IS_SPEAKER_VIEW ? l.switch_to_thumb_view : l.display_in_main_view))), JSX_("li", null, JSX_(meetings_button.A, { + icon: "sprite-fm-mono grid-separate", + onClick: onSeparate + }, JSX_("span", null, l.separate_from_grid_button))))))); + } + return null; + }; + this.onScroll = (chunks, evt) => { + const { + page + } = this.state; + if (evt.deltaY < 0) { + if (page > 0) { + this.movePage(utils.Bq.PREV); + } + } else if (evt.deltaY > 0) { + if (page < Object.values(chunks).length - 1) { + this.movePage(utils.Bq.NEXT); + } + } + }; + } + shouldComponentUpdate() { + const { + peers + } = this.props; + return peers && peers.length; + } + render() { + const { + call, + mode, + peers, + floatDetached, + chatRoom, + raisedHandPeers, + presenterThumbSelected, + onSpeakerChange + } = this.props; + if (peers && peers.length) { + const { + screen, + video, + rest + } = filterAndSplitSources(peers, call); + const sources = [...screen, ...video, ...rest]; + const $$PEER = (peer, i) => { + const { + clientId, + userHandle, + hasScreenAndCam, + hasScreen, + isLocal + } = peer; + if (screen.length && (screen[0].clientId === clientId || screen[0].isLocal && isLocal)) { + screen.shift(); + } + if (!(peer instanceof CallManager2.Peer)) { + const isPresenterNode = screen.length && screen[0].isLocal; + if (floatDetached && !isPresenterNode) { + return; + } + return this.renderLocalNode(!floatDetached && isPresenterNode); + } + const presenterCid = screen.length && screen[0].clientId === clientId; + let PeerClass; + if (hasScreenAndCam) { + PeerClass = presenterCid ? PeerVideoHiResCloned : PeerVideoThumbFixed; + } else { + PeerClass = PeerVideoThumb; + } + assert(!presenterCid || hasScreen); + const isActiveSpeaker = !peer.audioMuted && call.speakerCid === peer.clientId; + let isActive = false; + if (call.pinnedCid === clientId) { + if (presenterThumbSelected) { + isActive = !presenterCid; + } else if (hasScreen) { + isActive = presenterCid; + } else { + isActive = true; + } + } + const name = M.getNameByHandle(userHandle); + let label = name; + if (presenterCid) { + label = name ? l.presenter_nail.replace('%s', name) : megaChat.plugins.userHelper.SIMPLETIP_USER_LOADER; + } else { + label = name || megaChat.plugins.userHelper.SIMPLETIP_USER_LOADER; + } + return JSX_(PeerClass, { + key: `${userHandle}-${i}-${clientId}`, + className: ` + video-crop + ${isActive ? 'active' : ''} + ${isActiveSpeaker ? 'active-speaker' : ''} + `, + simpletip: { + ...SIMPLE_TIP, + label + }, + raisedHandPeers, + mode, + chatRoom, + source: peer, + isPresenterNode: !!presenterCid, + onSpeakerChange: node => onSpeakerChange(node, !presenterCid), + onClick: node => onSpeakerChange(node, !presenterCid) + }); + }; + if (sources.length <= (floatDetached ? MAX_STREAMS_PER_PAGE : 9)) { + return JSX_("div", { + ref: this.domRef, + className: "stream-participants-block theme-dark-forced" + }, JSX_("div", { + className: "participants-container" + }, JSX_("div", { + className: ` + participants-grid + ${floatDetached && sources.length === 1 || sources.length === 0 ? 'single-column' : ''} + ` + }, sources.map((p, i) => $$PEER(p, i))))); + } + const { + page + } = this.state; + const chunks = chunkNodes(sources, MAX_STREAMS_PER_PAGE); + return JSX_("div", { + ref: this.domRef, + className: "carousel" + }, JSX_("div", { + className: "carousel-container", + onWheel: evt => this.onScroll(chunks, evt) + }, JSX_("div", { + className: "stream-participants-block theme-dark-forced" + }, JSX_("div", { + className: "participants-container" + }, Object.values(chunks).map((chunk, i) => { + const { + id, + nodes + } = chunk; + return JSX_("div", { + key: id, + className: ` + carousel-page + ${i === page ? 'active' : ''} + ` + }, page === 0 ? null : JSX_("button", { + className: "carousel-control carousel-button-prev theme-dark-forced", + onClick: () => this.movePage(utils.Bq.PREV) + }, JSX_("i", { + className: "sprite-fm-mono icon-arrow-up" + })), JSX_("div", { + className: ` + participants-grid + ${nodes.length === 1 ? 'single-column' : ''} + ` + }, nodes.map((peer, j) => $$PEER(peer, j + i * MAX_STREAMS_PER_PAGE))), page >= Object.values(chunks).length - 1 ? null : JSX_("button", { + className: "carousel-control carousel-button-next theme-dark-forced", + onClick: () => this.movePage(utils.Bq.NEXT) + }, JSX_("i", { + className: "sprite-fm-mono icon-arrow-down" + }))); + }))))); + } + return null; + } +} +;// ./js/chat/ui/meetings/videoNodeMenu.jsx + + + + + +const Privilege = ({ + chatRoom, + stream +}) => { + const { + call, + userHandle + } = stream || {}; + if (call && call.isPublic) { + const { + OPERATOR, + FULL + } = ChatRoom.MembersSet.PRIVILEGE_STATE; + const currentUserModerator = chatRoom.members[u_handle] === OPERATOR; + const targetUserModerator = chatRoom.members[userHandle] === OPERATOR; + return currentUserModerator && JSX_(meetings_button.A, { + targetUserModerator, + icon: "sprite-fm-mono icon-admin-outline", + onClick: () => { + ['alterUserPrivilege', 'onCallPrivilegeChange'].map(event => chatRoom.trigger(event, [userHandle, targetUserModerator ? FULL : OPERATOR])); + } + }, JSX_("span", null, targetUserModerator ? l.remove_moderator : l.make_moderator)); + } + return null; +}; +const Contact = ({ + stream, + ephemeralAccounts, + onCallMinimize +}) => { + const { + userHandle + } = stream; + const IS_GUEST = (0,utils.P)() || ephemeralAccounts && ephemeralAccounts.includes(userHandle); + const HAS_RELATIONSHIP = M.u[userHandle].c === 1; + if (HAS_RELATIONSHIP) { + return JSX_(meetings_button.A, { + icon: "sprite-fm-mono icon-chat", + onClick: () => { + onCallMinimize(); + loadSubPage(`fm/chat/p/${userHandle}`); + } + }, JSX_("span", null, l[7997])); + } + return JSX_(meetings_button.A, { + className: IS_GUEST ? 'disabled' : '', + icon: "sprite-fm-mono icon-add", + onClick: () => { + return IS_GUEST ? false : M.syncContactEmail(userHandle, true).then(email => { + const OPC = Object.values(M.opc); + if (OPC && OPC.length && OPC.some(opc => opc.m === email)) { + return msgDialog('warningb', '', l[17545]); + } + msgDialog('info', l[150], l[5898]); + M.inviteContact(M.u[u_handle].m, email); + }).catch(() => mBroadcaster.sendMessage('meetings:ephemeralAdd', userHandle)); + } + }, JSX_("span", null, l[24581])); +}; +const Pin = ({ + stream, + mode, + onSpeakerChange, + onModeChange +}) => { + return JSX_(meetings_button.A, { + icon: "sprite-fm-mono grid-main", + onClick: () => mode === utils.g.THUMBNAIL ? onSpeakerChange == null ? void 0 : onSpeakerChange(stream) : onModeChange == null ? void 0 : onModeChange(utils.g.THUMBNAIL) + }, JSX_("span", null, mode === utils.g.THUMBNAIL ? l.display_in_main_view : l.switch_to_thumb_view)); +}; +class VideoNodeMenu extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + this.ToggleCrop = ({ + videoNodeRef + }) => { + const videoNode = videoNodeRef == null ? void 0 : videoNodeRef.current; + if (!videoNode) { + return null; + } + return videoNode.isVideoCropped() ? JSX_(meetings_button.A, { + icon: "sprite-fm-mono grid-main", + onClick: () => { + videoNode.uncropVideo(); + this.forceUpdate(); + } + }, JSX_("span", null, "Uncrop video")) : JSX_(meetings_button.A, { + icon: "sprite-fm-mono grid-main", + onClick: () => { + videoNode.cropVideo(); + this.forceUpdate(); + } + }, JSX_("span", null, "Crop video")); + }; + } + render() { + const { + NAMESPACE + } = VideoNodeMenu; + const { + stream, + isPresenterNode, + mode, + onSpeakerChange, + onModeChange + } = this.props; + const { + userHandle, + clientId + } = stream; + if (isPresenterNode) { + return JSX_("div", { + ref: this.domRef, + className: ` + ${NAMESPACE} + theme-dark-forced + ${mode === utils.g.THUMBNAIL ? '' : 'presenter'} + ` + }, JSX_("div", { + className: `${NAMESPACE}-toggle` + }, JSX_("i", { + className: `sprite-fm-mono call-node-pin icon-pin${mode === utils.g.MAIN ? '-off' : ''}`, + onClick: () => mode === utils.g.THUMBNAIL ? onSpeakerChange == null ? void 0 : onSpeakerChange(stream) : onModeChange == null ? void 0 : onModeChange(utils.g.THUMBNAIL) + }))); + } + if (userHandle !== u_handle) { + const $$CONTROLS = { + Contact, + Pin, + Privilege + }; + return JSX_("div", { + ref: this.domRef, + className: ` + ${NAMESPACE} + theme-dark-forced + ` + }, JSX_("div", { + className: `${NAMESPACE}-toggle` + }, JSX_("i", { + className: "sprite-fm-mono icon-more-horizontal-thin-outline" + })), JSX_("div", { + className: `${NAMESPACE}-content` + }, JSX_("ul", null, Object.values($$CONTROLS).map(($$CONTROL, i) => JSX_("li", { + key: `${Object.keys($$CONTROLS)[i]}-${clientId}-${userHandle}` + }, JSX_($$CONTROL, this.props)))))); + } + return null; + } +} +VideoNodeMenu.NAMESPACE = 'node-menu'; +// EXTERNAL MODULE: ./js/ui/modalDialogs.jsx + 1 modules +const modalDialogs = REQ_(8120); +;// ./js/chat/ui/meetings/modeSwitch.jsx + + + + +class ModeSwitch extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + this.state = { + expanded: false, + settings: false + }; + this.handleMousedown = ({ + target + }) => { + if (this.state.expanded || this.state.settings) { + let _this$domRef; + return (_this$domRef = this.domRef) != null && (_this$domRef = _this$domRef.current) != null && _this$domRef.contains(target) ? null : this.doClose(); + } + }; + this.handleKeydown = ({ + keyCode + }) => keyCode && keyCode === 27 && this.doClose(); + this.doClose = () => this.isMounted() && this.setState({ + expanded: false, + settings: false + }, () => this.props.setActiveElement(this.state.expanded)); + this.doToggle = () => this.isMounted() && this.setState(state => ({ + expanded: !state.expanded + }), () => this.props.setActiveElement(this.state.expanded || this.state.settings)); + this.setStreamsPerPage = streamsPerPage => { + if (streamsPerPage) { + let _this$props$onStreams, _this$props; + (_this$props$onStreams = (_this$props = this.props).onStreamsPerPageChange) == null || _this$props$onStreams.call(_this$props, streamsPerPage); + this.doClose(); + } + }; + this.getModeIcon = mode => { + switch (mode) { + case utils.g.THUMBNAIL: + return 'grid-9'; + case utils.g.MAIN: + return 'grid-main'; + default: + return null; + } + }; + this.Toggle = () => { + const { + mode + } = this.props; + return JSX_("div", { + className: `${ModeSwitch.BASE_CLASS}-toggle`, + onClick: this.doToggle + }, JSX_(meetings_button.A, null, JSX_("i", { + className: `sprite-fm-mono ${this.getModeIcon(mode)}` + }), mode === utils.g.THUMBNAIL && JSX_("div", null, l.thumbnail_view), mode === utils.g.MAIN && JSX_("div", null, l.main_view)), JSX_("i", { + className: "sprite-fm-mono icon-arrow-down" + })); + }; + this.Option = ({ + label, + mode + }) => { + return JSX_("div", { + className: ` + ${ModeSwitch.BASE_CLASS}-option + ${mode === this.props.mode ? 'active' : ''} + `, + onClick: () => { + this.doToggle(); + this.props.onModeChange(mode); + } + }, JSX_(meetings_button.A, null, JSX_("i", { + className: `sprite-fm-mono ${this.getModeIcon(mode)}` + }), JSX_("div", null, label))); + }; + this.Settings = () => { + const { + streamsPerPage + } = this.props; + return JSX_("div", { + className: `${ModeSwitch.BASE_CLASS}-settings` + }, JSX_("div", { + className: "settings-wrapper" + }, JSX_("strong", null, l.layout_settings_heading), JSX_("span", null, l.layout_settings_info), JSX_("div", { + className: "recurring-radio-buttons" + }, JSX_("div", { + className: "recurring-label-wrap" + }, JSX_("div", { + className: ` + uiTheme + ${streamsPerPage === utils.gh.MIN ? 'radioOn' : 'radioOff'} + ` + }, JSX_("input", { + type: "radio", + name: "9", + onClick: () => this.setStreamsPerPage(utils.gh.MIN) + })), JSX_("div", { + className: "radio-txt" + }, JSX_("span", { + className: "recurring-radio-label", + onClick: () => this.setStreamsPerPage(utils.gh.MIN) + }, "9"))), JSX_("div", { + className: "recurring-label-wrap" + }, JSX_("div", { + className: ` + uiTheme + ${streamsPerPage === utils.gh.MED ? 'radioOn' : 'radioOff'} + ` + }, JSX_("input", { + type: "radio", + name: "21", + onClick: () => { + this.setStreamsPerPage(utils.gh.MED); + } + })), JSX_("div", { + className: "radio-txt" + }, JSX_("span", { + className: "recurring-radio-label", + onClick: () => this.setStreamsPerPage(utils.gh.MED) + }, "21"))), JSX_("div", { + className: "recurring-label-wrap" + }, JSX_("div", { + className: ` + uiTheme + ${streamsPerPage === utils.gh.MAX ? 'radioOn' : 'radioOff'} + ` + }, JSX_("input", { + type: "radio", + name: "49", + onClick: () => { + this.setStreamsPerPage(utils.gh.MAX); + } + })), JSX_("div", { + className: "radio-txt" + }, JSX_("span", { + className: "recurring-radio-label", + onClick: () => this.setStreamsPerPage(utils.gh.MAX) + }, "49")))), JSX_("small", null, l.layout_settings_warning)), JSX_("div", { + className: "settings-close" + }, JSX_("i", { + className: "sprite-fm-mono icon-dialog-close", + onClick: this.doClose + }))); + }; + } + componentWillUnmount() { + super.componentWillUnmount(); + document.removeEventListener('mousedown', this.handleMousedown); + document.removeEventListener('keydown', this.handleKeydown); + } + componentDidMount() { + super.componentDidMount(); + document.addEventListener('mousedown', this.handleMousedown); + document.addEventListener('keydown', this.handleKeydown); + } + render() { + const { + Toggle, + Option, + Settings, + domRef, + state, + doToggle + } = this; + return JSX_("div", { + ref: domRef, + className: ModeSwitch.BASE_CLASS + }, JSX_(Toggle, null), JSX_("div", { + className: ` + ${ModeSwitch.BASE_CLASS}-menu + ${state.expanded ? 'expanded' : ''} + ` + }, JSX_(Option, { + label: l.main_view, + mode: utils.g.MAIN + }), JSX_(Option, { + label: l.thumbnail_view, + mode: utils.g.THUMBNAIL + }), JSX_("div", { + className: `${ModeSwitch.BASE_CLASS}-option`, + onClick: () => this.setState({ + settings: true + }, doToggle) + }, JSX_(meetings_button.A, null, JSX_("i", { + className: "sprite-fm-mono icon-settings" + }), JSX_("div", null, l.layout_settings_button)))), state.settings && JSX_(Settings, null)); + } +} +ModeSwitch.NAMESPACE = 'modeSwitch'; +ModeSwitch.BASE_CLASS = 'mode'; +;// ./js/chat/ui/meetings/streamHead.jsx + + + + + + + + + +class StreamHead extends mixins.w9 { + constructor(...args) { + super(...args); + this.delayProcID = null; + this.domRef = REaCt().createRef(); + this.durationRef = REaCt().createRef(); + this.dialogRef = REaCt().createRef(); + this.topicRef = REaCt().createRef(); + this.interval = undefined; + this.state = { + dialog: false, + duration: undefined, + banner: false, + modeSwitch: false + }; + this.updateDurationDOM = () => { + if (this.durationRef) { + this.durationRef.current.innerText = this.durationString; + } + }; + this.closeTooltips = () => { + for (const node of this.domRef.current.querySelectorAll('.simpletip')) { + node.dispatchEvent(StreamHead.EVENTS.SIMPLETIP); + } + }; + this.toggleFullscreen = () => this.fullscreen ? document.exitFullscreen() : document.documentElement.requestFullscreen(); + this.toggleBanner = callback => this.setState(state => ({ + banner: !state.banner + }), () => callback && callback()); + this.handleDialogClose = ({ + target + }) => { + if (this.state.dialog) { + let _targetDialog$domRef; + const { + topicRef, + dialogRef, + delayProcID + } = this; + const topicElement = topicRef && topicRef.current; + const targetDialog = dialogRef && dialogRef.current && dialogRef.current; + const dialogElement = (_targetDialog$domRef = targetDialog.domRef) == null ? void 0 : _targetDialog$domRef.current; + if (topicElement.contains(target)) { + return; + } + return (target.classList.contains('icon-dialog-close') || !dialogElement.contains(target)) && this.setState({ + dialog: false + }, () => delayProcID && delay.cancel(delayProcID)); + } + }; + this.getModerators = () => { + let _this$props$chatRoom; + const members = (_this$props$chatRoom = this.props.chatRoom) == null ? void 0 : _this$props$chatRoom.members; + if (members) { + const moderators = []; + for (const [handle, role] of Object.entries(members)) { + if (role === ChatRoom.MembersSet.PRIVILEGE_STATE.OPERATOR) { + moderators.push(M.getNameByHandle(handle)); + } + } + return mega.utils.trans.listToString(moderators, mega.icu.format(l.meeting_moderators, moderators.length)); + } + }; + this.Dialog = () => { + const link = `${getBaseUrl()}/${this.props.chatRoom.publicLink}`; + const mods = this.getModerators(); + return JSX_(modalDialogs.A.ModalDialog, (0,esm_extends.A)({ + ref: this.dialogRef + }, this.state, { + mods, + name: "meeting-info-dialog", + title: l[18132], + className: "group-chat-link dialog-template-main theme-dark-forced in-call-info", + hideOverlay: true + }), JSX_("section", { + className: "content" + }, JSX_("div", { + className: "content-block" + }, JSX_(ui_utils.zT, { + className: "info" + }, mods), JSX_("div", { + className: "info" + }, l.copy_and_share), JSX_("div", { + className: "link-input-container" + }, JSX_("div", { + className: "mega-input with-icon box-style" + }, JSX_("i", { + className: "sprite-fm-mono icon-link" + }), JSX_("input", { + type: "text", + className: "megaInputs", + readOnly: true, + value: link + })), JSX_(meetings_button.A, { + className: "mega-button positive copy-to-clipboard", + onClick: () => { + if (copyToClipboard(link)) { + this.toggleBanner(() => { + this.delayProcID = delay(`${StreamHead.NAMESPACE}-banner`, this.toggleBanner, 10000); + }); + } + } + }, JSX_("span", null, l[63]))), this.state.banner && JSX_("div", { + className: "banner-copy-success" + }, l[7654]))), JSX_("footer", null, JSX_("div", { + className: "footer-container" + }))); + }; + this.Pagination = () => { + const { + mode, + peers, + page, + streamsPerPage, + floatDetached, + chunksLength, + call, + onMovePage + } = this.props; + if (mode !== utils.g.THUMBNAIL || !peers) { + return null; + } + const { + screen, + video, + rest + } = filterAndSplitSources(peers, call); + if (screen.length + video.length + rest.length > (floatDetached ? streamsPerPage + 1 : streamsPerPage)) { + return JSX_("div", { + className: `${StreamHead.NAMESPACE}-pagination` + }, JSX_(meetings_button.A, { + className: ` + carousel-button-prev + theme-dark-forced + ${page !== 0 ? '' : 'disabled'} + `, + icon: "sprite-fm-mono icon-arrow-left", + onClick: () => page !== 0 && onMovePage(utils.Bq.PREV) + }), JSX_("div", null, page + 1, "/", chunksLength), JSX_(meetings_button.A, { + className: ` + carousel-button-next + theme-dark-forced + ${page < chunksLength - 1 ? '' : 'disabled'} + `, + icon: "sprite-fm-mono icon-arrow-right", + onClick: () => page < chunksLength - 1 && onMovePage(utils.Bq.NEXT) + })); + } + return null; + }; + } + get fullscreen() { + return document.fullscreenElement; + } + get duration() { + return (Date.now() - this.props.call.ts) / 1000; + } + get durationString() { + return this.duration ? secondsToTimeShort(this.duration) : '--:--:--'; + } + componentWillUnmount() { + super.componentWillUnmount(); + clearInterval(this.durationInterval); + document.removeEventListener(StreamHead.EVENTS.FULLSCREEN, this.closeTooltips); + document.removeEventListener(StreamHead.EVENTS.CLICK_DIALOG, this.handleDialogClose); + } + componentDidMount() { + super.componentDidMount(); + this.durationInterval = setInterval(this.updateDurationDOM, 1000); + document.addEventListener(StreamHead.EVENTS.FULLSCREEN, this.closeTooltips); + document.addEventListener(StreamHead.EVENTS.CLICK_DIALOG, this.handleDialogClose); + } + render() { + const { + NAMESPACE + } = StreamHead; + const { + mode, + streamsPerPage, + chatRoom, + onStreamsPerPageChange, + onCallMinimize, + onModeChange, + setActiveElement + } = this.props; + const { + dialog + } = this.state; + const SIMPLETIP = { + position: 'bottom', + offset: 5, + className: 'theme-dark-forced' + }; + return JSX_("div", { + ref: this.domRef, + className: `${NAMESPACE}` + }, dialog && JSX_(this.Dialog, null), JSX_("div", { + className: `${NAMESPACE}-content theme-dark-forced` + }, JSX_("div", { + className: `${NAMESPACE}-info` + }, JSX_("div", { + ref: this.durationRef, + className: "stream-duration" + }, this.durationString), JSX_("div", { + ref: this.topicRef, + className: ` + stream-topic + ${chatRoom.isMeeting && chatRoom.publicLink ? 'has-meeting-link' : ''} + `, + onClick: () => chatRoom.isMeeting && chatRoom.publicLink && this.setState({ + dialog: !dialog, + banner: false + }, () => setActiveElement(this.state.dialog)) + }, JSX_(ui_utils.zT, null, chatRoom.getRoomTitle()), chatRoom.isMeeting && chatRoom.publicLink && JSX_("i", { + className: ` + sprite-fm-mono + ${dialog ? 'icon-arrow-up' : 'icon-arrow-down'} + ` + }))), JSX_(this.Pagination, null), JSX_("div", { + className: `${NAMESPACE}-controls` + }, JSX_(ModeSwitch, { + mode, + streamsPerPage, + onStreamsPerPageChange, + onModeChange, + setActiveElement + }), JSX_(meetings_button.A, { + className: "head-control", + simpletip: { + ...SIMPLETIP, + label: this.fullscreen ? l.exit_fullscreen : l[17803] + }, + icon: this.fullscreen ? 'icon-fullscreen-leave' : 'icon-fullscreen-enter', + onClick: this.toggleFullscreen + }, JSX_("span", null, this.fullscreen ? l.exit_fullscreen : l[17803])), JSX_(meetings_button.A, { + className: "head-control", + simpletip: { + ...SIMPLETIP, + label: l.minimize + }, + icon: "icon-call-min-mode", + onClick: () => { + onCallMinimize(); + eventlog(500305); + } + }, JSX_("div", null, l.minimize))))); + } +} +StreamHead.NAMESPACE = 'stream-head'; +StreamHead.EVENTS = { + FULLSCREEN: 'fullscreenchange', + SIMPLETIP: new Event('simpletipClose'), + CLICK_DIALOG: 'click' +}; +// EXTERNAL MODULE: ./js/chat/ui/fallback.jsx +const fallback = REQ_(3439); +// EXTERNAL MODULE: ./js/ui/dropdowns.jsx +const dropdowns = REQ_(1510); +// EXTERNAL MODULE: ./js/ui/buttons.jsx +const buttons = REQ_(5155); +;// ./js/chat/ui/meetings/floatExtendedControls.jsx + + + +class FloatExtendedControls extends REaCt().Component { + constructor(...args) { + super(...args); + this.isActive = type => { + return !!(this.props.call.av & type); + }; + } + render() { + const { + hasToRenderPermissionsWarning, + renderPermissionsWarning, + resetError, + showScreenDialog, + onScreenSharingClick, + onHoldClick + } = this.props; + const { + onHold, + Screen + } = SfuClient.Av; + const isOnHold = this.isActive(onHold); + const callHoldLabel = isOnHold ? l[23459] : l[23460]; + const screenSharingLabel = this.isActive(Screen) ? l[22890] : l[22889]; + return JSX_(buttons.$, { + className: "mega-button theme-light-forced round large button-group", + icon: "sprite-fm-mono icon-options", + showScreenDialog + }, this.isActive(Screen) && JSX_("div", { + className: "info-indicator active" + }), JSX_(dropdowns.ms, { + className: "button-group-menu theme-dark-forced", + noArrow: true, + positionAt: "center top", + collision: "none", + vertOffset: -90, + ref: r => { + this.dropdownRef = r; + }, + onBeforeActiveChange: e => { + if (e) { + $(document.body).trigger('closeAllDropdownsExcept', this.dropdownRef); + } + }, + showScreenDialog + }, JSX_(dropdowns.tJ, { + key: "call-hold", + className: ` + theme-dark-forced + ${isOnHold ? 'active' : ''} + `, + label: callHoldLabel, + icon: ` + sprite-fm-mono + ${isOnHold ? 'icon-play-small-regular-outline' : 'icon-pause-small-regular-outline'} + `, + onClick: onHoldClick + }), JSX_(dropdowns.tJ, { + key: "screen-sharing", + className: ` + theme-dark-forced + ${isOnHold ? 'disabled' : ''} + ${this.isActive(Screen) ? 'active' : ''} + `, + label: screenSharingLabel, + icon: ` + sprite-fm-mono + ${this.isActive(Screen) ? 'icon-monitor-off' : 'icon-monitor'} + `, + onClick: () => { + resetError(Av.Screen); + onScreenSharingClick(); + } + }), hasToRenderPermissionsWarning(Screen) ? renderPermissionsWarning(Screen, this) : null)); + } +} +FloatExtendedControls.NAMESPACE = 'stream-extended-controls'; +;// ./js/chat/ui/meetings/micObserver.jsx + + + + +const withMicObserver = Component => class extends mixins.w9 { + constructor(props) { + super(props); + this.namespace = `SO-${Component.NAMESPACE}`; + this.inputObserver = `onNoMicInput.${this.namespace}`; + this.sendObserver = `onAudioSendDenied.${this.namespace}`; + this.state = { + signal: true, + blocked: false + }; + this.renderSignalWarning = this.renderSignalWarning.bind(this); + this.renderBlockedWarning = this.renderBlockedWarning.bind(this); + } + bindObservers() { + this.props.chatRoom.rebind(this.inputObserver, () => this.setState({ + signal: false + })).rebind(this.sendObserver, () => { + this.setState({ + blocked: true + }, () => { + if (this.props.minimized) { + const toast = new ChatToast(l.max_speakers_toast, { + icon: 'sprite-fm-uni icon-hazard', + close: true + }); + toast.dispatch(); + } + }); + }); + } + renderSignalDialog() { + return msgDialog('warningb', null, l.no_mic_title, l.chat_mic_off_tooltip, null, 1); + } + renderSignalWarning() { + return JSX_("div", { + className: ` + ${this.namespace} + meetings-signal-issue + simpletip + `, + "data-simpletip": l.show_info, + "data-simpletipposition": "top", + "data-simpletipoffset": "5", + "data-simpletip-class": "theme-dark-forced", + onClick: () => this.renderSignalDialog() + }, JSX_("i", { + className: "sprite-fm-mono icon-exclamation-filled" + })); + } + renderBlockedWarning() { + return JSX_("div", { + className: "stream-toast theme-dark-forced" + }, JSX_("div", { + className: "stream-toast-content" + }, JSX_("i", { + className: "stream-toast-icon sprite-fm-uni icon-warning" + }), JSX_("div", { + className: "stream-toast-message" + }, l.max_speakers_toast), JSX_(meetings_button.A, { + className: "mega-button action stream-toast-close", + icon: "sprite-fm-mono icon-close-component", + onClick: () => this.setState({ + blocked: false + }) + }))); + } + componentWillUnmount() { + super.componentWillUnmount(); + this.props.chatRoom.unbind(this.inputObserver); + } + componentDidMount() { + super.componentDidMount(); + this.bindObservers(); + } + render() { + return JSX_(Component, (0,esm_extends.A)({}, this.props, { + signal: this.state.signal, + renderSignalWarning: this.renderSignalWarning, + blocked: this.state.blocked, + renderBlockedWarning: this.renderBlockedWarning + })); + } +}; +// EXTERNAL MODULE: ./js/chat/ui/meetings/permissionsObserver.jsx +const permissionsObserver = REQ_(192); +// EXTERNAL MODULE: ./js/chat/ui/meetings/hostsObserver.jsx +const hostsObserver = REQ_(7677); +;// ./js/chat/ui/meetings/float.jsx + + + + + + + + + + + +class FloatingVideo extends REaCt().Component { + constructor(...args) { + super(...args); + this.collapseListener = null; + this.state = { + collapsed: false + }; + this.toggleCollapsedMode = () => { + return this.setState(state => ({ + collapsed: !state.collapsed + })); + }; + } + componentWillUnmount() { + mBroadcaster.removeListener(this.collapseListener); + } + componentDidMount() { + this.collapseListener = mBroadcaster.addListener('meetings:collapse', () => this.setState({ + collapsed: true + })); + } + componentDidUpdate() { + if (typeof psa !== 'undefined') { + psa.repositionMeetingsCall(); + } + } + render() { + const { + peers, + minimized, + call, + floatDetached + } = this.props; + if (peers.length === 0 && !minimized && !call.isSharingScreen()) { + return null; + } + const STREAM_PROPS = { + ...this.props, + collapsed: this.state.collapsed, + toggleCollapsedMode: this.toggleCollapsedMode, + onLoadedData: this.onLoadedData + }; + if (minimized) { + return JSX_(ui_utils.Ay.RenderTo, { + element: document.body + }, JSX_(Stream, STREAM_PROPS)); + } + return floatDetached ? JSX_(Stream, STREAM_PROPS) : null; + } +} +FloatingVideo.NAMESPACE = 'float-video'; +FloatingVideo.POSITION_MODIFIER = 'with-sidebar'; +class Stream extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + this.DRAGGABLE = { + POSITION: { + top: undefined, + left: undefined + }, + OPTIONS: { + scroll: 'false', + cursor: 'move', + opacity: 1, + start: () => { + if (this.state.options) { + this.handleOptionsToggle(); + } + $(document.body).trigger('closeAllDropdownsExcept'); + }, + stop: (event, ui) => { + this.DRAGGABLE.POSITION = ui.position; + const { + clientWidth, + clientHeight + } = document.body; + const { + helper + } = ui; + const { + left, + top + } = this.DRAGGABLE.POSITION; + if (left < clientWidth / 2) { + helper.css('left', `${left / clientWidth * 100}%`).css('right', 'unset'); + } else { + helper.css('left', 'unset').css('right', `${clientWidth - left - helper.width()}px`); + } + if (top < clientHeight / 2) { + helper.css('top', `${top / clientHeight * 100}%`).css('bottom', 'unset'); + } else { + helper.css('top', 'unset').css('bottom', `${clientHeight - top - helper.height()}px`); + } + } + } + }; + this.EVENTS = { + MINIMIZE: ['slideshow:open', 'contact:open', 'textEditor:open', 'chat:open'], + EXPAND: ['slideshow:close', 'textEditor:close'] + }; + this.LISTENERS = []; + this.PREV_STATE = {}; + this.state = { + options: false + }; + this.getStreamSource = () => { + const { + call, + mode, + forcedLocal + } = this.props; + return mode === utils.g.MINI && !forcedLocal ? call.getActiveStream() : call.getLocalStream(); + }; + this.unbindEvents = () => { + const events = [...this.EVENTS.MINIMIZE, ...this.EVENTS.EXPAND]; + for (let i = events.length; i--;) { + const event = events[i]; + mBroadcaster.removeListener(this.LISTENERS[event]); + } + document.removeEventListener('click', this.handleOptionsClose); + }; + this.bindEvents = () => { + for (let i = this.EVENTS.MINIMIZE.length; i--;) { + const event = this.EVENTS.MINIMIZE[i]; + this.LISTENERS[event] = mBroadcaster.addListener(event, () => { + this.PREV_STATE.minimised = this.props.minimized; + return this.props.onCallMinimize(); + }); + } + for (let i = this.EVENTS.EXPAND.length; i--;) { + const event = this.EVENTS.EXPAND[i]; + this.LISTENERS[event] = mBroadcaster.addListener(event, () => { + if (this.PREV_STATE.minimised) { + delete this.PREV_STATE.minimised; + return; + } + delete this.PREV_STATE.minimised; + return this.props.view === utils.gR.CHAT && this.props.onCallExpand(); + }); + } + document.addEventListener('click', this.handleOptionsClose); + }; + this.initDraggable = () => { + let _this$domRef; + const { + minimized, + wrapperRef + } = this.props; + const containerEl = (_this$domRef = this.domRef) == null ? void 0 : _this$domRef.current; + if (containerEl) { + $(containerEl).draggable({ + ...this.DRAGGABLE.OPTIONS, + containment: minimized ? 'body' : wrapperRef == null ? void 0 : wrapperRef.current + }); + } + }; + this.repositionDraggable = () => { + let _this$props$wrapperRe, _this$domRef2; + const wrapperEl = (_this$props$wrapperRe = this.props.wrapperRef) == null ? void 0 : _this$props$wrapperRe.current; + const localEl = (_this$domRef2 = this.domRef) == null ? void 0 : _this$domRef2.current; + if (localEl.offsetLeft + localEl.offsetWidth > wrapperEl.offsetWidth) { + localEl.style.left = 'unset'; + localEl.style.removeProperty("right"); + } + }; + this.handleOptionsClose = ({ + target + }) => { + if (this.state.options && !target.classList.contains('icon-options')) { + this.setState({ + options: false + }); + } + }; + this.handleOptionsToggle = () => this.setState({ + options: !this.state.options + }); + this.renderOnHoldVideoNode = () => JSX_(LocalVideoHiRes, { + chatRoom: this.props.chatRoom + }); + this.renderOptionsDialog = () => { + const { + call, + mode, + forcedLocal, + onScreenSharingClick, + onSpeakerChange, + onModeChange, + toggleCollapsedMode, + onMoveIntoGrid + } = this.props; + const IS_SPEAKER_VIEW = mode === utils.g.MAIN && forcedLocal; + const { + POSITION + } = this.DRAGGABLE; + return JSX_("div", { + className: ` + ${FloatingVideo.NAMESPACE}-options + ${POSITION.left < 200 ? 'options-top' : ''} + ${POSITION.left < 200 && POSITION.top < 100 ? 'options-bottom' : ''} + theme-dark-forced + ` + }, JSX_("ul", null, JSX_("li", null, JSX_(meetings_button.A, { + icon: ` + sprite-fm-mono + ${IS_SPEAKER_VIEW ? 'grid-9' : 'grid-main'} + `, + onClick: () => this.setState({ + options: false + }, () => { + if (IS_SPEAKER_VIEW) { + return onModeChange(utils.g.THUMBNAIL); + } + onSpeakerChange(call.getLocalStream()); + }) + }, JSX_("div", null, IS_SPEAKER_VIEW ? l.switch_to_thumb_view : l.display_in_main_view))), JSX_("li", null, JSX_(meetings_button.A, { + icon: "sprite-fm-mono icon-collapse-up", + onClick: onMoveIntoGrid + }, JSX_("div", null, l.move_into_grid_button))), JSX_("li", null, JSX_(meetings_button.A, { + icon: "sprite-fm-mono icon-download-standard", + onClick: () => this.setState({ + options: false + }, () => toggleCollapsedMode()) + }, JSX_("div", null, l.collapse_self_video)))), !!(call.av & SfuClient.Av.Screen) && JSX_("ul", { + className: "has-separator" + }, JSX_("li", null, JSX_(meetings_button.A, { + className: "end-screen-share", + icon: "icon-end-screenshare", + onClick: () => { + this.setState({ + options: false + }); + onScreenSharingClick(); + } + }, JSX_("div", null, l[22890]))))); + }; + this.renderMiniMode = source => { + const { + call, + chatRoom, + mode, + minimized, + isPresenterNode, + onLoadedData + } = this.props; + if (call.isOnHold) { + return this.renderOnHoldVideoNode(); + } + const VideoClass = source.isLocal ? isPresenterNode ? LocalVideoHiRes : LocalVideoThumb : PeerVideoHiRes; + return JSX_(VideoClass, { + key: source, + source, + chatRoom, + mode, + minimized, + isPresenterNode, + onLoadedData + }); + }; + this.renderSelfView = () => { + const { + isOnHold, + raisedHandPeers, + minimized, + chatRoom, + isPresenterNode, + call, + onLoadedData + } = this.props; + const { + options + } = this.state; + if (isOnHold) { + return this.renderOnHoldVideoNode(); + } + const VideoNode = call.isSharingScreen() ? LocalVideoHiResCloned : LocalVideoThumb; + return JSX_(REaCt().Fragment, null, JSX_(VideoNode, { + isSelfOverlay: true, + raisedHandPeers, + minimized, + chatRoom, + isPresenterNode, + onLoadedData + }), JSX_("div", { + className: `${FloatingVideo.NAMESPACE}-self-overlay` + }, minimized ? null : JSX_(meetings_button.A, { + className: ` + mega-button + theme-light-forced + action + small + float-video-options-control + ${options ? 'active' : ''} + `, + icon: "sprite-fm-mono icon-options", + onClick: () => this.handleOptionsToggle() + }), options && this.renderOptionsDialog())); + }; + } + componentWillUnmount() { + super.componentWillUnmount(); + this.unbindEvents(); + } + componentDidUpdate(prevProps) { + super.componentDidUpdate(); + if (this.props.mode !== prevProps.mode) { + this.initDraggable(); + } + if (this.props.sidebar !== prevProps.sidebar && this.props.sidebar) { + this.repositionDraggable(); + } + } + componentDidMount() { + super.componentDidMount(); + this.bindEvents(); + this.initDraggable(); + } + render() { + const { + NAMESPACE, + POSITION_MODIFIER + } = FloatingVideo; + const { + call, + mode, + minimized, + sidebar, + collapsed, + toggleCollapsedMode, + onCallExpand + } = this.props; + const IS_MINI_MODE = mode === utils.g.MINI; + if (collapsed) { + return JSX_("div", { + ref: this.domRef, + className: ` + ${NAMESPACE} + collapsed + theme-dark-forced + ${sidebar && !minimized ? POSITION_MODIFIER : ''} + `, + onClick: toggleCollapsedMode + }, JSX_("i", { + className: "sprite-fm-mono icon-arrow-up icon-collapse" + }), JSX_("div", { + className: "collapsed-audio-indicator" + }, JSX_(AudioLevelIndicator, { + source: call.getLocalStream() + }))); + } + const source = this.getStreamSource() || call.getLocalStream(); + return JSX_("div", { + ref: this.domRef, + className: ` + ${NAMESPACE} + ${IS_MINI_MODE ? 'mini' : ''} + ${minimized ? 'minimized' : ''} + ${this.state.options ? 'active' : ''} + ${sidebar && !minimized ? POSITION_MODIFIER : ''} + `, + onClick: ({ + target + }) => minimized && target.classList.contains(`${NAMESPACE}-overlay`) && onCallExpand() + }, IS_MINI_MODE && this.renderMiniMode(source), !IS_MINI_MODE && this.renderSelfView(), minimized && JSX_(__Minimized, (0,esm_extends.A)({}, this.props, { + onOptionsToggle: this.handleOptionsToggle + }))); + } +} +class Minimized extends mixins.w9 { + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.SIMPLETIP_PROPS = { + position: 'top', + offset: 5, + className: 'theme-dark-forced' + }; + this.waitingPeersListener = undefined; + this.raisedHandListener = undefined; + this.state = { + unread: 0, + waitingRoomPeers: [], + raisedHandPeers: [], + hideWrList: false, + hideHandsList: false + }; + this.isActive = type => { + return this.props.call.av & type; + }; + this.getUnread = () => { + const { + chatRoom + } = this.props; + chatRoom.rebind(Minimized.UNREAD_EVENT, () => this.setState({ + unread: chatRoom.getUnreadCount() + }, () => this.safeForceUpdate())); + }; + this.renderSignalWarning = () => this.props.signal ? null : this.props.renderSignalWarning(); + this.renderPermissionsWarning = type => { + const { + hasToRenderPermissionsWarning, + renderPermissionsWarning + } = this.props; + if (hasToRenderPermissionsWarning(type)) { + return renderPermissionsWarning(type, this); + } + return null; + }; + this.renderStreamControls = () => { + const { + call, + chatRoom, + recorderCid, + hasToRenderPermissionsWarning, + renderPermissionsWarning, + resetError, + onRecordingToggle, + onAudioClick, + onVideoClick, + onScreenSharingClick, + onHoldClick, + onCallEnd + } = this.props; + const audioLabel = this.isActive(SfuClient.Av.Audio) ? l[16214] : l[16708]; + const videoLabel = this.isActive(SfuClient.Av.Camera) ? l[22894] : l[22893]; + const LeaveButton = (0,hostsObserver.C)(({ + hasHost, + chatRoom, + confirmLeave, + onLeave + }) => { + return JSX_(meetings_button.A, { + simpletip: { + ...this.SIMPLETIP_PROPS, + label: l[5884] + }, + className: "mega-button theme-dark-forced round large end-call", + icon: "icon-phone-02", + onClick: ev => { + ev.stopPropagation(); + const callParticipants = chatRoom.getCallParticipants(); + const doLeave = () => !chatRoom.iAmOperator() || hasHost(chatRoom.call ? chatRoom.call.peers.map(a => a.userHandle) : []) || callParticipants.length === 1 ? onLeave() : confirmLeave({ + title: l.assign_host_leave_call, + body: l.assign_host_leave_call_details, + cta: l.assign_host_button, + altCta: l.leave_anyway + }); + return recorderCid && recorderCid === call.sfuClient.cid ? (0,utils.sX)(doLeave, onRecordingToggle) : doLeave(); + } + }, JSX_("span", null, l[5884])); + }); + return JSX_(REaCt().Fragment, null, JSX_("div", { + className: `${FloatingVideo.NAMESPACE}-controls` + }, JSX_("div", { + className: "meetings-signal-container" + }, JSX_(meetings_button.A, { + simpletip: { + ...this.SIMPLETIP_PROPS, + label: audioLabel + }, + className: ` + mega-button + theme-light-forced + round + ${this.isActive(SfuClient.Av.onHold) ? 'disabled' : ''} + ${this.isActive(SfuClient.Av.Audio) ? '' : 'with-fill'} + `, + icon: this.isActive(SfuClient.Av.Audio) ? 'icon-mic-thin-outline' : 'icon-mic-off-thin-outline', + onClick: ev => { + ev.stopPropagation(); + resetError(Av.Audio); + onAudioClick(); + } + }, JSX_("span", null, audioLabel)), this.renderSignalWarning(), this.renderPermissionsWarning(Av.Audio)), JSX_("div", { + className: "meetings-signal-container" + }, JSX_(meetings_button.A, { + simpletip: { + ...this.SIMPLETIP_PROPS, + label: videoLabel + }, + className: ` + mega-button + theme-light-forced + round + ${this.isActive(SfuClient.Av.onHold) ? 'disabled' : ''} + ${this.isActive(SfuClient.Av.Camera) ? '' : 'with-fill'} + `, + icon: this.isActive(SfuClient.Av.Camera) ? 'icon-video-thin-outline' : 'icon-video-off-thin-outline', + onClick: ev => { + ev.stopPropagation(); + resetError(Av.Camera); + onVideoClick(); + } + }, JSX_("span", null, videoLabel)), this.renderPermissionsWarning(Av.Camera)), JSX_("div", { + className: "meetings-signal-container" + }, JSX_(FloatExtendedControls, { + call, + chatRoom, + onScreenSharingClick, + onHoldClick, + hasToRenderPermissionsWarning, + renderPermissionsWarning, + resetError, + showScreenDialog: !!this.props[`dialog-${Av.Screen}`] + }), this.renderPermissionsWarning(Av.Screen)), JSX_(LeaveButton, { + chatRoom, + participants: chatRoom.getCallParticipants(), + onLeave: onCallEnd, + onConfirmDenied: onCallEnd + })), JSX_("span", { + className: `${FloatingVideo.NAMESPACE}-fade` + })); + }; + this.renderPeersList = () => { + const { + onCallExpand, + onParticipantsToggle, + onWrListToggle + } = this.props; + const { + waitingRoomPeers, + raisedHandPeers, + hideHandsList, + hideWrList + } = this.state; + if (hideHandsList && hideWrList) { + return null; + } + const showRaised = hideHandsList || !hideWrList && waitingRoomPeers.length ? false : !!raisedHandPeers.length; + if (!showRaised && hideWrList) { + return null; + } + const showButton = !showRaised || showRaised && raisedHandPeers.length > 1; + return JSX_("div", { + className: ` + ${FloatingVideo.NAMESPACE}-alert + alert--waiting-peers + theme-dark-forced + `, + onClick: onCallExpand + }, JSX_(meetings_button.A, { + className: "close js-close", + icon: "sprite-fm-mono icon-dialog-close", + hideWrList, + hideHandsList, + onClick: ev => { + ev.stopPropagation(); + this.setState({ + hideHandsList: hideWrList || showRaised, + hideWrList: true + }); + } + }), JSX_("div", { + className: `alert-label ${showButton ? '' : 'label-only'}` + }, showRaised && JSX_("i", { + className: "sprite-fm-uni icon-raise-hand" + }), !hideWrList && !!waitingRoomPeers.length && mega.icu.format(l.wr_peers_waiting, waitingRoomPeers.length), showRaised && (raisedHandPeers.length > 1 ? raisedHandPeers.includes(u_handle) ? mega.icu.format(l.raise_self_peers_raised, raisedHandPeers.length - 1) : mega.icu.format(l.raise_peers_raised, raisedHandPeers.length) : JSX_(ui_utils.P9, { + tag: "span", + content: raisedHandPeers[0] === u_handle ? l.raise_self_raised : l.raise_peer_raised.replace('%s', megaChat.html(M.getNameByHandle(raisedHandPeers[0]))) + }))), showButton && JSX_(meetings_button.A, { + className: "show-people", + label: showRaised ? l[16797] : l.wr_see_waiting, + onClick: ev => { + ev.stopPropagation(); + const promise = onCallExpand().catch(dump); + if (showRaised) { + promise.then(() => onParticipantsToggle(true)); + } else if (waitingRoomPeers.length > 1) { + promise.then(() => onWrListToggle(true)); + } + } + }, showRaised ? l[16797] : l.wr_see_waiting)); + }; + this.state.waitingRoomPeers = this.props.waitingRoomPeers || []; + this.state.raisedHandPeers = this.props.raisedHandPeers || []; + } + componentDidMount() { + super.componentDidMount(); + this.getUnread(); + this.waitingPeersListener = mBroadcaster.addListener('meetings:peersWaiting', waitingRoomPeers => this.setState({ + waitingRoomPeers, + hideWrList: false, + hideHandsList: false + }, () => this.safeForceUpdate())); + this.raisedHandListener = mBroadcaster.addListener('meetings:raisedHand', raisedHandPeers => this.setState({ + raisedHandPeers, + hideWrList: false, + hideHandsList: false + }, () => this.safeForceUpdate())); + ['onCallPeerJoined', 'onCallPeerLeft'].map(event => this.props.chatRoom.rebind(`${event}.${Minimized.NAMESPACE}`, (ev, { + userHandle + }) => this.isMounted() && this.setState(state => ({ + raisedHandPeers: state.raisedHandPeers.includes(userHandle) ? state.raisedHandPeers.filter(h => h !== userHandle) : [...this.props.call.sfuClient.raisedHands] + }), this.safeForceUpdate))); + } + componentWillUnmount() { + super.componentWillUnmount(); + this.props.chatRoom.unbind(Minimized.UNREAD_EVENT); + [this.waitingPeersListener, this.raisedHandListener].map(listener => mBroadcaster.removeListener(listener)); + ['onCallPeerJoined', 'onCallPeerLeft'].map(event => this.props.chatRoom.off(`${event}.${Minimized.NAMESPACE}`)); + } + render() { + const { + onCallExpand + } = this.props; + const { + unread, + raisedHandPeers, + waitingRoomPeers + } = this.state; + return JSX_("div", { + ref: this.domRef, + className: `${FloatingVideo.NAMESPACE}-wrapper` + }, JSX_("div", { + className: `${FloatingVideo.NAMESPACE}-overlay` + }, JSX_(meetings_button.A, { + simpletip: { + ...this.SIMPLETIP_PROPS, + label: l.expand_mini_call + }, + className: "mega-button theme-light-forced action small expand", + icon: "sprite-fm-mono icon-fullscreen-enter", + onClick: ev => { + ev.stopPropagation(); + onCallExpand(); + } + }), this.renderStreamControls()), waitingRoomPeers && waitingRoomPeers.length || raisedHandPeers && raisedHandPeers.length ? this.renderPeersList() : null, unread ? JSX_("div", { + className: `${FloatingVideo.NAMESPACE}-notifications` + }, JSX_(meetings_button.A, { + className: "mega-button round large chat-control", + icon: "icon-chat-filled" + }, JSX_("span", null, l.chats)), JSX_("span", null, unread > 9 ? '9+' : unread)) : null); + } +} +Minimized.NAMESPACE = 'float-video-minimized'; +Minimized.UNREAD_EVENT = 'onUnreadCountUpdate.localStreamNotifications'; +const __Minimized = (0,mixins.Zz)(withMicObserver, permissionsObserver.$)(Minimized); +;// ./js/chat/ui/meetings/stream.jsx + + + + + + + + + + + + + + +const Admit = (0,external_React_.lazy)(() => REQ_.e( 752).then(REQ_.bind(REQ_, 3056))); +const NAMESPACE = 'stream'; +const chunkNodes = (nodes, size) => { + if (nodes && nodes.length && size) { + const chunked = []; + let index = 0; + while (index < nodes.length) { + chunked.push({ + id: index, + nodes: nodes.slice(index, index + size) + }); + index += size; + } + return chunked; + } + return null; +}; +const filterAndSplitSources = (sources, call) => { + const screen = []; + const video = []; + const rest = []; + for (const peer of Object.values(sources.toJS())) { + if (peer instanceof CallManager2.Peer) { + if (peer.hasScreen) { + screen.push(peer, peer); + } else if (peer.videoMuted) { + rest.push(peer); + } else { + video.push(peer); + } + } + } + const local = call.getLocalStream(); + if (local.hasScreen) { + const presenters = [...call.presenterStreams]; + if (presenters.pop() === u_handle) { + screen.unshift(local, local); + } else { + screen.push(local, local); + } + } else if (local.av & Av.Camera) { + video.unshift(local); + } else { + rest.push(local); + } + return { + screen, + video, + rest + }; +}; +class stream_Stream extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + this.containerRef = REaCt().createRef(); + this.nodeRefs = []; + this.chunks = []; + this.chunksLength = 0; + this.lastRescaledCache = undefined; + this.state = { + page: 0, + overlayed: false, + streamsPerPage: utils.gh.MED, + floatDetached: false, + wrToggled: false + }; + this.toggleFloatDetachment = () => { + this.setState(state => ({ + floatDetached: !state.floatDetached + })); + }; + this.toggleWaitingRoomList = state => { + this.setState({ + wrToggled: state + }); + }; + } + movePage(direction) { + return this.setState(state => ({ + page: direction === utils.Bq.NEXT ? state.page + 1 : state.page - 1 + })); + } + getColumns(streamsCount) { + switch (true) { + case streamsCount >= 43: + return 7; + case streamsCount >= 26: + return 6; + case streamsCount >= 17: + return 5; + case streamsCount >= 13: + return 4; + case streamsCount === 1: + return 1; + case streamsCount >= 7: + return 3; + default: + return 2; + } + } + scaleNodes(columns, forced = false) { + let _Object$values$page; + const { + peers, + minimized, + mode, + call + } = this.props; + const { + screen, + video, + rest + } = filterAndSplitSources(peers, call); + let presenter = false; + const sources = [...screen, ...video, ...rest].filter(source => { + if (!source.isLocal) { + return true; + } + if (source.hasScreen && !presenter) { + presenter = true; + return true; + } + return false; + }); + presenter = false; + const container = this.containerRef.current; + this.lastRescaledCache = forced ? null : this.lastRescaledCache; + if (minimized || !container) { + return; + } + const { + floatDetached, + streamsPerPage, + page + } = this.state; + const parentRef = container.parentNode; + const parentStyle = getComputedStyle(parentRef); + const extraVerticalMargin = parseInt(parentStyle.paddingTop) + parseInt(parentStyle.paddingBottom); + let containerWidth = parentRef.offsetWidth; + let containerHeight = parentRef.offsetHeight - extraVerticalMargin; + const nodesPerPage = floatDetached ? streamsPerPage : streamsPerPage - 1; + const streamsInUI = sources.length > nodesPerPage ? (_Object$values$page = Object.values(this.chunks)[page]) == null ? void 0 : _Object$values$page.nodes : sources; + if (streamsInUI) { + const streamCountInUI = sources.length > nodesPerPage || floatDetached ? streamsInUI.length : streamsInUI.length + 1; + let rows; + if (mode === utils.g.THUMBNAIL) { + columns = typeof columns === 'number' ? columns : this.getColumns(streamCountInUI); + rows = Math.ceil(streamCountInUI / columns); + } else { + rows = 1; + columns = 1; + } + containerWidth -= columns * 6 * 2; + containerHeight -= rows * 6 * 2; + let targetWidth = Math.floor(containerWidth / columns); + let targetHeight = targetWidth / 16 * 9; + if (targetHeight * rows > containerHeight) { + targetHeight = Math.floor(containerHeight / rows); + targetWidth = targetHeight / 9 * 16; + } + const nodeRefs = this.nodeRefs.flat(); + const nodeRefsLength = nodeRefs.length; + const viewMode = mode || utils.g.MAIN; + let cache = `${viewMode}:${targetWidth}:${targetHeight}:${nodeRefsLength}:${rows}:${streamCountInUI}:${columns}`; + for (let i = 0; i < nodeRefsLength; i++) { + cache += `${nodeRefs[i].cacheKey}:`; + } + if (this.lastRescaledCache === cache) { + return; + } + this.lastRescaledCache = cache; + for (let i = 0; i < nodeRefsLength; i++) { + const node = nodeRefs[i]; + if (node && node.ref) { + node.ref.style.width = `${targetWidth}px`; + node.ref.style.height = `${targetHeight}px`; + } + } + container.style.width = `${(targetWidth + 12) * columns}px`; + } + } + renderNodes() { + const { + mode, + peers, + call, + raisedHandPeers, + chatRoom, + onVideoDoubleClick, + onModeChange + } = this.props; + const { + page, + streamsPerPage, + floatDetached + } = this.state; + const { + screen, + video, + rest + } = filterAndSplitSources(peers, call); + const sources = [...screen, ...video, ...rest]; + if (mode === utils.g.THUMBNAIL) { + const nodesPerPage = floatDetached ? streamsPerPage : streamsPerPage - 1; + if (sources.length <= nodesPerPage) { + const $$PEER = (peer, i) => { + const { + clientId, + hasScreenAndCam, + hasScreen, + isLocal + } = peer; + if (screen.length && (screen[0].clientId === clientId || screen[0].isLocal && isLocal)) { + screen.shift(); + } + if (!(peer instanceof CallManager2.Peer)) { + const isPresenterNode = screen.length && screen[0].isLocal; + if (floatDetached && !isPresenterNode) { + return; + } + if (floatDetached || !isPresenterNode) { + return JSX_(LocalVideoThumb, { + key: `${mode}_thumb_${u_handle}`, + chatRoom, + isPresenterNode: false, + raisedHandPeers, + source: peer, + didMount: ref => { + this.nodeRefs.push({ + clientId: u_handle, + cacheKey: `${mode}_${u_handle}_thumb`, + ref + }); + this.scaleNodes(undefined, true); + }, + willUnmount: () => { + this.nodeRefs = this.nodeRefs.filter(nodeRef => nodeRef.cacheKey !== `${mode}_${u_handle}_thumb`); + } + }, this.renderSelfViewMenu()); + } + return JSX_(LocalVideoHiRes, { + key: `${mode}_${u_handle}`, + chatRoom, + isPresenterNode, + source: isPresenterNode && peer, + raisedHandPeers, + didMount: ref => { + this.nodeRefs.push({ + clientId: u_handle, + cacheKey: `${mode}_${u_handle}`, + ref + }); + this.scaleNodes(undefined, true); + }, + willUnmount: () => { + this.nodeRefs = this.nodeRefs.filter(nodeRef => nodeRef.cacheKey !== `${mode}_${u_handle}`); + } + }, hasScreen ? this.renderNodeMenu(peer, { + isPresenterNode + }) : this.renderSelfViewMenu()); + } + const presenterCid = screen.length && screen[0].clientId === clientId; + let PeerClass = PeerVideoThumb; + if (hasScreenAndCam) { + PeerClass = presenterCid ? PeerVideoHiRes : PeerVideoThumbFixed; + } + const cacheKey = `${mode}_${clientId}_${i}_${hasScreenAndCam ? 1 : 0}`; + return JSX_(PeerClass, { + key: cacheKey, + mode, + chatRoom, + menu: true, + source: peer, + raisedHandPeers, + isPresenterNode: !!presenterCid, + onDoubleClick: (peer, e) => { + e.preventDefault(); + e.stopPropagation(); + onVideoDoubleClick(peer, !presenterCid); + }, + didMount: ref => { + this.nodeRefs.push({ + clientId: presenterCid || clientId, + cacheKey, + ref + }); + }, + willUnmount: () => { + this.nodeRefs = this.nodeRefs.filter(nodeRef => nodeRef.cacheKey !== cacheKey); + } + }, this.renderNodeMenu(peer, { + isPresenterNode: !!presenterCid + })); + }; + return sources.map((p, i) => $$PEER(p, i)); + } + if (floatDetached) { + for (let i = 0; i < sources.length; i++) { + if (sources[i].isLocal) { + sources.splice(i, 1); + break; + } + } + } + this.chunks = chunkNodes(sources, streamsPerPage); + this.chunksLength = Object.values(this.chunks).length; + return JSX_("div", { + className: "carousel" + }, JSX_("div", { + className: "carousel-container" + }, Object.values(this.chunks).map((chunk, i) => { + const { + id, + nodes + } = chunk; + return JSX_("div", { + key: id, + className: ` + carousel-page + ${i === page ? 'active' : ''} + ` + }, nodes.map((peer, j) => { + const { + clientId, + hasScreenAndCam, + hasScreen, + isLocal + } = peer; + if (screen.length && (screen[0].clientId === clientId || screen[0].isLocal && isLocal)) { + screen.shift(); + } + if (peer instanceof CallManager2.Peer) { + const presenterCid = screen.length && screen[0].clientId === clientId; + const cacheKey = `${mode}_${clientId}_${j + i * streamsPerPage}_${hasScreenAndCam ? 1 : 0}`; + let PeerClass = PeerVideoThumb; + if (hasScreenAndCam) { + PeerClass = presenterCid ? PeerVideoHiRes : PeerVideoThumbFixed; + } + return JSX_(PeerClass, { + key: cacheKey, + mode, + source: peer, + chatRoom, + isPresenterNode: !!presenterCid, + raisedHandPeers, + onDoubleClick: (peer, e) => { + e.preventDefault(); + e.stopPropagation(); + onVideoDoubleClick(peer); + }, + didMount: ref => { + if (!this.nodeRefs[id]) { + this.nodeRefs[id] = []; + } + this.nodeRefs[id].push({ + clientId: presenterCid || clientId, + ref, + cacheKey + }); + this.scaleNodes(undefined, true); + }, + willUnmount: () => { + this.nodeRefs = this.nodeRefs.map(chunk => chunk.filter(nodeRef => nodeRef.cacheKey !== cacheKey)); + } + }, this.renderNodeMenu(peer, { + isPresenterNode: !!presenterCid + })); + } + const isPresenterNode = screen.length && screen[0].isLocal; + if (floatDetached && !isPresenterNode) { + return null; + } + if (floatDetached || !isPresenterNode) { + return JSX_(LocalVideoThumb, { + key: `${mode}_thumb_${u_handle}`, + chatRoom, + source: peer, + isPresenterNode: false, + didMount: ref => { + if (!this.nodeRefs[id]) { + this.nodeRefs[id] = []; + } + this.nodeRefs[id].push({ + clientId: u_handle, + cacheKey: `${mode}_${u_handle}_thumb`, + ref + }); + this.scaleNodes(undefined, true); + }, + willUnmount: () => { + this.nodeRefs = this.nodeRefs.map(chunk => chunk.filter(nodeRef => nodeRef.cacheKey !== `${mode}_${u_handle}_thumb`)); + } + }, this.renderSelfViewMenu()); + } + return JSX_(LocalVideoHiRes, { + key: `${mode}_${u_handle}`, + chatRoom, + raisedHandPeers, + isPresenterNode, + source: isPresenterNode && peer, + didMount: ref => { + if (!this.nodeRefs[id]) { + this.nodeRefs[id] = []; + } + this.nodeRefs[id].push({ + clientId: u_handle, + ref, + cacheKey: `${mode}_${u_handle}` + }); + this.scaleNodes(undefined, true); + }, + willUnmount: () => { + this.nodeRefs = this.nodeRefs.map(chunk => chunk.filter(nodeRef => nodeRef.cacheKey !== `${mode}_${u_handle}`)); + } + }, hasScreen ? this.renderNodeMenu(peer, { + isPresenterNode + }) : this.renderSelfViewMenu()); + })); + }))); + } + const source = call.getActiveStream(); + if (!source) { + return null; + } + const VideoType = source.isLocal ? LocalVideoHiRes : PeerVideoHiRes; + const videoNodeRef = REaCt().createRef(); + return JSX_(VideoType, { + key: source.clientId, + chatRoom, + raisedHandPeers, + source, + isPresenterNode: source.hasScreen, + toggleFullScreen: () => { + call.setPinnedCid(source.clientId); + }, + onSpeakerChange: () => { + onModeChange(utils.g.THUMBNAIL); + }, + ref: node => { + videoNodeRef.current = node; + } + }, this.renderNodeMenu(source, { + key: `${source.clientId}-main`, + isMain: true, + videoNodeRef, + isPresenterNode: source.hasScreen + })); + } + renderNodeMenu(peer, props) { + const { + mode, + chatRoom, + ephemeralAccounts, + onCallMinimize, + onSpeakerChange, + onModeChange + } = this.props; + return JSX_(VideoNodeMenu, (0,esm_extends.A)({ + mode, + privilege: chatRoom.members[peer.userHandle], + chatRoom, + stream: peer, + ephemeralAccounts, + onCallMinimize, + onSpeakerChange, + onModeChange + }, props)); + } + renderSelfViewMenu() { + const { + call, + onSpeakerChange + } = this.props; + return JSX_("div", { + className: "node-menu theme-dark-forced" + }, JSX_("div", { + className: "node-menu-toggle" + }, JSX_("i", { + className: "sprite-fm-mono icon-more-horizontal-thin-outline" + })), JSX_("div", { + className: "node-menu-content" + }, JSX_("ul", null, JSX_("li", null, JSX_(meetings_button.A, { + icon: "sprite-fm-mono grid-main", + onClick: () => onSpeakerChange(call.getLocalStream()) + }, JSX_("span", null, l.display_in_main_view))), JSX_("li", null, JSX_(meetings_button.A, { + icon: "sprite-fm-mono grid-separate", + onClick: this.toggleFloatDetachment + }, JSX_("span", null, l.separate_from_grid_button)))))); + } + renderOnHold() { + return JSX_("div", { + className: "on-hold-overlay" + }, JSX_("div", { + className: "stream-on-hold theme-light-forced", + onClick: this.props.onHoldClick + }, JSX_("i", { + className: "sprite-fm-mono icon-play" + }), JSX_("span", null, l[23459]))); + } + renderStreamContainer() { + const { + call, + chatRoom, + peers, + stayOnEnd, + everHadPeers, + isOnHold, + mode, + hasOtherParticipants, + onInviteToggle, + onStayConfirm, + onCallEnd + } = this.props; + const { + screen, + video, + rest + } = filterAndSplitSources(peers, call); + const sources = [...screen, ...video, ...rest]; + const showNotice = sources.length === 0 || !hasOtherParticipants && !call.presenterStreams.has(u_handle); + const streamContainer = content => JSX_("div", { + ref: this.containerRef, + className: ` + ${NAMESPACE}-container + ${showNotice ? 'with-notice' : ''} + ${sources.length === 1 && mode === utils.g.THUMBNAIL ? `${this.state.floatDetached ? 'single' : 'dual'}-stream` : ''} + ` + }, content); + if (showNotice) { + return JSX_(ParticipantsNotice, { + call, + hasLeft: call.left, + chatRoom, + everHadPeers, + streamContainer, + stayOnEnd, + isOnHold, + onInviteToggle, + onStayConfirm, + onCallEnd: () => onCallEnd(1) + }); + } + return streamContainer(this.renderNodes()); + } + renderToaster() { + return JSX_(chatToaster.default, { + showDualNotifications: true, + hidden: this.props.minimized, + onShownToast: toast => { + if (toast.options && toast.options.persistent) { + this.setState({ + overlayed: true + }); + } + }, + onHideToast: toast => { + if (this.state.overlayed && toast.options && toast.options.persistent) { + this.setState({ + overlayed: false + }); + } + } + }); + } + specShouldComponentUpdate(nextProps) { + if (nextProps.minimized !== this.props.minimized || nextProps.mode !== this.props.mode || nextProps.isFloatingPresenter !== this.props.isFloatingPresenter) { + return true; + } + return null; + } + componentWillUnmount() { + super.componentWillUnmount(); + chatGlobalEventManager.r.removeEventListener('resize', this.getUniqueId()); + mBroadcaster.removeListener(this.callHoldListener); + } + componentDidMount() { + super.componentDidMount(); + this.scaleNodes(); + chatGlobalEventManager.r.addEventListener('resize', this.getUniqueId(), () => this.scaleNodes()); + this.callHoldListener = mBroadcaster.addListener('meetings:toggleHold', () => this.scaleNodes(undefined, true)); + } + componentDidUpdate() { + super.componentDidMount(); + const { + call, + mode, + forcedLocal, + onSpeakerChange, + onModeChange + } = this.props; + this.scaleNodes(); + if (this.chunksLength > 0 && this.state.page + 1 > this.chunksLength) { + this.movePage(utils.Bq.PREV); + } + if (mode === utils.g.THUMBNAIL && call.pinnedCid !== null) { + this.hasPresenter = true; + onSpeakerChange(call.getActiveStream()); + } else if (mode === utils.g.MAIN && call.pinnedCid === null && !call.presenterStreams.size && this.hasPresenter) { + this.hasPresenter = false; + onModeChange(utils.g.THUMBNAIL); + } else if (mode === utils.g.MAIN && forcedLocal && call.pinnedCid !== 0) { + onSpeakerChange(call.getActiveStream()); + } else if (!call.presenterStreams.size) { + this.hasPresenter = false; + } + } + render() { + const { + overlayed, + page, + streamsPerPage, + floatDetached, + wrToggled + } = this.state; + const { + mode, + call, + chatRoom, + minimized, + peers, + sidebar, + hovered, + forcedLocal, + view, + isOnHold, + waitingRoomPeers, + recorderCid, + raisedHandPeers, + isFloatingPresenter, + onRecordingToggle, + onCallMinimize, + onCallExpand, + onModeChange, + onAudioClick, + onVideoClick, + onCallEnd, + onScreenSharingClick, + onHoldClick, + onSpeakerChange, + onParticipantsToggle, + setActiveElement + } = this.props; + return JSX_("div", { + ref: this.domRef, + className: ` + ${NAMESPACE} + ${sidebar ? '' : 'full'} + ${hovered ? 'hovered' : ''} + ` + }, JSX_(external_React_.Suspense, { + fallback: JSX_(fallback.A, null) + }, waitingRoomPeers && waitingRoomPeers.length ? JSX_(Admit, { + chatRoom, + call, + peers: waitingRoomPeers, + expanded: wrToggled, + onWrListToggle: this.toggleWaitingRoomList + }) : null), this.renderToaster(), minimized ? null : JSX_(REaCt().Fragment, null, JSX_("div", { + className: ` + ${NAMESPACE}-wrapper + ${mode === utils.g.MAIN ? 'with-participants-block' : ''} + ` + }, isOnHold ? this.renderOnHold() : overlayed && JSX_("div", { + className: "call-overlay" + }), this.renderStreamContainer()), mode === utils.g.MAIN && JSX_(ParticipantsBlock, (0,esm_extends.A)({}, this.props, { + floatDetached, + onSeparate: this.toggleFloatDetachment + })), JSX_(StreamHead, { + disableCheckingVisibility: true, + mode, + peers, + page, + streamsPerPage, + floatDetached, + chunksLength: this.chunksLength, + call, + chatRoom, + onCallMinimize, + onModeChange, + onStreamsPerPageChange: streamsPerPage => this.setState({ + streamsPerPage + }), + onMovePage: direction => this.movePage(direction), + setActiveElement + })), minimized || floatDetached ? JSX_(FloatingVideo, { + call, + peers, + mode, + view, + floatDetached, + isOnHold, + chatRoom, + minimized, + sidebar, + forcedLocal, + isPresenterNode: isFloatingPresenter, + wrapperRef: this.domRef, + waitingRoomPeers, + recorderCid, + raisedHandPeers, + onRecordingToggle, + onAudioClick, + onVideoClick, + onCallEnd, + onScreenSharingClick, + onCallMinimize, + onMoveIntoGrid: this.toggleFloatDetachment, + onCallExpand: async () => { + await onCallExpand(); + this.scaleNodes(undefined, true); + }, + onSpeakerChange, + onModeChange, + onHoldClick, + onParticipantsToggle, + onWrListToggle: this.toggleWaitingRoomList + }) : null); + } +} +// EXTERNAL MODULE: ./js/chat/ui/composedTextArea.jsx + 1 modules +const composedTextArea = REQ_(2558); +// EXTERNAL MODULE: ./js/chat/ui/historyPanel.jsx + 7 modules +const historyPanel = REQ_(5522); +// EXTERNAL MODULE: ./js/ui/perfectScrollbar.jsx +const perfectScrollbar = REQ_(1301); +;// ./js/chat/ui/meetings/collapse.jsx + +class Collapse extends REaCt().Component { + constructor(...args) { + super(...args); + this.state = { + expanded: true + }; + } + render() { + const { + expanded + } = this.state; + const { + heading, + badge, + children + } = this.props; + return JSX_("div", { + className: "collapse" + }, heading && JSX_("div", { + className: "collapse-head", + onClick: () => this.setState(state => ({ + expanded: !state.expanded + })) + }, JSX_("i", { + className: ` + sprite-fm-mono + ${expanded ? 'icon-arrow-down' : 'icon-arrow-up'} + ` + }), JSX_("h5", null, heading), badge !== undefined && badge > 0 && JSX_("span", { + className: "participants-count" + }, badge)), expanded && children); + } +} +// EXTERNAL MODULE: ./js/chat/ui/contactsPanel/utils.jsx +const contactsPanel_utils = REQ_(836); +;// ./js/chat/ui/meetings/participants.jsx + + + + + + + + + + + + +class Participant extends mixins.w9 { + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.raisedHandListener = undefined; + this.baseIconClass = 'sprite-fm-mono'; + this.state = { + raisedHandPeers: [] + }; + this.state.raisedHandPeers = this.props.raisedHandPeers || []; + } + componentDidMount() { + super.componentDidMount(); + this.props.source.registerConsumer(this); + this.raisedHandListener = mBroadcaster.addListener('meetings:raisedHand', raisedHandPeers => this.setState({ + raisedHandPeers + }, () => this.safeForceUpdate())); + } + componentWillUnmount() { + super.componentWillUnmount(); + this.props.source.deregisterConsumer(this); + mBroadcaster.removeListener(this.raisedHandListener); + } + onAvChange() { + this.safeForceUpdate(); + } + render() { + const { + call, + mode, + chatRoom, + source, + contact, + handle, + name, + recorderCid, + onCallMinimize, + onSpeakerChange, + onModeChange + } = this.props; + const { + isOnHold, + videoMuted, + audioMuted, + clientId + } = source; + const isRelated = (0,contactsPanel_utils.X7)(contact); + return JSX_("div", { + ref: this.domRef, + className: "participant-wrapper" + }, this.state.raisedHandPeers.includes(handle) && !isOnHold ? JSX_("div", { + className: "participant-signifier" + }, JSX_("i", { + className: "sprite-fm-uni icon-raise-hand" + })) : JSX_(ui_contacts.eu, { + contact: M.u[handle] + }), JSX_("div", { + className: "name" + }, handle === u_handle ? JSX_(ui_utils.zT, null, `${name} ${l.me}`) : JSX_(ui_contacts.uA, { + contact: M.u[handle], + emoji: true + }), (0,utils.Cy)(chatRoom, handle) && JSX_("span", null, JSX_("i", { + className: `${this.baseIconClass} icon-admin-outline` + }))), JSX_("div", { + className: "status" + }, recorderCid === clientId || recorderCid === call.sfuClient.cid && handle === u_handle ? JSX_("div", { + className: "recording-status" + }, JSX_("span", null)) : null, JSX_("i", { + className: ` + ${this.baseIconClass} + ${videoMuted ? 'icon-video-off-thin-outline inactive' : 'icon-video-thin-outline'} + ` + }), JSX_(AudioLevelIndicator, { + source + }), JSX_("div", { + className: "participants-menu theme-dark-forced" + }, JSX_("div", { + className: "participants-menu-toggle" + }, JSX_("i", { + className: "sprite-fm-mono icon-side-menu" + })), JSX_("div", { + className: "participants-menu-content" + }, JSX_("ul", null, isRelated ? JSX_("li", null, JSX_(meetings_button.A, { + icon: "sprite-fm-mono icon-info", + onClick: () => { + onCallMinimize(); + loadSubPage(`fm/chat/contacts/${handle}`); + } + }, JSX_("span", null, l[6859]))) : null, chatRoom.iAmOperator() && u_handle !== handle && !audioMuted && JSX_("li", null, JSX_(meetings_button.A, { + icon: "sprite-fm-mono icon-mic-off-thin-outline", + onClick: () => { + call.sfuClient.mutePeer(clientId); + megaChat.plugins.userHelper.getUserNickname(handle).catch(dump).always(name => { + ChatToast.quick(l.you_muted_peer.replace('%NAME', name || '')); + }); + } + }, JSX_("span", null, l[16214]))), isRelated ? JSX_("li", null, JSX_(meetings_button.A, { + icon: "sprite-fm-mono icon-chat", + onClick: () => { + onCallMinimize(); + loadSubPage(`fm/chat/p/${handle}`); + } + }, JSX_("span", null, l.send_message))) : null, chatRoom.iAmOperator() && u_handle !== handle && JSX_("li", null, JSX_(Privilege, { + stream: source, + chatRoom + })), JSX_("li", null, JSX_(Pin, { + mode, + stream: source, + onSpeakerChange, + onModeChange + })), call.isPublic && chatRoom.iAmOperator() && u_handle !== handle && JSX_("li", null, JSX_(meetings_button.A, { + icon: "sprite-fm-mono icon-disabled-filled", + onClick: () => chatRoom.trigger('onRemoveUserRequest', handle) + }, JSX_("span", null, l[8867])))))))); + } +} +class Participants extends mixins.w9 { + get allPeersMuted() { + return Object.values(this.props.peers).filter(p => p instanceof CallManager2.Peer).every(p => p.audioMuted); + } + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.muteRef = REaCt().createRef(); + this.NAMESPACE = 'participants'; + this.FILTER = { + IN_CALL: 0, + CHAT_PARTICIPANTS: 1 + }; + this.state = { + filter: this.FILTER.IN_CALL, + noResponsePeers: [], + ringingPeers: [], + allPeersMuted: undefined + }; + this.doHangUp = handle => { + if (handle) { + const { + call, + chatRoom + } = this.props; + return this.isMounted() && this.setState(state => ({ + ringingPeers: state.ringingPeers.filter(p => p !== handle) + }), () => chatRoom.ringUser(handle, call.callId, 0)); + } + }; + this.doCall = handle => { + if (handle) { + const { + call, + chatRoom + } = this.props; + this.setState(state => ({ + ringingPeers: [...state.ringingPeers, handle] + }), () => { + chatRoom.ringUser(handle, call.callId, 1); + if (chatRoom.options.w) { + let _call$sfuClient; + call == null || (_call$sfuClient = call.sfuClient) == null || _call$sfuClient.wrAllowJoin([handle]); + } + tSleep(40).then(() => { + this.doHangUp(handle); + return Object.keys(chatRoom.uniqueCallParts).includes(handle) ? null : this.setState(state => ({ + noResponsePeers: [...state.noResponsePeers, handle] + })); + }); + }); + } + }; + this.getCallState = handle => { + const { + noResponsePeers, + ringingPeers + } = this.state; + if (this.props.initialCallRinging || ringingPeers.includes(handle)) { + return l.call_state_calling; + } + if (noResponsePeers.includes(handle)) { + return l.call_state_no_response; + } + return l.call_state_not_in_call; + }; + this.getCallParticipants = () => { + const { + call, + mode, + chatRoom, + recorderCid, + raisedHandPeers, + onCallMinimize, + onSpeakerChange, + onModeChange + } = this.props; + const peers = Object.values(this.props.peers); + const $$PEER = peer => peer && JSX_("li", { + key: `${peer.clientId || ''}-${peer.userHandle}` + }, JSX_(Participant, { + call, + mode, + chatRoom, + source: peer.userHandle ? peer : call.getLocalStream(), + contact: M.u[peer.userHandle] || undefined, + handle: peer.userHandle || u_handle, + name: peer.name || M.getNameByHandle(u_handle), + recorderCid, + raisedHandPeers, + onCallMinimize, + onSpeakerChange, + onModeChange + })); + let $$RAISED = []; + for (const userHandle of call.sfuClient.raisedHands) { + const peer = peers.find(p => (p.userHandle || p.localPeerStream.userHandle) === userHandle); + $$RAISED = [...$$RAISED, $$PEER(peer)]; + } + const $$REST = peers.filter(p => ![...call.sfuClient.raisedHands].includes(p.userHandle || p.localPeerStream.userHandle)).sort((a, b) => !!a.userHandle - !!b.userHandle).map(peer => $$PEER(peer)); + return JSX_("ul", null, $$RAISED, $$REST); + }; + this.getChatParticipants = () => { + const { + chatRoom, + initialCallRinging + } = this.props; + const { + ringingPeers + } = this.state; + const callParticipants = Object.keys(chatRoom.uniqueCallParts); + const chatParticipants = chatRoom.getParticipantsExceptMe().filter(h => !callParticipants.includes(h)); + if (chatParticipants != null && chatParticipants.length) { + return JSX_(REaCt().Fragment, null, chatParticipants.length > 1 ? (() => { + const isRingingAll = initialCallRinging || JSON.stringify(ringingPeers) === JSON.stringify(chatParticipants); + return JSX_(meetings_button.A, { + className: ` + mega-button + action + neutral + call-control-all + ${isRingingAll ? 'disabled' : ''} + `, + icon: "sprite-fm-mono phone-call-01", + onClick: () => isRingingAll ? null : chatParticipants.map(handle => this.doCall(handle)) + }, l.call_all_button); + })() : null, JSX_("ul", null, chatParticipants.map(handle => { + const contact = M.u[handle]; + const isRinging = initialCallRinging || ringingPeers.includes(handle); + return JSX_("li", { + key: handle + }, JSX_(ui_contacts.eu, { + contact + }), JSX_("div", { + className: "name" + }, JSX_(ui_contacts.uA, { + contact: M.u[handle], + emoji: true + }), JSX_("span", { + className: ` + user-card-presence + ${megaChat.userPresenceToCssClass(contact.presence)} + ` + }), (0,utils.Cy)(chatRoom, handle) && JSX_("span", null, JSX_("i", { + className: "sprite-fm-mono icon-admin-outline" + })), JSX_("div", { + className: "call-state" + }, this.getCallState(handle))), isRinging ? null : JSX_("div", { + className: "call-control" + }, JSX_(meetings_button.A, { + className: "mega-button action neutral", + onClick: () => this.doCall(handle) + }, l.call_button))); + }))); + } + return JSX_("div", { + className: "participants-empty" + }, JSX_("span", { + className: "empty-check-icon" + }), JSX_("h3", null, l.all_participants_in_call)); + }; + this.renderParticipantsList = () => { + const { + filter, + raisedHandPeers + } = this.state; + return JSX_("div", { + className: ` + participants-list + ${filter === this.FILTER.IN_CALL ? '' : 'with-chat-participants'} + ${this.props.guest ? 'guest' : ''} + ` + }, JSX_(perfectScrollbar.O, { + filter, + raisedHandPeers, + options: { + 'suppressScrollX': true + } + }, filter === this.FILTER.IN_CALL ? this.getCallParticipants() : this.getChatParticipants())); + }; + this.renderMuteAllControl = () => { + const { + allPeersMuted + } = this.state; + const simpletip = { + label: l.mute_all_tooltip, + position: 'top', + className: 'theme-dark-forced' + }; + return JSX_(meetings_button.A, { + ref: this.muteRef, + simpletip: allPeersMuted ? null : simpletip, + className: ` + mega-button + action + ${this.NAMESPACE}-mute + ${allPeersMuted ? 'disabled' : ''} + `, + icon: "sprite-fm-mono icon-mic-off-thin-outline", + onClick: () => { + let _this$muteRef, _muteRef$buttonRef; + const muteRef = (_this$muteRef = this.muteRef) == null ? void 0 : _this$muteRef.current; + const buttonRef = (_muteRef$buttonRef = muteRef.buttonRef) == null ? void 0 : _muteRef$buttonRef.current; + return allPeersMuted ? null : this.setState({ + allPeersMuted: true + }, () => { + this.props.call.sfuClient.mutePeer(); + ChatToast.quick(l.you_muted_all_peers); + if (buttonRef) { + $(buttonRef).trigger('simpletipClose'); + } + }); + } + }, allPeersMuted ? l.all_muted : l.mute_all); + }; + this.state.allPeersMuted = this.allPeersMuted; + } + componentWillUnmount() { + super.componentWillUnmount(); + ['onCallPeerJoined', 'onPeerAvChange'].map(event => this.props.chatRoom.off(`${event}.${this.NAMESPACE}`)); + } + componentDidMount() { + super.componentDidMount(); + this.props.chatRoom.rebind(`onCallPeerJoined.${this.NAMESPACE}`, (ev, userHandle) => { + const { + noResponsePeers, + ringingPeers + } = this.state; + this.setState({ + noResponsePeers: noResponsePeers.includes(userHandle) ? noResponsePeers.filter(h => h !== userHandle) : noResponsePeers, + ringingPeers: ringingPeers.includes(userHandle) ? ringingPeers.filter(h => h !== userHandle) : ringingPeers + }); + }).rebind(`onPeerAvChange.${this.NAMESPACE}`, () => this.isMounted() && this.setState({ + allPeersMuted: this.allPeersMuted + })); + } + render() { + const { + IN_CALL, + CHAT_PARTICIPANTS + } = this.FILTER; + const { + withInvite, + chatRoom, + peers, + onInviteToggle + } = this.props; + const { + filter + } = this.state; + return JSX_("div", { + ref: this.domRef, + className: this.NAMESPACE + }, chatRoom.type === 'private' ? null : JSX_("div", { + className: `${this.NAMESPACE}-nav` + }, JSX_(meetings_button.A, { + className: filter === IN_CALL ? 'active' : '', + onClick: () => this.setState({ + filter: IN_CALL + }) + }, l.call_heading_in_call), JSX_(meetings_button.A, { + className: filter === CHAT_PARTICIPANTS ? 'active' : '', + onClick: () => this.setState({ + filter: CHAT_PARTICIPANTS + }) + }, l.call_heading_not_in_call)), filter === IN_CALL ? JSX_(REaCt().Fragment, null, JSX_("div", { + className: `${this.NAMESPACE}-actions` + }, withInvite && JSX_(meetings_button.A, { + className: ` + mega-button + action + ${this.NAMESPACE}-invite + `, + icon: "sprite-fm-mono icon-user-plus-thin-outline", + onClick: onInviteToggle + }, l[8726]), chatRoom.iAmOperator() && this.renderMuteAllControl()), JSX_(Collapse, (0,esm_extends.A)({}, this.props, { + filter, + heading: l[16217], + badge: (peers == null ? void 0 : peers.length) + 1 + }), this.renderParticipantsList())) : this.renderParticipantsList()); + } +} +;// ./js/chat/ui/meetings/guest.jsx + + +class Guest extends REaCt().Component { + constructor(...args) { + super(...args); + this.state = { + copy: '' + }; + } + componentDidMount() { + this.setState({ + copy: `${l.free_storage_info__call.replace('%s', bytesToSize(mega.bstrg, 0))}` + }); + } + render() { + const { + copy + } = this.state; + return JSX_("div", { + className: "guest-register" + }, JSX_("div", { + className: "guest-register-content" + }, JSX_(meetings_button.A, { + className: "close-guest-register", + icon: "icon-close-component", + onClick: this.props.onGuestClose + }, JSX_("span", null, l[148])), JSX_("div", null, JSX_("i", { + className: "sprite-fm-illustration-wide registration" + }), JSX_("span", null, copy)), JSX_(meetings_button.A, { + className: "mega-button positive register-button", + onClick: () => loadSubPage('register') + }, l.sign_up_btn))); + } +} +;// ./js/chat/ui/meetings/sidebar.jsx + + + + + + + + +const inviteAllowed = chatRoom => { + if (chatRoom) { + return chatRoom.type !== 'private' && !!(chatRoom.options[MCO_FLAGS.OPEN_INVITE] || (0,utils.Cy)(chatRoom, u_handle) || chatRoom.publicLink); + } + return false; +}; +class Sidebar extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + this.historyPanel = null; + this.renderHead = ({ + title, + children + }) => { + return JSX_("div", { + className: "sidebar-head" + }, JSX_(meetings_button.A, { + simpletip: { + label: l.close_sidebar, + className: 'theme-dark-forced' + }, + className: "mega-button action small left", + icon: "icon-collapse-right", + onClick: this.props.onSidebarClose + }, JSX_("span", null, l.close_sidebar)), JSX_("h2", null, title), children || null); + }; + this.renderParticipantsView = () => { + const { + call, + mode, + peers, + initialCallRinging, + chatRoom, + guest, + recorderCid, + raisedHandPeers, + onInviteToggle, + onCallMinimize, + onSpeakerChange, + onModeChange + } = this.props; + const withInvite = inviteAllowed(chatRoom); + return JSX_(REaCt().Fragment, null, this.renderHead({ + title: l[16217] + }), JSX_(Participants, { + withInvite, + call, + mode, + peers, + initialCallRinging, + chatRoom, + guest, + recorderCid, + raisedHandPeers, + onInviteToggle, + onCallMinimize, + onSpeakerChange, + onModeChange + })); + }; + this.renderChatView = () => { + const { + chatRoom, + typingAreaText, + onDeleteMessage, + onTypingAreaChanged + } = this.props; + return JSX_(REaCt().Fragment, null, this.renderHead({ + title: l.chats + }), JSX_(historyPanel.A, { + ref: ref => { + this.historyPanel = ref; + }, + chatRoom, + className: "in-call", + onDeleteClicked: onDeleteMessage + }), JSX_(composedTextArea.A, { + chatRoom, + parent: this, + containerRef: this.domRef, + typingAreaText, + onTypingAreaChanged + })); + }; + } + render() { + const { + view, + guest, + onGuestClose + } = this.props; + return JSX_("div", { + className: "sidebar-wrapper theme-dark-forced" + }, JSX_("div", { + ref: this.domRef, + className: ` + sidebar + ${view === utils.gR.CHAT ? 'chat-opened' : 'theme-dark-forced'} + ` + }, view === utils.gR.PARTICIPANTS && this.renderParticipantsView(), view === utils.gR.CHAT && this.renderChatView(), guest && view !== utils.gR.CHAT && JSX_(Guest, { + onGuestClose + }))); + } +} +;// ./js/chat/ui/meetings/workflow/invite/search.jsx +let _Search; + + +class Search extends REaCt().Component { + render() { + const { + value, + placeholder, + onChange + } = this.props; + return JSX_("div", { + className: `${Invite.NAMESPACE}-field` + }, JSX_("i", { + className: "sprite-fm-mono icon-preview-reveal" + }), JSX_("input", { + type: "text", + autoFocus: true, + placeholder: l[23750].replace('[X]', placeholder), + ref: Search.inputRef, + value, + onChange + })); + } +} +_Search = Search; +Search.inputRef = REaCt().createRef(); +Search.focus = () => { + return _Search.inputRef && _Search.inputRef.current && _Search.inputRef.current.focus(); +}; +;// ./js/chat/ui/meetings/workflow/invite/footer.jsx + + +const Footer = ({ + selected, + onClose, + onAdd +}) => { + return JSX_("footer", null, JSX_("div", { + className: "footer-container" + }, JSX_(meetings_button.A, { + className: "mega-button", + onClick: onClose + }, l.msg_dlg_cancel), JSX_(meetings_button.A, { + className: ` + mega-button + positive + ${selected.length > 0 ? '' : 'disabled'} + `, + onClick: onAdd + }, l.add))); +}; + const footer = Footer; +;// ./js/chat/ui/meetings/workflow/invite/nil.jsx + + +const Nil = () => { + return JSX_("div", { + className: `${Invite.NAMESPACE}-nil` + }, JSX_("div", { + className: "fm-empty-contacts-bg" + }), JSX_("h2", null, HAS_CONTACTS() ? l[8674] : l[784])); +}; + const nil = Nil; +// EXTERNAL MODULE: ./js/chat/ui/link.jsx +const ui_link = REQ_(4649); +;// ./js/chat/ui/meetings/workflow/invite/invite.jsx + + + + + + + + + + + +const HAS_CONTACTS = () => { + const keys = M.u.keys(); + for (let i = 0; i < keys.length; i++) { + if (M.u[keys[i]].c === 1) { + return true; + } + } +}; +class Invite extends REaCt().Component { + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.wrapperRef = REaCt().createRef(); + this.state = { + loading: true, + value: '', + searching: false, + contacts: [], + contactsInitial: [], + frequents: [], + frequentsInitial: [], + selected: [], + excluded: [], + input: false + }; + this.getSortedContactsList = (frequents, excluded) => { + frequents = frequents || this.state.frequents; + excluded = excluded || this.state.excluded; + const filteredContacts = []; + (this.props.contacts || M.u).forEach(contact => { + if (contact.c === 1 && !frequents.includes(contact.u) && !excluded.includes(contact.u)) { + filteredContacts.push(contact); + } + }); + const sortFn = M.getSortByNameFn2(1); + filteredContacts.sort((a, b) => sortFn(a, b)); + return filteredContacts; + }; + this.doMatch = (value, collection) => { + value = value.toLowerCase(); + return collection.filter(contact => { + contact = typeof contact === 'string' ? M.getUserByHandle(contact) : contact; + const name = M.getNameByHandle(contact.u || contact).toLowerCase(); + const email = contact.m && contact.m.toLowerCase(); + return name.includes(value) || email.includes(value); + }); + }; + this.handleSearch = ev => { + const { + value + } = ev.target; + const searching = value.length >= 2; + const frequents = searching ? this.doMatch(value, this.state.frequentsInitial) : this.state.frequentsInitial; + const contacts = searching ? this.doMatch(value, this.state.contactsInitial) : this.state.contactsInitial; + this.setState({ + value, + searching, + frequents, + contacts + }, () => { + const wrapperRef = this.wrapperRef && this.wrapperRef.current; + if (wrapperRef && searching) { + wrapperRef.reinitialise(); + wrapperRef.scrollToY(0); + } + }); + }; + this.handleSelect = userHandle => { + this.setState(state => ({ + selected: state.selected.includes(userHandle) ? state.selected.filter(c => c !== userHandle) : [...state.selected, userHandle] + }), () => Search.focus()); + }; + this.handleAdd = () => { + const { + selected + } = this.state; + const { + call, + chatRoom, + onClose + } = this.props; + if (selected.length > 0) { + if (chatRoom.options.w) { + let _call$sfuClient; + call == null || (_call$sfuClient = call.sfuClient) == null || _call$sfuClient.wrAllowJoin(selected); + } + chatRoom == null || chatRoom.trigger('onAddUserRequest', [selected]); + onClose == null || onClose(); + } + }; + this.getFrequentContacts = () => megaChat.getFrequentContacts().then(response => { + if (!this.domRef.current) { + return; + } + const frequents = []; + const maxFreq = Math.max(response.length - ui_contacts.lO, 0); + for (let i = response.length - 1; i >= maxFreq; i--) { + const contact = response[i]; + if (!this.state.excluded.includes(contact.userId)) { + frequents.push(contact.userId); + } + } + this.setState({ + frequents, + frequentsInitial: frequents, + contacts: this.getSortedContactsList(frequents), + loading: false + }); + }); + this.getFilteredFrequents = () => { + const { + frequents, + selected + } = this.state; + if (frequents.length === 0) { + return false; + } + return frequents.map(userHandle => { + return JSX_(ui_contacts.nB, { + key: userHandle, + contact: M.u[userHandle], + chatRoom: false, + className: ` + contacts-search + short + ${selected.includes(userHandle) ? 'selected' : ''} + `, + noContextButton: true, + noContextMenu: true, + selectable: true, + onClick: () => this.handleSelect(userHandle) + }); + }); + }; + this.getFilteredContacts = () => { + const { + contacts, + frequents, + excluded, + selected + } = this.state; + const $$CONTACTS = []; + for (let i = 0; i < contacts.length; i++) { + const contact = contacts[i]; + const { + u: userHandle + } = contact; + if (!frequents.includes(userHandle) && !excluded.includes(userHandle)) { + $$CONTACTS.push(JSX_(ui_contacts.nB, { + key: userHandle, + contact, + chatRoom: false, + className: ` + contacts-search + short + ${selected.includes(userHandle) ? 'selected' : ''} + `, + noContextButton: true, + noContextMenu: true, + selectable: true, + onClick: () => this.handleSelect(userHandle) + })); + } + } + return $$CONTACTS.length === 0 ? false : $$CONTACTS; + }; + this.renderContent = () => { + const frequentContacts = this.getFilteredFrequents(); + const contactsFiltered = this.getFilteredContacts(); + if (HAS_CONTACTS()) { + const { + contacts, + frequents + } = this.state; + const $$RESULT_TABLE = (header, children) => JSX_("div", { + className: "contacts-search-subsection" + }, JSX_("div", { + className: "contacts-list-header" + }, header), JSX_("div", { + className: "contacts-search-list" + }, children)); + if (frequents.length === 0 && contacts.length === 0) { + return JSX_(nil, null); + } + return JSX_(perfectScrollbar.O, { + ref: this.wrapperRef, + options: { + 'suppressScrollX': true + } + }, frequentContacts ? $$RESULT_TABLE(l[20141], frequentContacts) : '', contactsFiltered ? $$RESULT_TABLE(l[165], contactsFiltered) : ''); + } + return JSX_(nil, null); + }; + this.renderLoading = () => { + return JSX_("div", { + className: `${Invite.NAMESPACE}-loading` + }, JSX_("h2", null, l[1456])); + }; + this.state.excluded = this.props.chatRoom ? this.props.chatRoom.getParticipantsExceptMe() : []; + this.state.contacts = this.state.contactsInitial = this.getSortedContactsList(); + } + componentDidMount() { + this.getFrequentContacts(); + } + render() { + const { + NAMESPACE + } = Invite; + const { + value, + loading, + selected, + contactsInitial + } = this.state; + const { + chatRoom, + call, + onClose + } = this.props; + const { + isMeeting, + publicLink + } = chatRoom || {}; + const callPartsLength = chatRoom.getCallParticipants().length; + return JSX_(modalDialogs.A.ModalDialog, (0,esm_extends.A)({}, this.state, { + ref: this.domRef, + name: NAMESPACE, + className: ` + ${NAMESPACE} + dialog-template-tool + `, + callPartsLength, + hideOverlay: true, + onClose + }), JSX_("div", { + className: `${NAMESPACE}-head` + }, JSX_("h2", null, isMeeting ? l.invite_participants : l[8726]), isMeeting && publicLink && JSX_(REaCt().Fragment, null, JSX_("p", null, l.copy_and_share), JSX_("div", { + className: "link-input-container" + }, JSX_(meetings_button.A, { + className: `mega-button large positive ${publicLink ? '' : 'disabled'}`, + onClick: () => publicLink && copyToClipboard(`${getBaseUrl()}/${publicLink}`, l[371]) + }, !publicLink ? l[7006] : l[1394]))), HAS_CONTACTS() && JSX_(Search, { + value, + placeholder: contactsInitial.length, + onChange: this.handleSearch + }), call.sfuClient.callLimits && call.sfuClient.callLimits.usr && callPartsLength >= call.sfuClient.callLimits.usr && JSX_("div", { + className: `${NAMESPACE}-user-limit-banner` + }, call.organiser === u_handle ? (0,ui_utils.lI)(l.invite_limit_banner_organiser, '[A]', ui_link.A, { + className: 'invite-limit-link', + onClick() { + window.open(`${getBaseUrl()}/pro`, '_blank', 'noopener,noreferrer'); + eventlog(500260); + } + }) : l.invite_limit_banner_host)), JSX_("div", { + className: "fm-dialog-body" + }, JSX_("div", { + className: `${NAMESPACE}-contacts` + }, loading ? this.renderLoading() : this.renderContent())), JSX_(footer, { + selected, + onAdd: this.handleAdd, + onClose + })); + } +} +Invite.NAMESPACE = 'invite-meeting'; +;// ./js/chat/ui/meetings/workflow/ephemeral.jsx + + + +const Ephemeral = ({ + ephemeralAccounts, + onClose +}) => { + const ephemeralAccount = ephemeralAccounts && ephemeralAccounts[ephemeralAccounts.length - 1]; + return JSX_(modalDialogs.A.ModalDialog, { + name: "ephemeral-dialog", + dialogType: "message", + icon: "sprite-fm-uni icon-info", + title: JSX_(ui_contacts.uA, { + emoji: true, + contact: M.u[ephemeralAccount] + }), + noCloseOnClickOutside: true, + buttons: [{ + key: 'ok', + label: l[81], + onClick: onClose + }], + onClose + }, JSX_("p", null, l.ephemeral_info)); +}; + const workflow_ephemeral = Ephemeral; +;// ./js/chat/ui/meetings/offline.jsx + + +const Offline = ({ + onCallEnd, + onClose +}) => { + return JSX_(modalDialogs.A.ModalDialog, { + name: "reconnect-dialog", + dialogType: "message", + icon: "sprite-fm-uni icon-warning", + title: l.no_internet, + noCloseOnClickOutside: true, + buttons: [{ + key: 'ok', + label: l.msg_dlg_cancel, + onClick: onClose + }, { + key: 'leave', + label: l[5883], + className: 'negative', + onClick: onCallEnd + }], + onClose + }, JSX_("p", null, l.no_connection)); +}; + const meetings_offline = Offline; +// EXTERNAL MODULE: ./js/chat/ui/conversationpanel.jsx + 10 modules +const conversationpanel = REQ_(5677); +;// ./js/chat/ui/meetings/streamControls.jsx + + + + + + + + + + +class StreamControls extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + this.endContainerRef = REaCt().createRef(); + this.endButtonRef = REaCt().createRef(); + this.SIMPLETIP = { + position: 'top', + offset: 8, + className: 'theme-dark-forced' + }; + this.state = { + endCallOptions: false, + endCallPending: false, + devices: {}, + audioSelectDropdown: false, + videoSelectDropdown: false, + loading: false, + muteSpeak: false + }; + this.LeaveButton = (0,hostsObserver.C)(({ + hasHost, + chatRoom, + confirmLeave, + onLeave + }) => { + const doLeave = () => hasHost(chatRoom.call ? chatRoom.call.peers.map(a => a.userHandle) : []) ? onLeave() : confirmLeave({ + title: l.assign_host_leave_call, + body: l.assign_host_leave_call_details, + cta: l.assign_host_button, + altCta: l.leave_anyway + }); + return JSX_(meetings_button.A, { + className: "mega-button", + onClick: () => { + const { + recorderCid, + call, + onRecordingToggle + } = this.props; + return recorderCid && recorderCid === call.sfuClient.cid ? (0,utils.sX)(doLeave, onRecordingToggle) : doLeave(); + } + }, JSX_("span", null, l.leave)); + }); + this.setActiveElement = forced => this.props.setActiveElement(forced || this.state.audioSelectDropdown || this.state.videoSelectDropdown || this.state.endCallOptions); + this.handleMousedown = ({ + target + }) => { + if (this.isMounted()) { + const { + audioSelectDropdown, + videoSelectDropdown, + endCallOptions + } = this.state; + return (audioSelectDropdown || videoSelectDropdown || endCallOptions) && ['audio-sources', 'video-sources', 'meetings-end-options'].some(selector => { + let _document$querySelect; + return (_document$querySelect = document.querySelector(`.${selector}`)) == null ? void 0 : _document$querySelect.contains(target); + }) ? 0x4B1D : this.setState({ + audioSelectDropdown: false, + videoSelectDropdown: false, + endCallOptions: false + }, this.setActiveElement); + } + }; + this.renderDebug = () => { + return JSX_("div", { + className: "stream-debug", + style: { + position: 'absolute', + left: 25, + bottom: 36, + display: 'flex', + alignItems: 'center', + color: 'tomato' + } + }, JSX_(meetings_button.A, { + className: "mega-button round small theme-dark-forced positive", + simpletip: { + ...this.SIMPLETIP, + label: 'Add Stream' + }, + onClick: () => this.props.onStreamToggle(utils.hK.ADD) + }, JSX_("span", null, l.add)), JSX_(meetings_button.A, { + className: "mega-button round small theme-dark-forced negative", + simpletip: { + ...this.SIMPLETIP, + label: 'Remove Stream' + }, + onClick: () => this.props.peers.length > 1 && this.props.onStreamToggle(utils.hK.REMOVE) + }, JSX_("span", null, l[83])), JSX_("span", null, this.props.peers.length + 1)); + }; + this.renderEndCallOptions = () => { + let _this$endContainerRef; + const { + call, + chatRoom, + recorderCid, + onRecordingToggle, + onCallEnd + } = this.props; + const { + endCallOptions, + endCallPending + } = this.state; + const doEnd = () => this.setState({ + endCallPending: true + }, () => chatRoom.endCallForAll()); + const endContainerRef = (_this$endContainerRef = this.endContainerRef) == null ? void 0 : _this$endContainerRef.current; + return JSX_("div", (0,esm_extends.A)({}, endCallOptions && { + style: (({ + left, + top + }) => ({ + left, + top + }))(endContainerRef.getBoundingClientRect()) + }, { + className: ` + meetings-end-options + theme-dark-forced + ${endCallOptions ? '' : 'hidden'} + ` + }), JSX_("div", { + className: "meetings-end-options-content" + }, JSX_(this.LeaveButton, { + chatRoom, + recorderCid, + participants: chatRoom.getCallParticipants(), + onLeave: onCallEnd, + onConfirmDenied: onCallEnd + }), JSX_(meetings_button.A, { + className: ` + mega-button + positive + ${endCallPending ? 'disabled' : ''} + `, + onClick: () => { + if (recorderCid && recorderCid === call.sfuClient.cid) { + return renderEndConfirm(doEnd, onRecordingToggle); + } + return doEnd(); + } + }, JSX_("span", null, l.end_for_all)))); + }; + this.renderEndCall = () => { + const { + call, + chatRoom, + peers, + recorderCid, + onRecordingToggle, + onCallEnd + } = this.props; + return JSX_("div", { + ref: this.endContainerRef, + className: "end-call-container", + onClick: () => { + if (chatRoom.type !== 'private' && peers.length && (0,utils.Cy)(chatRoom, u_handle)) { + return this.setState(state => ({ + endCallOptions: !state.endCallOptions + }), () => { + if (this.endButtonRef) { + $(this.endButtonRef.current).trigger('simpletipClose'); + } + this.setActiveElement(); + }); + } + if (recorderCid && recorderCid === call.sfuClient.cid) { + return chatRoom.type === 'private' ? renderEndConfirm(onCallEnd, onRecordingToggle) : (0,utils.sX)(onCallEnd, onRecordingToggle); + } + return onCallEnd(); + } + }, JSX_(ui_utils.Ay.RenderTo, { + element: document.body + }, this.renderEndCallOptions()), JSX_(meetings_button.A, { + simpletip: { + ...this.SIMPLETIP, + label: l[5884] + }, + className: "mega-button theme-dark-forced round negative end-call call-action", + icon: "icon-phone-02", + didMount: button => { + this.endButtonRef = button.buttonRef; + } + }), JSX_("span", null, l.end_button)); + }; + this.renderSourceOpener = ({ + type, + eventId + }) => { + return JSX_("div", { + className: ` + input-source-opener + button + ${this.state[type] ? 'active-dropdown' : ''} + `, + onClick: async ev => { + ev.stopPropagation(); + this.setState(() => ({ + loading: true + }), async () => { + const devices = await this.updateMediaDevices(); + const updated = JSON.stringify(devices) !== JSON.stringify(this.state.devices); + this.setState(state => ({ + loading: false, + audioSelectDropdown: false, + videoSelectDropdown: false, + devices: updated ? devices : this.state.devices, + [type]: !state[type] + }), () => { + const { + audioSelectDropdown, + videoSelectDropdown + } = this.state; + this.props.setActiveElement(audioSelectDropdown || videoSelectDropdown); + eventlog(eventId); + }); + }); + } + }, JSX_("i", { + className: "sprite-fm-mono icon-arrow-up" + })); + }; + this.handleDeviceChange = () => { + this.micDefaultRenamed = false; + this.updateMediaDevices().always(devices => { + let _sfuClient$localAudio, _oldDevices$audioIn; + if (!this.isMounted()) { + return; + } + const { + devices: oldDevices + } = this.state; + const { + sfuClient, + av + } = this.props.call; + if (av & Av.Audio && !SfuClient.micDeviceId && ((_sfuClient$localAudio = sfuClient.localAudioTrack()) == null ? void 0 : _sfuClient$localAudio.getCapabilities().deviceId) === 'default' && oldDevices != null && (_oldDevices$audioIn = oldDevices.audioIn) != null && _oldDevices$audioIn.default && devices.audioIn.default && oldDevices.audioIn.default !== devices.audioIn.default) { + for (const [key, value] of Object.entries(devices.audioIn)) { + if (key !== 'default' && devices.audioIn.default.indexOf(value) > -1) { + sfuClient.setMicDevice(key).then(() => SfuClient.persistMicDevice(null)); + break; + } + } + } + this.setState({ + devices, + audioSelectDropdown: false, + videoSelectDropdown: false + }, this.setActiveElement); + }); + }; + this.renderOnboardingRaise = () => { + const { + chatRoom, + onOnboardingRaiseDismiss + } = this.props; + return JSX_("div", { + className: "meetings-call-onboarding" + }, JSX_("div", { + className: "mega-dialog mega-onboarding-dialog dialog-template-message onboarding-raise", + id: "ob-dialog", + role: "dialog", + "aria-labelledby": "ob-dialog-title", + "aria-modal": "true" + }, JSX_("i", { + className: "sprite-fm-mono icon-tooltip-arrow tooltip-arrow bottom", + id: "ob-dialog-arrow" + }), JSX_("header", null, JSX_("div", null, JSX_("h2", { + id: "ob-dialog-title" + }, l.raise_onboarding_title), JSX_("p", { + id: "ob-dialog-text" + }, chatRoom.isMeeting ? l.raise_onboarding_body : l.raise_onboarding_group_body))), JSX_("footer", null, JSX_("div", { + className: "footer-container" + }, JSX_("button", { + className: "mega-button js-next small theme-light-forced", + onClick: onOnboardingRaiseDismiss + }, JSX_("span", null, l.ok_button)))))); + }; + this.renderRaiseButton = () => { + const { + call, + raisedHandPeers, + onboardingRaise + } = this.props; + const isOnHold = call.av & Av.onHold; + const hasRaisedHand = raisedHandPeers.includes(u_handle); + return JSX_("li", { + className: isOnHold ? 'disabled' : '' + }, onboardingRaise && this.renderOnboardingRaise(), JSX_(meetings_button.A, { + className: ` + mega-button + theme-light-forced + call-action + round + ${isOnHold ? 'disabled' : ''} + ${hasRaisedHand ? 'with-fill' : ''} + `, + icon: "icon-raise-hand", + onClick: isOnHold ? null : () => { + if (hasRaisedHand) { + call.sfuClient.lowerHand(); + eventlog(500311); + return; + } + call.sfuClient.raiseHand(); + eventlog(500249); + } + }), JSX_("span", null, l.raise_button)); + }; + } + renderSoundDropdown() { + const { + call + } = this.props; + const { + micDeviceId, + audioOutDeviceId + } = SfuClient; + const { + audioIn = {}, + audioOut = {} + } = this.state.devices; + let selectedIn; + const inTrack = call.sfuClient.localAudioTrack(); + if (inTrack) { + const { + deviceId + } = inTrack.getCapabilities(); + selectedIn = deviceId in audioIn ? deviceId : 'default'; + if (deviceId === 'default' && inTrack.label !== audioIn.default) { + this.micDefaultRenamed = inTrack.label; + } + } else if (micDeviceId) { + selectedIn = micDeviceId in audioIn ? micDeviceId : 'default'; + } else { + selectedIn = 'default'; + } + if (this.micDefaultRenamed) { + audioIn.default = this.micDefaultRenamed; + } + let selectedOut; + let peerPlayer; + if (call.sfuClient.peers.size) { + peerPlayer = call.sfuClient.peers.values().next().audioPlayer; + } + if (peerPlayer && peerPlayer.playerElem && peerPlayer.playerElem.sinkId) { + const { + sinkId + } = peerPlayer.playerElem; + selectedOut = sinkId in audioOut ? sinkId : 'default'; + } else if (audioOutDeviceId) { + selectedOut = audioOutDeviceId in audioOut ? audioOutDeviceId : 'default'; + } else { + selectedOut = 'default'; + } + const mics = Object.entries(audioIn).map(([id, name]) => { + return JSX_(dropdowns.tJ, { + key: id, + onClick: () => { + call.sfuClient.setMicDevice(id === 'default' ? null : id); + this.setState({ + audioSelectDropdown: false + }, this.setActiveElement); + } + }, JSX_(REaCt().Fragment, null, JSX_("div", { + className: "av-device-name" + }, name), selectedIn === id && JSX_("i", { + className: "sprite-fm-mono icon-check-small-regular-outline" + }))); + }); + const speakers = Object.entries(audioOut).map(([id, name]) => { + return JSX_(dropdowns.tJ, { + key: id, + onClick: () => { + Promise.resolve(call.sfuClient.setAudioOutDevice(id === 'default' ? null : id)).catch(dump); + this.setState({ + audioSelectDropdown: false + }, this.setActiveElement); + } + }, JSX_(REaCt().Fragment, null, JSX_("div", { + className: "av-device-name" + }, name), selectedOut === id && JSX_("i", { + className: "sprite-fm-mono icon-check-small-regular-outline" + }))); + }); + return JSX_(dropdowns.ms, { + className: "input-sources audio-sources theme-dark-forced", + active: true, + noArrow: true, + positionMy: "center top", + positionAt: "center bottom", + horizOffset: -50, + vertOffset: 16, + closeDropdown: () => this.setState({ + audioSelectDropdown: false + }, this.setActiveElement) + }, JSX_("div", { + className: "source-label" + }, l.microphone), mics.length ? mics : JSX_(dropdowns.tJ, { + label: l.no_mics + }), JSX_("hr", null), JSX_("div", { + className: "source-label" + }, l.speaker), speakers.length ? speakers : JSX_(dropdowns.tJ, { + label: l.no_speakers + }), JSX_("hr", null), JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-volume-max-small-regular-outline", + label: l.test_speaker, + disabled: speakers.length === 0, + onClick: () => { + delay('call-test-speaker', () => { + this.testAudioOut().catch(ex => { + console.error('Failed to test audio on the selected device', ex, audioOutDeviceId); + }); + }); + } + })); + } + renderVideoDropdown() { + const { + call + } = this.props; + const { + videoIn = {} + } = this.state.devices; + const { + camDeviceId + } = SfuClient; + let selectedCam; + if (call.sfuClient.localCameraTrack()) { + const { + deviceId + } = call.sfuClient.localCameraTrack().getCapabilities(); + selectedCam = deviceId in videoIn ? deviceId : 'default'; + } else if (camDeviceId) { + selectedCam = camDeviceId in videoIn ? camDeviceId : 'default'; + } else { + selectedCam = 'default'; + } + const cameras = Object.entries(videoIn).map(([id, name]) => { + return JSX_(dropdowns.tJ, { + key: id, + onClick: () => { + call.sfuClient.setCameraDevice(id === 'default' ? null : id); + this.setState({ + videoSelectDropdown: false + }, this.setActiveElement); + } + }, JSX_(REaCt().Fragment, null, JSX_("div", { + className: "av-device-name" + }, name), selectedCam === id && JSX_("i", { + className: "sprite-fm-mono icon-check-small-regular-outline" + }))); + }); + return JSX_(dropdowns.ms, { + className: "input-sources video-sources theme-dark-forced", + active: true, + noArrow: true, + positionMy: "center top", + positionAt: "center bottom", + horizOffset: -50, + vertOffset: 16, + closeDropdown: () => this.setState({ + videoSelectDropdown: false + }, this.setActiveElement) + }, JSX_("div", { + className: "source-label" + }, l.camera_button), cameras.length ? cameras : JSX_(dropdowns.tJ, { + label: l.no_cameras + })); + } + async updateMediaDevices() { + let devices = await SfuClient.enumMediaDevices().catch(dump); + devices = devices || { + audioIn: {}, + audioOut: {}, + videoIn: {} + }; + const removeEmptyDevices = devices => { + for (const key of Object.keys(devices)) { + if (!key || !devices[key]) { + delete devices[key]; + } + } + }; + removeEmptyDevices(devices.audioIn); + removeEmptyDevices(devices.audioOut); + removeEmptyDevices(devices.videoIn); + if (devices.audioIn.communications) { + delete devices.audioIn.communications; + } + return devices; + } + async testAudioOut() { + if (!SfuClient.audioOutDeviceId) { + return megaChat.playSound(megaChat.SOUNDS.SPEAKER_TEST); + } + const currentDevices = await this.updateMediaDevices(); + if (currentDevices.audioOut && !(SfuClient.audioOutDeviceId in currentDevices.audioOut)) { + return megaChat.playSound(megaChat.SOUNDS.SPEAKER_TEST); + } + const ctx = new AudioContext({ + sinkId: SfuClient.audioOutDeviceId + }); + if (ctx.state !== 'running') { + throw new Error('The audio context failed to start'); + } + const soundBuffer = await megaChat.fetchSoundBuffer(megaChat.SOUNDS.SPEAKER_TEST); + const buffer = await ctx.decodeAudioData(soundBuffer); + const gain = ctx.createGain(); + const source = ctx.createBufferSource(); + source.buffer = buffer; + source.connect(gain); + gain.connect(ctx.destination); + gain.gain.value = 0.07; + source.start(); + } + componentWillUnmount() { + super.componentWillUnmount(); + document.removeEventListener('mousedown', this.handleMousedown); + navigator.mediaDevices.removeEventListener('devicechange', this.handleDeviceChange); + this.props.chatRoom.off(`onLocalSpeechDetected.${StreamControls.NAMESPACE}`); + } + componentDidMount() { + super.componentDidMount(); + document.addEventListener('mousedown', this.handleMousedown); + navigator.mediaDevices.addEventListener('devicechange', this.handleDeviceChange); + this.props.chatRoom.rebind(`onLocalSpeechDetected.${StreamControls.NAMESPACE}`, () => this.setState({ + muteSpeak: true + }, () => this.setActiveElement(true))); + } + render() { + const { + call, + signal, + chatRoom, + renderSignalWarning, + hasToRenderPermissionsWarning, + renderPermissionsWarning, + resetError, + blocked, + renderBlockedWarning, + onAudioClick, + onVideoClick, + onScreenSharingClick, + onHoldClick + } = this.props; + const { + audioSelectDropdown, + videoSelectDropdown, + muteSpeak + } = this.state; + const avFlags = call.av; + const isOnHold = avFlags & Av.onHold; + return JSX_(REaCt().Fragment, null, blocked && renderBlockedWarning(), JSX_("div", { + ref: this.domRef, + className: StreamControls.NAMESPACE + }, d && localStorage.callDebug ? this.renderDebug() : '', JSX_("ul", null, JSX_("li", { + className: ` + ${isOnHold ? 'disabled' : ''} + with-input-selector + `, + onClick: () => isOnHold ? null : this.setState({ + muteSpeak: false + }, () => { + resetError(Av.Audio); + onAudioClick(); + }) + }, muteSpeak && JSX_("div", { + className: "mic-muted-tip theme-light-forced", + onClick: ev => ev.stopPropagation() + }, JSX_("span", null, l.mic_still_muted), JSX_(meetings_button.A, { + className: "mic-muted-tip-btn", + onClick: () => { + this.setState({ + muteSpeak: false + }, () => { + this.setActiveElement(); + eventlog(500509); + }); + } + }, l[148]), JSX_("i", { + className: "sprite-fm-mono icon-tooltip-arrow tooltip-arrow bottom" + })), JSX_(meetings_button.A, { + className: ` + mega-button + theme-light-forced + call-action + round + ${isOnHold ? 'disabled' : ''} + ${avFlags & Av.Audio || isOnHold ? '' : 'with-fill'} + `, + icon: avFlags & Av.Audio ? 'icon-mic-thin-outline' : 'icon-mic-off-thin-outline' + }), JSX_("span", null, l.mic_button), signal ? null : renderSignalWarning(), hasToRenderPermissionsWarning(Av.Audio) ? renderPermissionsWarning(Av.Audio) : null, this.renderSourceOpener({ + type: 'audioSelectDropdown', + eventId: chatRoom.isMeeting ? 500299 : 500300 + })), audioSelectDropdown && JSX_("div", { + ref: this.audioDropdownRef + }, this.renderSoundDropdown()), JSX_("li", { + className: ` + ${isOnHold ? 'disabled' : ''} + with-input-selector + `, + onClick: () => { + if (isOnHold) { + return; + } + resetError(Av.Camera); + onVideoClick(); + } + }, JSX_(meetings_button.A, { + className: ` + mega-button + theme-light-forced + call-action + round + ${isOnHold ? 'disabled' : ''} + ${avFlags & Av.Camera || isOnHold ? '' : 'with-fill'} + `, + icon: avFlags & Av.Camera ? 'icon-video-thin-outline' : 'icon-video-off-thin-outline' + }), JSX_("span", null, l.camera_button), hasToRenderPermissionsWarning(Av.Camera) ? renderPermissionsWarning(Av.Camera) : null, this.renderSourceOpener({ + type: 'videoSelectDropdown', + eventId: chatRoom.isMeeting ? 500301 : 500302 + })), videoSelectDropdown && JSX_("div", { + ref: this.videoDropdownRef + }, this.renderVideoDropdown()), JSX_("li", { + className: isOnHold ? 'disabled' : '', + onClick: () => { + if (isOnHold) { + return; + } + resetError(Av.Screen); + onScreenSharingClick(); + if (chatRoom.isMeeting) { + eventlog(500303); + } else { + eventlog(500304); + } + } + }, JSX_(meetings_button.A, { + className: ` + mega-button + theme-light-forced + call-action + round + ${isOnHold ? 'disabled' : ''} + ${avFlags & Av.Screen ? 'with-fill' : ''} + `, + icon: avFlags & Av.Screen ? 'icon-monitor-off' : 'icon-monitor' + }), JSX_("span", null, avFlags & Av.Screen ? l.screenshare_stop_button : l.screenshare_button), hasToRenderPermissionsWarning(Av.Screen) ? renderPermissionsWarning(Av.Screen, this) : null), chatRoom.type === 'private' ? null : this.renderRaiseButton(), JSX_("li", { + onClick: onHoldClick + }, JSX_(meetings_button.A, { + className: ` + mega-button + theme-light-forced + call-action + round + ${isOnHold ? 'with-fill' : ''} + `, + icon: isOnHold ? 'icon-play-small-regular-outline' : 'icon-pause-small-regular-outline' + }), JSX_("span", null, isOnHold ? l.resume_call_button : l.hold_button)), JSX_("li", null, this.renderEndCall())))); + } +} +StreamControls.NAMESPACE = 'stream-controls'; + const streamControls = (0,mixins.Zz)(withMicObserver, permissionsObserver.$)(StreamControls); +;// ./js/chat/ui/meetings/sidebarControls.jsx + + + +const SidebarControls = ({ + npeers, + view, + sidebar, + call, + chatRoom, + onChatToggle, + onParticipantsToggle, + onInviteToggle +}) => { + const notifications = chatRoom.getUnreadCount(); + const isOnHold = !!((call == null ? void 0 : call.av) & Av.onHold); + const canInvite = chatRoom.type !== 'private' && !!(chatRoom.iAmOperator() || chatRoom.options[MCO_FLAGS.OPEN_INVITE] || chatRoom.publicLink); + return JSX_("div", { + className: "sidebar-controls" + }, JSX_("ul", { + className: isOnHold ? 'disabled' : '' + }, canInvite && JSX_("li", { + onClick: isOnHold ? null : onInviteToggle + }, JSX_(meetings_button.A, { + className: ` + mega-button + theme-dark-forced + call-action + round + `, + icon: "icon-user-plus-thin-outline" + }), JSX_("span", { + className: "control-label" + }, l[8726])), JSX_("li", { + onClick: isOnHold ? null : onChatToggle + }, JSX_(meetings_button.A, { + className: ` + mega-button + theme-dark-forced + call-action + round + ${sidebar && view === utils.gR.CHAT ? 'selected' : ''} + ${isOnHold ? 'disabled' : ''} + `, + icon: sidebar && view === utils.gR.CHAT ? 'icon-chat-filled' : 'icon-message-chat-circle-thin' + }), JSX_("span", { + className: "control-label" + }, l.chat_call_button), notifications > 0 && JSX_("span", { + className: "notification-badge notifications-count" + }, notifications > 9 ? '9+' : notifications)), JSX_("li", { + onClick: isOnHold ? null : onParticipantsToggle + }, JSX_(meetings_button.A, { + className: ` + mega-button + theme-dark-forced + call-action + round + ${sidebar && view === utils.gR.PARTICIPANTS ? 'selected' : ''} + ${isOnHold ? 'disabled' : ''} + `, + icon: sidebar && view === utils.gR.PARTICIPANTS ? 'icon-users-thin-solid' : 'icon-users-thin-outline' + }), JSX_("span", { + className: "control-label" + }, l.participants_call_button), JSX_("span", { + className: ` + notification-badge + participants-count + theme-dark-forced + ${npeers + 1 > 99 ? 'large' : ''} + ` + }, npeers + 1)))); +}; + const sidebarControls = SidebarControls; +;// ./js/chat/ui/meetings/call.jsx + + + + + + + + + + + + + + + + + + +const call_NAMESPACE = 'meetings-call'; +const MOUSE_OUT_DELAY = 2500; +class RecordingConsentDialog extends REaCt().Component { + componentWillUnmount() { + if ($.dialog && $.dialog === RecordingConsentDialog.dialogName) { + closeDialog(); + } + } + render() { + const { + peers, + recorderCid, + onCallEnd, + onClose + } = this.props; + const recordingPeer = peers[recorderCid]; + const recorderName = nicknames.getNickname(recordingPeer).substr(0, ChatToastIntegration.MAX_NAME_CHARS); + return JSX_(modalDialogs.A.ModalDialog, { + dialogName: RecordingConsentDialog.dialogName, + className: ` + mega-dialog + dialog-template-message + info + `, + stopKeyPropagation: true, + noCloseOnClickOutside: true + }, JSX_("header", null, JSX_("div", { + className: "graphic" + }, JSX_("i", { + className: "info sprite-fm-uni icon-info" + })), JSX_("div", { + className: "info-container" + }, JSX_("h3", { + id: "msgDialog-title" + }, l.call_recorded_heading), JSX_("p", { + className: "text" + }, JSX_(ui_utils.P9, null, l.call_recorded_body.replace('[A]', ``).replace('[/A]', ''))))), JSX_("footer", null, JSX_("div", { + className: "footer-container" + }, JSX_("div", { + className: "space-between" + }, JSX_(meetings_button.A, { + className: "mega-button", + onClick: onCallEnd + }, JSX_("span", null, l[5883])), JSX_(meetings_button.A, { + className: "mega-button positive", + onClick: () => { + onClose(); + ChatToast.quick(l.user_recording_toast.replace('%NAME', recorderName)); + } + }, JSX_("span", null, l.ok_button)))))); + } +} +RecordingConsentDialog.dialogName = `${"meetings-call"}-consent`; +class Call extends mixins.w9 { + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.recordingConsentDialog = `${call_NAMESPACE}-consent`; + this.ephemeralAddListener = undefined; + this.delayProcID = null; + this.pCallTimer = null; + this.offlineDelayed = undefined; + this.callStartTimeout = undefined; + this.flagMap = attribCache.bitMapsManager.exists('obv4') ? attribCache.bitMapsManager.get('obv4') : undefined; + this.timeoutBannerRef = REaCt().createRef(); + this.state = { + mode: undefined, + view: utils.gR.PARTICIPANTS, + sidebar: false, + forcedLocal: false, + hovered: false, + invite: false, + ephemeral: false, + offline: false, + ephemeralAccounts: [], + everHadPeers: false, + guest: (0,utils.P)(), + waitingRoomPeers: [], + raisedHandPeers: [], + raisedHandToast: false, + initialCallRinging: false, + onboardingUI: false, + onboardingRecording: false, + onboardingRaise: false, + recorderCid: undefined, + recordingConsentDialog: false, + recordingConsented: false, + recordingActivePeer: undefined, + recordingTooltip: false, + invitePanel: false, + presenterThumbSelected: false, + timeoutBanner: false, + showTimeoutUpgrade: false, + activeElement: false + }; + this.handleRetryTimeout = () => { + const { + call, + chatRoom + } = this.props; + if ((call == null ? void 0 : call.sfuClient.connState) === SfuClient.ConnState.kDisconnectedRetrying) { + this.handleCallEnd(); + chatRoom.trigger('onRetryTimeout'); + megaChat.playSound(megaChat.SOUNDS.CALL_END); + } + }; + this.handleCallOnline = () => { + if (this.pCallTimer) { + this.pCallTimer.abort(); + this.pCallTimer = null; + } + this.setState({ + offline: false, + raisedHandPeers: [...this.props.call.sfuClient.raisedHands] + }); + }; + this.customIsEventuallyVisible = () => true; + this.renderRaisedHandToast = () => { + const { + raisedHandPeers + } = this.state; + window.toaster.main.hideAll(); + toaster.main.show({ + buttons: [{ + text: l[16797], + onClick: () => this.setState({ + sidebar: true, + view: utils.gR.PARTICIPANTS, + raisedHandToast: false + }, () => window.toaster.main.hideAll()) + }], + onClose: () => this.setState({ + raisedHandToast: false + }, () => window.toaster.main.hideAll()), + classes: ['theme-dark-forced', 'call-toast'], + icons: ['sprite-fm-uni icon-raise-hand'], + timeout: 0, + content: (() => { + const peerName = M.getNameByHandle(raisedHandPeers[0]); + const peersCount = raisedHandPeers.length; + const withCurrentPeer = raisedHandPeers.includes(u_handle); + const CONTENT = { + 1: () => l.raise_peer_raised.replace('%s', peerName), + 2: () => { + const message = withCurrentPeer ? l.raise_self_peers_raised : l.raise_two_raised; + return mega.icu.format(message, peersCount - 1).replace('%s', peerName); + }, + rest: () => { + const message = withCurrentPeer ? l.raise_self_peers_raised : l.raise_peers_raised; + return mega.icu.format(message, withCurrentPeer ? peersCount - 1 : peersCount); + } + }; + return (CONTENT[peersCount] || CONTENT.rest)(); + })() + }); + }; + this.bindCallEvents = () => { + const { + chatRoom + } = this.props; + chatRoom.rebind(`onCallPeerLeft.${call_NAMESPACE}`, (ev, { + userHandle, + clientId + }) => { + const { + minimized, + peers, + call, + chatRoom + } = this.props; + if (clientId === this.state.recorderCid) { + chatRoom.trigger('onRecordingStopped', { + userHandle, + clientId + }); + } + if (minimized) { + this.setState({ + mode: peers.length === 0 ? utils.g.THUMBNAIL : utils.g.MINI + }, () => { + call.setViewMode(this.state.mode); + }); + } + }); + chatRoom.rebind(`onCallPeerJoined.${call_NAMESPACE}`, () => { + const { + minimized, + peers, + call + } = this.props; + if (minimized) { + this.setState({ + mode: peers.length === 0 ? utils.g.THUMBNAIL : utils.g.MINI + }, () => { + call.setViewMode(this.state.mode); + }); + } + if (call.hasOtherParticipant()) { + if (!this.state.everHadPeers) { + this.setState({ + everHadPeers: true + }); + } + clearTimeout(this.callStartTimeout); + } + }); + chatRoom.rebind(`onCallLeft.${call_NAMESPACE}`, () => this.props.minimized && this.props.onCallEnd()); + chatRoom.rebind(`wrOnUsersEntered.${call_NAMESPACE}`, (ev, users) => Object.entries(users).forEach(([handle, host]) => { + return host || this.state.waitingRoomPeers.includes(handle) ? null : this.isMounted() && this.setState({ + waitingRoomPeers: [...this.state.waitingRoomPeers, handle] + }, () => { + const { + waitingRoomPeers + } = this.state; + if (waitingRoomPeers && waitingRoomPeers.length === 1) { + megaChat.playSound(megaChat.SOUNDS.CALL_JOIN_WAITING); + } + mBroadcaster.sendMessage('meetings:peersWaiting', waitingRoomPeers); + }); + })); + const usrwr = (e, users) => { + users = typeof users === 'string' ? [users] : users; + return this.isMounted() && this.setState({ + waitingRoomPeers: this.state.waitingRoomPeers.filter(h => !users.includes(h)) + }, () => mBroadcaster.sendMessage('meetings:peersWaiting', this.state.waitingRoomPeers)); + }; + chatRoom.rebind(`wrOnUserLeft.${call_NAMESPACE}`, usrwr); + chatRoom.rebind(`wrOnUsersAllow.${call_NAMESPACE}`, usrwr); + chatRoom.rebind(`wrOnUserDump.${call_NAMESPACE}`, (ev, users) => Object.entries(users).forEach(([handle, host]) => { + return host || this.state.waitingRoomPeers.includes(handle) ? null : this.isMounted() && this.setState({ + waitingRoomPeers: [...this.state.waitingRoomPeers, handle] + }); + })); + chatRoom.rebind(`onRecordingStarted.${call_NAMESPACE}`, (ev, { + userHandle, + clientId + }) => { + if (!this.state.recorderCid) { + return this.state.recordingConsented ? this.setState({ + recorderCid: clientId + }, () => { + ChatToast.quick(l.user_recording_toast.replace('%NAME', nicknames.getNickname(userHandle).substr(0, ChatToastIntegration.MAX_NAME_CHARS))); + }) : (() => { + closeDialog(); + M.safeShowDialog(RecordingConsentDialog.dialogName, () => this.setState({ + recorderCid: clientId, + recordingConsentDialog: true + })); + })(); + } + }); + chatRoom.rebind(`onRecordingStopped.${call_NAMESPACE}`, (ev, { + userHandle, + clientId + }) => { + const { + recorderCid + } = this.state; + this.setState({ + recordingConsentDialog: false, + recorderCid: clientId === recorderCid ? false : recorderCid + }, () => window.sfuClient && clientId === recorderCid && ChatToast.quick(l.user_recording_nop_toast.replace('%NAME', nicknames.getNickname(userHandle).substr(0, ChatToastIntegration.MAX_NAME_CHARS)))); + }); + chatRoom.rebind(`onMutedBy.${call_NAMESPACE}`, (ev, { + cid + }) => { + megaChat.plugins.userHelper.getUserNickname(this.props.peers[cid]).catch(dump).always(name => { + ChatToast.quick(l.muted_by.replace('%NAME', name || '')); + }); + }); + chatRoom.rebind(`onCallEndTimeUpdated.${call_NAMESPACE}`, ({ + data + }) => { + this.setState({ + timeoutBanner: !!data, + showTimeoutUpgrade: this.props.call.organiser === u_handle && data - Date.now() >= 120e3 + }, () => { + if (this.state.timeoutBanner) { + this.timeoutBannerInterval = this.timeoutBannerInterval || setInterval(() => this.updateTimeoutDuration(), 1000); + } else { + clearInterval(this.timeoutBannerInterval); + delete this.timeoutBannerInterval; + } + }); + }); + chatRoom.rebind(`onRaisedHandAdd.${call_NAMESPACE}`, (ev, { + userHandle + }) => this.isMounted() && this.setState(state => ({ + raisedHandPeers: [...state.raisedHandPeers, userHandle] + }), () => { + const { + raisedHandPeers + } = this.state; + if (userHandle !== u_handle && !this.props.minimized) { + this.setState({ + raisedHandToast: true + }, () => this.renderRaisedHandToast()); + } + mBroadcaster.sendMessage('meetings:raisedHand', raisedHandPeers); + })); + chatRoom.rebind(`onRaisedHandDel.${call_NAMESPACE}`, (ev, { + userHandle + }) => this.isMounted() && this.setState(state => ({ + raisedHandPeers: state.raisedHandPeers.filter(h => h !== userHandle) + }), () => { + const { + raisedHandPeers, + raisedHandToast + } = this.state; + mBroadcaster.sendMessage('meetings:raisedHand', raisedHandPeers); + if (raisedHandPeers && raisedHandPeers.length) { + return raisedHandToast ? this.renderRaisedHandToast() : null; + } + return this.setState({ + raisedHandToast: false + }, () => window.toaster.main.hideAll()); + })); + chatRoom.rebind(`onRecordingActivePeer.${call_NAMESPACE}`, (ev, { + userHandle + }) => this.setState({ + recordingActivePeer: userHandle + })); + }; + this.unbindCallEvents = () => ['onCallPeerLeft', 'onCallPeerJoined', 'onCallLeft', 'wrOnUsersAllow', 'wrOnUsersEntered', 'wrOnUserLeft', 'alterUserPrivilege', 'onCallState', 'onRecordingStarted', 'onRecordingStopped', 'onRecordingActivePeer', 'onCallEndTimeUpdated', 'onRaisedHandAdd', 'onRaisedHandDel'].map(event => this.props.chatRoom.off(`${event}.${call_NAMESPACE}`)); + this.handleCallMinimize = () => { + const { + call, + peers, + onCallMinimize + } = this.props; + const { + mode, + sidebar, + view + } = this.state; + const { + callToutId, + stayOnEnd, + presenterStreams + } = call; + Call.STATE.PREVIOUS = mode !== utils.g.MINI ? { + mode, + sidebar, + view + } : Call.STATE.PREVIOUS; + const doMinimize = () => { + onCallMinimize(); + window.toaster.main.hideAll(); + }; + mega.ui.mInfoPanel.hide(); + return peers.length > 0 || presenterStreams.has(u_handle) ? this.setState({ + mode: utils.g.MINI, + sidebar: false + }, () => { + doMinimize(); + call.setViewMode(utils.g.MINI); + }) : (() => { + doMinimize(); + if (typeof callToutId !== 'undefined' && !stayOnEnd) { + onIdle(() => call.showTimeoutDialog()); + } + })(); + }; + this.handleCallExpand = async () => { + mega.ui.mInfoPanel.hide(); + return new Promise(resolve => { + this.setState({ + ...Call.STATE.PREVIOUS + }, () => { + this.props.onCallExpand(); + resolve(); + }); + }); + }; + this.handleStreamToggle = action => { + const { + peers + } = this.props; + if (action === utils.hK.ADD && peers.length === utils.$A) { + return; + } + return action === utils.hK.ADD ? peers.addFakeDupStream() : peers.removeFakeDupStream(); + }; + this.handleSpeakerChange = (source, presenterThumbSelected) => { + if (source) { + this.handleModeChange(utils.g.MAIN); + const sourceId = source.isLocal ? 0 : source.clientId; + if (sourceId !== this.props.call.pinnedCid) { + this.props.call.setPinnedCid(sourceId); + } else { + this.props.call.setPinnedCid(sourceId, !source.hasScreen || presenterThumbSelected === this.state.presenterThumbSelected); + } + const { + pinnedCid + } = this.props.call; + this.setState({ + forcedLocal: !!(source.isLocal && pinnedCid !== null), + presenterThumbSelected: pinnedCid === null ? false : !!presenterThumbSelected && source.hasScreen + }); + } else if (source === null) { + this.setState({ + presenterThumbSelected: !!presenterThumbSelected + }); + } + }; + this.handleModeChange = mode => { + this.props.call.setViewMode(mode); + this.setState({ + mode, + forcedLocal: false + }); + }; + this.handleChatToggle = () => { + if (this.state.sidebar && this.state.view === utils.gR.CHAT) { + return this.setState({ + ...Call.STATE.DEFAULT + }); + } + return this.setState({ + sidebar: true, + view: utils.gR.CHAT + }); + }; + this.handleParticipantsToggle = forceOpen => { + if (forceOpen !== true) { + forceOpen = false; + } + if (this.state.sidebar && this.state.view === utils.gR.CHAT) { + return this.setState({ + sidebar: true, + view: utils.gR.PARTICIPANTS + }); + } + return this.setState({ + sidebar: forceOpen ? true : !this.state.sidebar, + view: utils.gR.PARTICIPANTS + }); + }; + this.handleInviteToggle = () => { + if (Object.values(M.u.toJS()).some(u => u.c === 1)) { + const participants = (0,conversationpanel.z)(this.props.chatRoom); + if ((0,conversationpanel.e)(participants)) { + msgDialog(`confirmationa:!^${l[8726]}!${l.msg_dlg_cancel}`, null, `${l.all_contacts_added}`, `${l.all_contacts_added_to_chat}`, res => { + if (res) { + contactAddDialog(null, false); + } + }); + } else { + this.setState({ + invite: !this.state.invite + }); + } + } else { + msgDialog(`confirmationa:!^${l[8726]}!${l.msg_dlg_cancel}`, null, `${l.no_contacts}`, `${l.no_contacts_text}`, resp => { + if (resp) { + contactAddDialog(null, false); + } + }); + } + }; + this.handleHoldToggle = async () => { + await this.props.call.toggleHold(); + mBroadcaster.sendMessage('meetings:toggleHold'); + }; + this.handleScreenSharingToggle = () => { + const { + call + } = this.props; + const userAgent = navigator.userAgent.match(/Chrom(e|ium)\/(\d+)\./); + const version = parseInt(userAgent[2], 10); + if (version === 92) { + return msgDialog('info', undefined, l[47], l.chrome_screensharing); + } + return call.toggleScreenSharing(); + }; + this.handleCallEnd = () => { + let _this$props$call; + mega.ui.mInfoPanel.hide(); + (_this$props$call = this.props.call) == null || _this$props$call.destroy(SfuClient.TermCode.kUserHangup); + }; + this.handleEphemeralAdd = handle => handle && this.setState(state => ({ + ephemeral: true, + ephemeralAccounts: [...state.ephemeralAccounts, handle] + })); + this.handleStayConfirm = () => { + const { + call + } = this.props; + call.handleStayConfirm(); + onIdle(() => this.safeForceUpdate()); + }; + this.handleRecordingToggle = () => { + const { + call, + chatRoom + } = this.props; + if (chatRoom.isMeeting) { + eventlog(500286); + } else { + eventlog(500287); + } + if (this.state.recorderCid) { + return msgDialog(`confirmation:!^${l.stop_recording_dialog_cta}!${l.stop_recording_nop_dialog_cta}`, undefined, l.stop_recording_dialog_heading, l.stop_recording_dialog_body, cb => cb && sfuClient.recordingStop(), 1); + } + msgDialog(`warningb:!^${l.start_recording_dialog_cta}!${l.msg_dlg_cancel}`, null, l.notify_participants_dialog_heading, l.notify_participants_dialog_body, cb => { + if (cb || cb === null) { + return; + } + call.sfuClient.recordingStart(this.onWeStoppedRecording).then(() => { + call.recorderCid = this.state.recorderCid; + this.setState({ + recorderCid: call.sfuClient.cid + }); + this.handleModeChange(utils.g.MAIN); + call.recordActiveStream(); + ChatToast.quick(l.started_recording_toast); + }).catch(dump); + }, 1); + }; + this.onWeStoppedRecording = err => this.isMounted() && this.setState({ + recorderCid: undefined, + recordingActivePeer: undefined + }, () => err ? ChatToast.quick(`${l.stopped_recording_toast} Error: ${err.message || err}`) : ChatToast.quick(l.stopped_recording_toast)); + this.renderRecordingControl = () => { + const { + chatRoom, + call, + peers + } = this.props; + const { + recorderCid, + recordingTooltip, + recordingActivePeer + } = this.state; + const userIsModerator = (0,utils.Cy)(chatRoom, u_handle); + const $$CONTAINER = ({ + className, + onClick, + children + }) => JSX_("div", { + className: ` + recording-control + ${localStorage.callDebug ? 'with-offset' : ''} + ${className || ''} + `, + onClick + }, children); + if (recorderCid) { + const isRecorder = userIsModerator && recorderCid === call.sfuClient.cid; + const recordingPeer = peers[recorderCid]; + return JSX_($$CONTAINER, { + recordingTooltip, + className: "recording-fixed" + }, JSX_("div", (0,esm_extends.A)({ + className: ` + recording-ongoing + simpletip + ${isRecorder ? '' : 'plain-background'} + ` + }, recorderCid !== call.sfuClient.cid && { + 'data-simpletip': l.host_recording.replace('%NAME', nicknames.getNickname(recordingPeer) || megaChat.plugins.userHelper.SIMPLETIP_USER_LOADER), + 'data-simpletipposition': 'top', + 'data-simpletipoffset': 5, + 'data-simpletip-class': 'theme-dark-forced' + }), JSX_("span", { + className: ` + recording-icon + button + ${recordingTooltip ? 'active-dropdown' : ''} + ${isRecorder ? 'clickable' : ''} + `, + onMouseEnter: () => isRecorder && this.setState({ + recordingTooltip: true + }), + onMouseOut: () => isRecorder && delay('meetings-rec-hover', () => this.setState({ + recordingTooltip: false + }), 1250) + }, "REC ", JSX_("i", null), JSX_(dropdowns.ms, { + className: "recording-info theme-dark-forced", + active: recordingTooltip, + noArrow: false, + positionMy: "center top", + positionAt: "center bottom", + vertOffset: 40, + horizOffset: 30 + }, JSX_("div", null, "Currently recording: ", nicknames.getNickname(recordingActivePeer)))), isRecorder && JSX_("span", { + className: "recording-toggle", + onClick: this.handleRecordingToggle + }, l.record_stop_button))); + } + const isOnHold = !!((call == null ? void 0 : call.av) & Av.onHold); + return userIsModerator && JSX_($$CONTAINER, { + className: isOnHold ? 'disabled' : '', + onClick: () => { + this.setState({ + onboardingRecording: false, + hovered: false + }, () => { + this.flagMap.setSync(OBV4_FLAGS.CHAT_CALL_RECORDING, 1); + this.flagMap.safeCommit(); + }); + return isOnHold || recorderCid && recorderCid !== call.sfuClient.cid ? null : this.handleRecordingToggle(); + } + }, JSX_(meetings_button.A, { + className: ` + mega-button + theme-dark-forced + call-action + round + recording-start + ${isOnHold ? 'disabled' : ''} + ` + }, JSX_("div", null, JSX_("i", null))), JSX_("span", { + className: "record-label" + }, l.record_start_button)); + }; + this.setActiveElement = activeElement => this.setState({ + activeElement + }); + const { + SOUNDS + } = megaChat; + [SOUNDS.RECONNECT, SOUNDS.CALL_END, SOUNDS.CALL_JOIN_WAITING].map(sound => ion.sound.preload(sound)); + this.state.mode = props.call.viewMode; + this.setOnboarding(); + this.handleMouseMove = this.handleMouseMove.bind(this); + this.handleMouseOut = this.handleMouseOut.bind(this); + } + handleMouseMove() { + this.setState({ + hovered: true + }); + if (this.delayProcID) { + delay.cancel(this.delayProcID); + this.delayProcID = null; + } + } + handleMouseOut() { + if (this.state.hovered) { + this.delayProcID = delay('meetings-call-hover', () => { + if (this.isMounted()) { + this.setState({ + hovered: false + }); + } + }, MOUSE_OUT_DELAY); + } + } + handleCallOffline() { + if (!this.pCallTimer) { + (this.pCallTimer = tSleep(30)).then(() => { + this.setState({ + offline: true + }); + }); + } + } + setOnboarding() { + this.state.onboardingUI = this.state.hovered = this.flagMap && !this.flagMap.getSync(OBV4_FLAGS.CHAT_CALL_UI); + if (!this.state.onboardingUI) { + this.state.onboardingRecording = this.state.hovered = this.flagMap && !this.flagMap.getSync(OBV4_FLAGS.CHAT_CALL_RECORDING); + } + if (!this.state.onboardingUI && !this.state.onboardingRecording) { + this.state.onboardingRaise = this.state.hovered = this.flagMap && !this.flagMap.getSync(OBV4_FLAGS.CHAT_CALL_RAISE); + } + } + handleInvitePanelToggle() { + delay('chat-event-inv-call', () => eventlog(99962)); + this.setState({ + invitePanel: !this.state.invitePanel + }); + } + handleInviteOrAdd() { + const { + chatRoom + } = this.props; + if (chatRoom.type === 'group') { + return this.handleInviteToggle(); + } + loadingDialog.show('fetchchatlink'); + chatRoom.updatePublicHandle(false, false, true).catch(dump).always(() => { + loadingDialog.hide('fetchchatlink'); + if (!this.isMounted()) { + return; + } + if (!chatRoom.iAmOperator() && chatRoom.options[MCO_FLAGS.OPEN_INVITE] && !chatRoom.publicLink) { + this.handleInviteToggle(); + } else if (chatRoom.type === 'public' && !chatRoom.topic) { + this.handleInviteToggle(); + } else { + this.handleInvitePanelToggle(); + } + }); + } + renderTimeLimitBanner() { + return JSX_("div", { + className: "call-time-limit-banner theme-dark-forced" + }, JSX_("span", { + ref: this.timeoutBannerRef + }, this.timeoutString), JSX_("span", { + className: "call-limit-banner-action", + onClick: () => { + clearInterval(this.timeoutBannerInterval); + delete this.timeoutBannerInterval; + this.setState({ + timeoutBanner: false + }); + } + }, l[2005]), this.state.showTimeoutUpgrade && JSX_(ui_link.A, { + className: "call-limit-banner-action", + onClick: () => { + window.open(`${getBaseUrl()}/pro`, '_blank', 'noopener,noreferrer'); + eventlog(500262); + } + }, l.upgrade_now)); + } + get timeoutString() { + const { + call + } = this.props; + if (call.callEndTime === 0) { + return ''; + } + const remainSeconds = Math.max(0, Math.ceil((call.callEndTime - Date.now()) / 1000)); + if (call.organiser === u_handle) { + if (remainSeconds < 60) { + return mega.icu.format(l.free_call_banner_organiser_ending_sec, remainSeconds); + } + if (remainSeconds <= 120) { + return mega.icu.format(l.free_call_banner_organiser_ending, Math.ceil(remainSeconds / 60)); + } + return mega.icu.format(l.free_call_banner_organiser_warning, Math.ceil(remainSeconds / 60)); + } + if (remainSeconds < 60) { + return mega.icu.format(l.free_call_banner_ending_sec, remainSeconds); + } + if (remainSeconds <= 120) { + return mega.icu.format(l.free_call_banner_ending, Math.ceil(remainSeconds / 60)); + } + return mega.icu.format(l.free_call_banner_warning, Math.ceil(remainSeconds / 60)); + } + updateTimeoutDuration() { + if (this.timeoutBannerRef) { + const { + current + } = this.timeoutBannerRef; + const newStr = this.timeoutString; + if (newStr && current && current.innerText !== newStr) { + current.innerText = newStr; + } + if (this.state.showTimeoutUpgrade && this.props.call.callEndTime - Date.now() <= 12e4) { + this.setState({ + showTimeoutUpgrade: false + }); + } + } + } + componentWillUnmount() { + super.componentWillUnmount(); + const { + minimized, + willUnmount, + chatRoom + } = this.props; + chatRoom.megaChat.off(`sfuConnClose.${call_NAMESPACE}`); + chatRoom.megaChat.off(`sfuConnOpen.${call_NAMESPACE}`); + chatRoom.megaChat.off(`onSpeakerChange.${call_NAMESPACE}`); + chatRoom.megaChat.off(`onPeerAvChange.${call_NAMESPACE}`); + mBroadcaster.removeListener(this.ephemeralAddListener); + mBroadcaster.removeListener(this.pageChangeListener); + clearTimeout(this.callStartTimeout); + delay.cancel('callOffline'); + if ($.dialog) { + closeDialog(); + } + if (this.timeoutBannerInterval) { + clearInterval(this.timeoutBannerInterval); + } + window.toaster.main.hideAll(); + this.unbindCallEvents(); + willUnmount == null || willUnmount(minimized); + } + componentDidMount() { + super.componentDidMount(); + const { + call, + didMount, + chatRoom + } = this.props; + this.ephemeralAddListener = mBroadcaster.addListener('meetings:ephemeralAdd', handle => this.handleEphemeralAdd(handle)); + this.pageChangeListener = mBroadcaster.addListener('pagechange', () => { + const currentRoom = megaChat.getCurrentRoom(); + if ((0,utils.Av)() && (!M.chat || currentRoom && currentRoom.chatId !== chatRoom.chatId)) { + this.handleCallMinimize(); + } + }); + chatRoom.megaChat.rebind(`sfuConnOpen.${call_NAMESPACE}`, () => this.handleCallOnline()); + chatRoom.megaChat.rebind(`sfuConnClose.${call_NAMESPACE}`, () => this.handleCallOffline()); + chatRoom.rebind(`onCallState.${call_NAMESPACE}`, (ev, { + arg + }) => this.setState({ + initialCallRinging: arg + })); + const { + tresizer + } = $; + chatRoom.rebind(`onPeerAvChange.${call_NAMESPACE}`, tresizer); + chatRoom.rebind(`onSpeakerChange.${call_NAMESPACE}`, tresizer); + this.callStartTimeout = setTimeout(() => { + if (!mega.config.get('callemptytout') && !call.hasOtherParticipant()) { + call.left = true; + call.initCallTimeout(); + } + }, 300000); + setTimeout(() => { + let _call$peers; + return ((_call$peers = call.peers) == null ? void 0 : _call$peers.length) && !call.hasOtherParticipant() && this.setState({ + everHadPeers: true + }); + }, 2e3); + if (sessionStorage.previewMedia) { + const { + audio, + video + } = JSON.parse(sessionStorage.previewMedia); + sessionStorage.removeItem('previewMedia'); + tSleep(2).then(() => audio && call.sfuClient.muteAudio()).then(() => video && call.sfuClient.muteCamera()).catch(dump); + } + this.bindCallEvents(); + didMount == null || didMount(); + } + componentDidUpdate() { + if (typeof psa !== 'undefined') { + psa.repositionMeetingsCall(); + } + } + render() { + let _ref; + const { + minimized, + peers, + call, + chatRoom, + parent, + typingAreaText, + onDeleteMessage, + onTypingAreaChanged + } = this.props; + const { + mode, + view, + sidebar, + hovered, + forcedLocal, + invite, + ephemeral, + ephemeralAccounts, + guest, + offline, + onboardingUI, + onboardingRecording, + onboardingRaise, + everHadPeers, + initialCallRinging, + waitingRoomPeers, + recorderCid, + raisedHandPeers, + recordingConsentDialog, + invitePanel, + presenterThumbSelected, + timeoutBanner, + activeElement + } = this.state; + const { + stayOnEnd + } = call; + const hasOnboarding = onboardingUI || onboardingRecording || onboardingRaise; + const STREAM_PROPS = { + mode, + peers, + sidebar, + hovered: hasOnboarding || hovered, + forcedLocal, + call, + view, + chatRoom, + parent, + stayOnEnd, + everHadPeers, + waitingRoomPeers, + recorderCid, + presenterThumbSelected, + raisedHandPeers, + activeElement, + hasOtherParticipants: call.hasOtherParticipant(), + isOnHold: call.sfuClient.isOnHold, + isFloatingPresenter: (_ref = mode === utils.g.MINI && !forcedLocal ? call.getActiveStream() : call.getLocalStream()) == null ? void 0 : _ref.hasScreen, + onSpeakerChange: this.handleSpeakerChange, + onModeChange: this.handleModeChange, + onInviteToggle: this.handleInviteToggle, + onStayConfirm: this.handleStayConfirm + }; + return JSX_("div", { + ref: this.domRef, + className: ` + meetings-call + ${minimized ? 'minimized' : ''} + ${timeoutBanner ? 'with-timeout-banner' : ''} + ${activeElement ? 'with-active-element' : ''} + `, + onMouseMove: hasOnboarding ? null : this.handleMouseMove, + onMouseOut: hasOnboarding ? null : this.handleMouseOut + }, timeoutBanner && this.renderTimeLimitBanner(), JSX_(stream_Stream, (0,esm_extends.A)({}, STREAM_PROPS, { + minimized, + ephemeralAccounts, + onCallMinimize: this.handleCallMinimize, + onCallExpand: this.handleCallExpand, + onCallEnd: this.handleCallEnd, + onStreamToggle: this.handleStreamToggle, + onRecordingToggle: () => call.sfuClient.recordingStop(), + onChatToggle: this.handleChatToggle, + onParticipantsToggle: this.handleParticipantsToggle, + onAudioClick: () => call.toggleAudio(), + onVideoClick: () => call.toggleVideo(), + onScreenSharingClick: this.handleScreenSharingToggle, + onHoldClick: this.handleHoldToggle, + onVideoDoubleClick: this.handleSpeakerChange, + setActiveElement: this.setActiveElement + })), sidebar && JSX_(Sidebar, (0,esm_extends.A)({}, STREAM_PROPS, { + guest, + initialCallRinging, + typingAreaText, + onGuestClose: () => this.setState({ + guest: false + }), + onSidebarClose: () => this.setState({ + ...Call.STATE.DEFAULT + }), + onDeleteMessage, + onCallMinimize: this.handleCallMinimize, + onInviteToggle: () => this.handleInviteOrAdd(), + onTypingAreaChanged + })), minimized ? null : JSX_(REaCt().Fragment, null, this.renderRecordingControl(), JSX_(streamControls, { + call, + minimized, + peers, + chatRoom, + recorderCid, + hovered, + raisedHandPeers, + onboardingRaise, + onOnboardingRaiseDismiss: () => { + this.setState({ + onboardingRaise: false, + hovered: false + }, () => { + this.flagMap.setSync(OBV4_FLAGS.CHAT_CALL_RAISE, 1); + this.flagMap.safeCommit(); + }); + }, + onRecordingToggle: () => this.setState({ + recorderCid: undefined + }, () => call.sfuClient.recordingStop()), + onAudioClick: () => call.toggleAudio(), + onVideoClick: () => call.toggleVideo(), + onScreenSharingClick: this.handleScreenSharingToggle, + onCallEnd: this.handleCallEnd, + onStreamToggle: this.handleStreamToggle, + onHoldClick: this.handleHoldToggle, + setActiveElement: this.setActiveElement + }), JSX_(sidebarControls, { + call, + chatRoom, + npeers: peers.length, + mode, + view, + sidebar, + onChatToggle: this.handleChatToggle, + onParticipantsToggle: this.handleParticipantsToggle, + onInviteToggle: () => this.handleInviteOrAdd() + })), invite && JSX_(Invite, { + contacts: M.u, + call, + chatRoom, + onClose: () => this.setState({ + invite: false + }) + }), ephemeral && JSX_(workflow_ephemeral, { + ephemeralAccounts, + onClose: () => this.setState({ + ephemeral: false + }) + }), offline && JSX_(meetings_offline, { + onClose: () => { + if (offline) { + this.setState({ + offline: false + }, () => delay('call:timeout', this.handleRetryTimeout, 3e4)); + } + }, + onCallEnd: () => { + this.setState({ + offline: false + }, () => this.handleRetryTimeout()); + } + }), onboardingUI && JSX_("div", { + className: `${call_NAMESPACE}-onboarding` + }, JSX_("div", { + className: "mega-dialog mega-onboarding-dialog dialog-template-message onboarding-UI", + id: "ob-dialog", + role: "dialog", + "aria-labelledby": "ob-dialog-title", + "aria-modal": "true" + }, JSX_("i", { + className: "sprite-fm-mono icon-tooltip-arrow tooltip-arrow top", + id: "ob-dialog-arrow" + }), JSX_("header", null, JSX_("div", null, JSX_("h2", { + id: "ob-dialog-title" + }, l.onboarding_call_title), JSX_("p", { + id: "ob-dialog-text" + }, l.onboarding_call_body))), JSX_("footer", null, JSX_("div", { + className: "footer-container" + }, JSX_("button", { + className: "mega-button js-next small theme-light-forced", + onClick: () => { + this.setState({ + onboardingUI: false, + onboardingRecording: chatRoom.iAmOperator() && !this.flagMap.getSync(OBV4_FLAGS.CHAT_CALL_RECORDING) + }, () => { + this.flagMap.setSync(OBV4_FLAGS.CHAT_CALL_UI, 1); + this.flagMap.safeCommit(); + this.setState({ + onboardingRaise: !this.state.onboardingRecording && !this.flagMap.getSync(OBV4_FLAGS.CHAT_CALL_RAISE) + }); + }); + } + }, JSX_("span", null, l.ok_button)))))), onboardingRecording && (0,utils.Cy)(chatRoom, u_handle) && JSX_("div", { + className: `${call_NAMESPACE}-onboarding` + }, JSX_("div", { + className: "mega-dialog mega-onboarding-dialog dialog-template-message onboarding-recording", + id: "ob-dialog", + role: "dialog", + "aria-labelledby": "ob-dialog-title", + "aria-modal": "true" + }, JSX_("i", { + className: "sprite-fm-mono icon-tooltip-arrow tooltip-arrow bottom", + id: "ob-dialog-arrow" + }), JSX_("header", null, JSX_("div", null, JSX_("h2", { + id: "ob-dialog-title" + }, l.recording_onboarding_title), JSX_("p", { + id: "ob-dialog-text" + }, l.recording_onboarding_body_intro), JSX_("p", { + id: "ob-dialog-text" + }, l.recording_onboarding_body_details))), JSX_("footer", null, JSX_("div", { + className: "footer-container" + }, JSX_(ui_link.A, { + className: "link-button", + to: "https://help.mega.io/chats-meetings/chats/call-recording", + target: "_blank" + }, l[8742]), JSX_("button", { + className: "mega-button js-next small theme-light-forced", + onClick: () => { + this.setState({ + onboardingRecording: false, + onboardingRaise: true + }, () => { + this.flagMap.setSync(OBV4_FLAGS.CHAT_CALL_RECORDING, 1); + this.flagMap.safeCommit(); + }); + } + }, JSX_("span", null, l.ok_button)))))), recordingConsentDialog && JSX_(RecordingConsentDialog, { + peers, + recorderCid, + onClose: () => this.setState({ + recordingConsentDialog: false, + recordingConsented: true + }), + onCallEnd: this.handleCallEnd + }), invitePanel && JSX_(modalDialogs.A.ModalDialog, { + className: "theme-dark-forced", + onClose: () => { + this.setState({ + invitePanel: false + }); + }, + dialogName: "chat-link-dialog", + chatRoom + }, JSX_(inviteParticipantsPanel.Q, { + chatRoom, + onAddParticipants: () => { + this.setState({ + invitePanel: false + }, () => this.handleInviteToggle()); + } + }))); + } +} +Call.STATE = { + DEFAULT: { + sidebar: false + }, + PREVIOUS: { + mode: null, + sidebar: null, + view: null + } +}; + + }, + + 192 +(_, EXP_, REQ_) { + + REQ_.d(EXP_, { + $: () => withPermissionsObserver + }); + const _babel_runtime_helpers_extends0__ = REQ_(8168); + const react1__ = REQ_(1594); + const react1___default = REQ_.n(react1__); + const _mixins_js2__ = REQ_(8264); + const _ui_modalDialogs_jsx3__ = REQ_(8120); + const _ui_utils_jsx4__ = REQ_(6411); + + + + + +const errors = { + browser: 'NotAllowedError: Permission denied', + system: 'NotAllowedError: Permission denied by system', + dismissed: 'NotAllowedError: Permission dismissed', + nil: 'NotFoundError: Requested device not found', + sharedCam: 'NotReadableError: Could not start video source', + sharedMic: 'NotReadableError: Could not start audio source', + sharedGeneric: 'NotReadableError: Device in use' +}; +const isUserActionError = error => { + return error && error === errors.browser; +}; +const withPermissionsObserver = Component => { + return class extends _mixins_js2__ .w9 { + constructor(props) { + super(props); + this.namespace = `PO-${Component.NAMESPACE}`; + this.observer = `onLocalMediaError.${this.namespace}`; + this.childRef = undefined; + this.platform = ua.details.os; + this.helpURL = `${l.mega_help_host}/chats-meetings/meetings/enable-audio-video-call-permissions`; + this.macURI = 'x-apple.systempreferences:com.apple.preference.security'; + this.winURI = 'ms-settings'; + this.CONTENT = { + [Av.Audio]: { + system: { + title: l.no_mic_title, + info: this.platform === 'Windows' ? l.no_mic_system_windows.replace('[A]', ``).replace('[/A]', '') : l.no_mic_system_mac.replace('[A]', ``).replace('[/A]', ''), + buttons: [this.platform === 'Apple' || this.platform === 'Windows' ? { + key: 'open-settings', + label: l.open_system_settings, + className: 'positive', + onClick: () => { + window.open(this.platform === 'Apple' ? `${this.macURI}?Privacy_Microphone` : `${this.winURI}:privacy-microphone`, '_blank', 'noopener,noreferrer'); + this.closePermissionsDialog(Av.Audio); + } + } : { + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Audio) + }] + }, + browser: { + title: l.no_mic_title, + cover: 'permissions-mic', + info: l.allow_mic_access.replace('[X]', ''), + buttons: [{ + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Audio) + }] + }, + nil: { + title: l.no_mic_detected_title, + info: l.no_mic_detected_info, + buttons: [{ + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Audio) + }] + }, + shared: { + title: l.no_mic_title, + info: l.shared_mic_err_info.replace('[A]', ``).replace('[/A]', ''), + buttons: [{ + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Audio) + }] + } + }, + [Av.Camera]: { + system: { + title: l.no_camera_title, + info: this.platform === 'Windows' ? l.no_camera_system_windows.replace('[A]', ``).replace('[/A]', '') : l.no_camera_system_mac.replace('[A]', ``).replace('[/A]', ''), + buttons: [this.platform === 'Apple' || this.platform === 'Windows' ? { + key: 'open-settings', + label: l.open_system_settings, + className: 'positive', + onClick: () => { + window.open(this.platform === 'Apple' ? `${this.macURI}?Privacy_Camera` : `${this.winURI}:privacy-webcam`, '_blank', 'noopener,noreferrer'); + this.closePermissionsDialog(Av.Camera); + } + } : { + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Camera) + }] + }, + browser: { + title: l.no_camera_title, + cover: 'permissions-camera', + info: l.allow_camera_access.replace('[X]', ''), + buttons: [{ + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Camera) + }] + }, + nil: { + title: l.no_camera_detected_title, + info: l.no_camera_detected_info, + buttons: [{ + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Camera) + }] + }, + shared: { + title: l.no_camera_title, + info: l.shared_cam_err_info.replace('[A]', ``).replace('[/A]', ''), + buttons: [{ + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Camera) + }] + } + }, + [Av.Screen]: { + title: l.no_screen_title, + info: l.no_screen_system.replace('[A]', ``).replace('[/A]', ''), + buttons: [{ + key: 'open-settings', + label: l.open_system_settings, + className: 'positive', + onClick: () => { + window.open(`${this.macURI}?Privacy_ScreenCapture`, '_blank', 'noopener,noreferrer'); + this.closePermissionsDialog(Av.Screen); + } + }] + } + }; + this.state = { + errMic: '', + errCamera: '', + errScreen: '', + [`dialog-${Av.Audio}`]: null, + [`dialog-${Av.Camera}`]: null, + [`dialog-${Av.Screen}`]: null + }; + this.getPermissionsDialogContent = () => { + const { + CONTENT, + state + } = this; + const { + errMic, + errCamera + } = state; + const { + browser, + system, + nil, + sharedCam, + sharedMic, + sharedGeneric + } = errors; + return { + [Av.Audio]: { + ...errMic === browser && CONTENT[Av.Audio].browser, + ...errMic === system && CONTENT[Av.Audio].system, + ...errMic === nil && CONTENT[Av.Audio].nil, + ...errMic === sharedMic && CONTENT[Av.Audio].shared, + ...errMic === sharedGeneric && CONTENT[Av.Audio].shared + }, + [Av.Camera]: { + ...errCamera === browser && CONTENT[Av.Camera].browser, + ...errCamera === system && CONTENT[Av.Camera].system, + ...errCamera === nil && CONTENT[Av.Camera].nil, + ...errCamera === sharedCam && CONTENT[Av.Camera].shared, + ...errCamera === sharedGeneric && CONTENT[Av.Camera].shared + }, + [Av.Screen]: CONTENT[Av.Screen] + }; + }; + this.resetError = av => { + this.setState({ + errMic: av === Av.Audio ? '' : this.state.errMic, + errCamera: av === Av.Camera ? '' : this.state.errCamera, + errScreen: av === Av.Screen ? '' : this.state.errScreen + }); + }; + this.hasToRenderPermissionsWarning = this.hasToRenderPermissionsWarning.bind(this); + this.renderPermissionsWarning = this.renderPermissionsWarning.bind(this); + } + hasToRenderPermissionsWarning(av) { + const CONFIG = { + [Av.Audio]: { + showOnUserActionError: true, + err: this.state.errMic + }, + [Av.Camera]: { + showOnUserActionError: true, + err: this.state.errCamera + }, + [Av.Screen]: { + showOnUserActionError: false, + err: this.state.errScreen + } + }; + const current = CONFIG[av]; + if (current) { + return isUserActionError(current.err) ? current.showOnUserActionError : current.err; + } + return false; + } + closePermissionsDialog(av) { + this.setState({ + [`dialog-${av}`]: false + }, () => { + let _this$childRef; + return (_this$childRef = this.childRef) == null ? void 0 : _this$childRef.safeForceUpdate(); + }); + } + renderPermissionsDialog(av, child) { + const content = this.getPermissionsDialogContent(); + const { + title, + info, + buttons, + cover + } = content[av] || {}; + return JSX_(_ui_modalDialogs_jsx3__ .A.ModalDialog, { + dialogName: `${this.namespace}-permissions-${av}`, + className: ` + meetings-permissions-dialog + dialog-template-message + with-close-btn + warning + `, + buttons, + hideOverlay: Component.NAMESPACE === 'preview-meeting' && !document.body.classList.contains('not-logged'), + onClose: () => { + this.setState({ + [`dialog-${av}`]: false + }, () => child && child.safeForceUpdate()); + } + }, JSX_("header", null, cover ? null : JSX_("div", { + className: "graphic" + }, JSX_("i", { + className: "warning sprite-fm-uni icon-warning" + })), JSX_("div", { + className: "info-container" + }, JSX_("h3", { + id: "msgDialog-title" + }, title || l[47]), cover && JSX_("div", { + className: "permissions-warning-cover" + }, JSX_("span", { + className: cover + })), JSX_(_ui_utils_jsx4__ .P9, { + tag: "p", + className: "permissions-warning-info", + content: info + })))); + } + renderPermissionsWarning(av, child) { + const { + errMic, + errCamera + } = this.state; + const dismissed = errMic === errors.dismissed || errCamera === errors.dismissed; + return JSX_("div", { + className: ` + ${this.namespace} + meetings-signal-issue + simpletip + ${dismissed ? 'with-small-area' : ''} + `, + "data-simpletip": l.show_info, + "data-simpletipposition": "top", + "data-simpletipoffset": "5", + "data-simpletip-class": "theme-dark-forced", + onClick: () => dismissed ? null : this.setState({ + [`dialog-${av}`]: true + }, () => { + if (child) { + this.childRef = child; + } + }) + }, JSX_("span", { + className: "signal-issue-background" + }), JSX_("i", { + className: "sprite-fm-mono icon-exclamation-filled" + }), this.state[`dialog-${av}`] && this.renderPermissionsDialog(av, child)); + } + componentWillUnmount() { + super.componentWillUnmount(); + megaChat.unbind(this.observer); + } + componentDidMount() { + super.componentDidMount(); + megaChat.rebind(this.observer, (ev, errAv) => { + this.setState({ + errMic: errAv && errAv.mic ? String(errAv.mic) : this.state.errMic, + errCamera: errAv && errAv.camera ? String(errAv.camera) : this.state.errCamera, + errScreen: errAv && errAv.screen ? String(errAv.screen) : this.state.errScreen + }); + }); + megaChat.rebind(`onLocalMediaQueryError.${this.namespace}`, (ev, { + type, + err + }) => { + if (type === 'screen' && String(err) === errors.system) { + this.setState({ + [`dialog-${Av.Screen}`]: true + }, () => this.safeForceUpdate()); + } + }); + } + render() { + return JSX_(Component, (0,_babel_runtime_helpers_extends0__ .A)({}, this.props, this.state, { + errMic: this.state.errMic, + errCamera: this.state.errCamera, + errScreen: this.state.errScreen, + hasToRenderPermissionsWarning: this.hasToRenderPermissionsWarning, + resetError: this.resetError, + renderPermissionsWarning: this.renderPermissionsWarning + })); + } + }; +}; + + }, + + 7128 +(_, EXP_, REQ_) { + +REQ_.r(EXP_); + REQ_.d(EXP_, { + "default": () => Join + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _ui_modalDialogs_jsx1__ = REQ_(8120); + const _ui_utils_jsx2__ = REQ_(6411); + const _button_jsx3__ = REQ_(6740); + const _preview_jsx4__ = REQ_(3546); + const _historyPanel_jsx5__ = REQ_(5522); + const _link_jsx6__ = REQ_(4649); + const _utils_jsx7__ = REQ_(2153); + + + + + + + + +class Join extends react0___default().Component { + constructor(props) { + super(props); + this.NAMESPACE = 'join-meeting'; + this.state = { + preview: false, + view: _utils_jsx7__ .j.INITIAL, + firstName: '', + lastName: '', + previewAudio: true, + previewVideo: false, + ephemeralDialog: false + }; + this.handleKeyDown = ({ + key + }) => { + let _this$props$onClose, _this$props; + return key && key === 'Escape' ? (_this$props$onClose = (_this$props = this.props).onClose) == null ? void 0 : _this$props$onClose.call(_this$props) : true; + }; + this.showPanels = () => { + return [document.querySelector('.nw-fm-left-icons-panel'), document.querySelector('.chat-app-container')].map(el => el && el.classList.remove('hidden')); + }; + this.hidePanels = () => { + return [document.querySelector('.nw-fm-left-icons-panel'), document.querySelector('.chat-app-container')].map(el => el && el.classList.add('hidden')); + }; + this.showConfirmationDialog = () => { + megaChat.destroy(); + return mega.ui.sendSignupLinkDialog(JSON.parse(localStorage.awaitingConfirmationAccount), () => { + delete localStorage.awaitingConfirmationAccount; + u_logout(true).then(() => location.reload()); + }); + }; + this.Ephemeral = () => { + const onCancel = () => this.setState({ + ephemeralDialog: false + }); + const msgFragments = l.ephemeral_data_lost.split(/\[A]|\[\/A]/); + return JSX_(_ui_modalDialogs_jsx1__ .A.ModalDialog, { + name: "end-ephemeral", + dialogType: "message", + icon: "sprite-fm-uni icon-warning", + title: l.ephemeral_data_lost_title, + noCloseOnClickOutside: true, + buttons: [{ + key: 'cancel', + label: l.msg_dlg_cancel, + onClick: onCancel + }, { + key: 'continue', + label: l[507], + className: 'positive', + onClick: () => { + u_logout(true).then(() => location.reload()); + sessionStorage.guestForced = true; + } + }], + onClose: onCancel + }, JSX_("p", null, msgFragments[0], JSX_(_link_jsx6__ .A, { + to: "/register", + onClick: () => loadSubPage('register') + }, msgFragments[1]), msgFragments[2])); + }; + this.Head = () => { + let _this$props$chatRoom; + return JSX_("div", { + className: `${this.NAMESPACE}-head` + }, JSX_("div", { + className: `${this.NAMESPACE}-logo` + }, JSX_("i", { + className: ` + sprite-fm-illustration-wide + ${mega.ui.isDarkTheme() ? 'mega-logo-dark' : 'img-mega-logo-light'} + ` + })), JSX_("h1", null, JSX_(_ui_utils_jsx2__ .zT, null, l.you_have_invitation.replace('%1', (_this$props$chatRoom = this.props.chatRoom) == null ? void 0 : _this$props$chatRoom.topic))), isEphemeral() && JSX_("div", { + className: "ephemeral-info" + }, JSX_("i", { + className: "sprite-fm-uni icon-warning" + }), JSX_("p", null, l.ephemeral_data_store_lost))); + }; + this.Intro = () => { + const $$CONTAINER = ({ + children + }) => JSX_(react0___default().Fragment, null, JSX_("div", { + className: `${this.NAMESPACE}-content` + }, children), this.Chat()); + if (isEphemeral()) { + return JSX_($$CONTAINER, null, JSX_(_button_jsx3__ .A, { + className: "mega-button positive", + onClick: () => this.setState({ + ephemeralDialog: true + }) + }, l.join_as_guest), JSX_(_button_jsx3__ .A, { + className: "mega-button", + onClick: () => loadSubPage('register') + }, l[5582]), JSX_("span", null, l[5585], JSX_("a", { + href: "#", + onClick: () => mega.ui.showLoginRequiredDialog({ + minUserType: 3, + skipInitialDialog: 1 + }).done(() => this.setState({ + view: _utils_jsx7__ .j.ACCOUNT + })) + }, l[171]))); + } + return JSX_($$CONTAINER, null, JSX_(_button_jsx3__ .A, { + className: "mega-button positive", + onClick: () => this.setState({ + view: _utils_jsx7__ .j.GUEST + }) + }, l.join_as_guest), JSX_(_button_jsx3__ .A, { + className: "mega-button", + onClick: () => { + let _this$props$chatRoom2; + megaChat.loginOrRegisterBeforeJoining((_this$props$chatRoom2 = this.props.chatRoom) == null ? void 0 : _this$props$chatRoom2.publicChatHandle, false, true, undefined, () => this.setState({ + view: _utils_jsx7__ .j.ACCOUNT + })); + } + }, l[171]), JSX_("p", null, JSX_(_ui_utils_jsx2__ .P9, { + onClick: e => { + e.preventDefault(); + megaChat.loginOrRegisterBeforeJoining(this.props.chatRoom.publicChatHandle, true, undefined, undefined, () => this.setState({ + view: _utils_jsx7__ .j.ACCOUNT + })); + } + }, l[20635]))); + }; + this.Chat = () => { + const { + chatRoom + } = this.props; + const { + preview + } = this.state; + return JSX_("div", { + className: ` + ${this.NAMESPACE}-chat + ${preview ? 'expanded' : ''} + ` + }, JSX_("div", { + className: "chat-content" + }, JSX_("div", { + className: "chat-content-head", + onClick: () => this.setState({ + preview: !preview + }) + }, JSX_(_ui_utils_jsx2__ .zT, null, chatRoom.topic), JSX_(_button_jsx3__ .A, { + icon: "icon-minimise" + })), preview && JSX_("div", { + className: "chat-body" + }, JSX_(_historyPanel_jsx5__ .A, { + chatRoom, + onMount: cmp => { + let _cmp$messagesListScro; + return (_cmp$messagesListScro = cmp.messagesListScrollable) == null ? void 0 : _cmp$messagesListScro.scrollToBottom(); + } + })))); + }; + this.Card = ({ + children + }) => { + const { + previewAudio, + previewVideo + } = this.state; + return JSX_("div", { + className: "card" + }, JSX_("div", { + className: "card-body" + }, children, JSX_("div", null, JSX_(_link_jsx6__ .A, { + to: "https://mega.io/chatandmeetings", + target: "_blank" + }, l.how_meetings_work))), JSX_("div", { + className: "card-preview" + }, JSX_(_preview_jsx4__ .A, { + audio: previewAudio, + video: previewVideo, + context: this.NAMESPACE, + onToggle: (audio, video) => this.setState({ + previewAudio: audio, + previewVideo: video + }) + }))); + }; + this.Field = ({ + name, + children + }) => { + let _this$state$name; + return JSX_("div", { + className: ` + mega-input + title-ontop + ${(_this$state$name = this.state[name]) != null && _this$state$name.length ? 'valued' : ''} + ` + }, JSX_("div", { + className: "mega-input-title" + }, children, JSX_("span", { + className: "required-red" + }, "*")), JSX_("input", { + type: "text", + name, + className: "titleTop required megaInputs", + placeholder: children, + value: this.state[name] || '', + maxLength: 40, + onChange: ev => this.setState({ + [name]: ev.target.value + }) + })); + }; + this.Guest = () => JSX_(this.Card, null, JSX_("h2", null, l.enter_name_join_meeting), JSX_("div", { + className: "card-fields" + }, JSX_(this.Field, { + name: "firstName" + }, l[1096]), JSX_(this.Field, { + name: "lastName" + }, l[1097])), JSX_(_button_jsx3__ .A, { + className: ` + mega-button + positive + large + ${this.state.firstName.length && this.state.lastName.length ? '' : 'disabled'} + ${this.state.joining && " loading disabled"} + `, + onClick: () => { + if (this.state.joining) { + return; + } + let { + firstName, + lastName, + previewAudio, + previewVideo + } = this.state; + firstName = firstName && firstName.trim(); + lastName = lastName && lastName.trim(); + if (firstName && lastName && firstName.length > 0 && lastName.length > 0) { + this.setState({ + 'joining': true + }); + if (this.props.chatRoom.scheduledMeeting) { + delay('chat-event-sm-guest-join', () => eventlog(99929)); + } + this.props.onJoinGuestClick(firstName, lastName, previewAudio, previewVideo); + } + } + }, l.join_chat_button)); + this.Account = () => JSX_(this.Card, null, JSX_("h4", null, l.join_meeting), JSX_(_button_jsx3__ .A, { + className: `mega-button positive large ${this.state.joining && " loading disabled"}`, + onClick: () => { + if (!this.state.joining) { + this.setState({ + 'joining': true + }); + this.props.onJoinClick(this.state.previewAudio, this.state.previewVideo); + } + } + }, l.join_chat_button)); + this.Unsupported = () => JSX_("div", { + className: "meetings-unsupported-container" + }, JSX_("i", { + className: "sprite-fm-uni icon-error" + }), JSX_("div", { + className: "unsupported-info" + }, JSX_("h3", null, l.heading_unsupported_browser), JSX_("h3", null, l.join_meeting_methods), JSX_("ul", null, JSX_("li", null, l.join_via_link), JSX_("li", null, JSX_(_ui_utils_jsx2__ .P9, null, l.join_via_mobile.replace('[A]', '').replace('[/A]', '')))))); + this.View = view => { + switch (view) { + default: + return this.Intro(); + case _utils_jsx7__ .j.GUEST: + return this.Guest(); + case _utils_jsx7__ .j.ACCOUNT: + return this.Account(); + case _utils_jsx7__ .j.UNSUPPORTED: + return this.Unsupported(); + } + }; + this.state.view = sessionStorage.guestForced ? _utils_jsx7__ .j.GUEST : props.initialView || this.state.view; + if (localStorage.awaitingConfirmationAccount) { + this.showConfirmationDialog(); + } + } + componentDidMount() { + document.addEventListener('keydown', this.handleKeyDown); + this.hidePanels(); + megaChat._joinDialogIsShown = true; + alarm.hideAllWarningPopups(); + sessionStorage.removeItem('guestForced'); + if (!megaChat.hasSupportForCalls) { + this.setState({ + view: _utils_jsx7__ .j.UNSUPPORTED + }); + } + } + componentWillUnmount() { + document.removeEventListener('keydown', this.handleKeyDown); + this.showPanels(); + megaChat._joinDialogIsShown = false; + if (this.props.onClose) { + this.props.onClose(); + } + } + render() { + const { + view, + ephemeralDialog + } = this.state; + return JSX_(_ui_utils_jsx2__ .Ay.RenderTo, { + element: document.body + }, JSX_("div", { + className: this.NAMESPACE + }, this.Head(), this.View(view), ephemeralDialog && JSX_(this.Ephemeral, null))); + } +} + + }, + + 2914 +(_, EXP_, REQ_) { + +REQ_.r(EXP_); + REQ_.d(EXP_, { + "default": () => Loading + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + +class Loading extends react0___default().Component { + constructor(...args) { + super(...args); + this.domRef = react0___default().createRef(); + this.PERMISSIONS = { + VIDEO: 'camera', + AUDIO: 'microphone' + }; + this.state = { + pendingPermissions: false + }; + this.queryPermissions = name => { + navigator.permissions.query({ + name + }).then(status => { + const { + name, + state + } = status; + status.onchange = () => name === 'audio_capture' && this.queryPermissions(this.PERMISSIONS.VIDEO); + if (state === 'prompt') { + return this.domRef.current && this.setState({ + pendingPermissions: name + }); + } + }).catch(ex => console.warn(`Failed to get permissions state: ${ex}`)); + }; + this.renderLoading = () => { + return JSX_(react0___default().Fragment, null, JSX_("span", null, JSX_("i", { + className: "sprite-fm-mono icon-video-call-filled" + })), JSX_("h3", null, this.props.title || l[5533]), JSX_("div", { + className: "loading-container" + }, JSX_("div", { + className: "loading-indication" + }))); + }; + this.renderDebug = () => { + const { + chatRoom + } = this.props; + if (chatRoom && chatRoom.call) { + return JSX_("div", { + className: `${Loading.NAMESPACE}-debug` + }, JSX_("div", null, "callId: ", chatRoom.call.callId), JSX_("div", null, "roomId: ", chatRoom.roomId), JSX_("div", null, "isMeeting: ", chatRoom.isMeeting ? 'true' : 'false')); + } + }; + } + componentWillUnmount() { + megaChat.unbind(`onLocalMediaQueryError.${Loading.NAMESPACE}`); + } + componentDidMount() { + let _notify, _alarm; + document.dispatchEvent(new Event('closeDropdowns')); + if ($.dialog) { + closeDialog == null || closeDialog(); + } + mega.ui.mInfoPanel.hide(); + (_notify = notify) == null || _notify.closePopup(); + (_alarm = alarm) == null || _alarm.hideAllWarningPopups(); + document.querySelectorAll('.js-dropdown-account').forEach(({ + classList + }) => classList.contains('show') && classList.remove('show')); + const { + chatRoom + } = this.props; + const { + audio, + video + } = chatRoom.meetingsLoading; + const isVideoCall = audio && video; + if (audio && !video) { + this.queryPermissions(this.PERMISSIONS.AUDIO); + } + if (isVideoCall) { + Object.values(this.PERMISSIONS).forEach(name => this.queryPermissions(name)); + } + megaChat.rebind(`onLocalMediaQueryError.${Loading.NAMESPACE}`, (ev, { + type, + err + }) => { + if (isVideoCall && type === 'mic' && String(err).includes('dismissed')) { + this.queryPermissions(this.PERMISSIONS.VIDEO); + } + }); + } + render() { + const { + pendingPermissions + } = this.state; + return JSX_("div", { + ref: this.domRef, + className: Loading.NAMESPACE + }, JSX_("div", { + className: `${Loading.NAMESPACE}-content` + }, pendingPermissions ? JSX_("h2", null, pendingPermissions === 'audio_capture' ? l.permissions_allow_mic : l.permissions_allow_camera) : this.renderLoading()), d ? this.renderDebug() : ''); + } +} +Loading.NAMESPACE = 'meetings-loading'; + + }, + + 3546 +(_, EXP_, REQ_) { + + REQ_.d(EXP_, { + A: () => __WEBPACK_DEFAULT_EXPORT__ + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _mixins_js1__ = REQ_(8264); + const _contacts_jsx2__ = REQ_(8022); + const _utils_jsx3__ = REQ_(3901); + const _button_jsx4__ = REQ_(6740); + const _permissionsObserver_jsx5__ = REQ_(192); + + + + + + +class Preview extends react0___default().Component { + constructor(props) { + super(props); + this.domRef = react0___default().createRef(); + this.videoRef = react0___default().createRef(); + this.stream = null; + this.state = { + audio: false, + video: false, + avatarMeta: undefined + }; + this.getTrackType = type => !type ? 'getTracks' : type === Preview.STREAMS.AUDIO ? 'getAudioTracks' : 'getVideoTracks'; + this.startStream = type => { + this.stopStream(); + const { + audio, + video + } = this.state; + navigator.mediaDevices.getUserMedia({ + audio, + video + }).then(stream => { + const videoRef = this.videoRef.current; + if (videoRef) { + videoRef.srcObject = stream; + this.stream = stream; + if (this.props.onToggle) { + this.props.onToggle(this.state.audio, this.state.video); + } + } + }).catch(ex => { + const stream = type === Preview.STREAMS.AUDIO ? 'audio' : 'video'; + return this.domRef.current && this.setState(state => ({ + [stream]: !state[stream] + }), () => { + megaChat.trigger('onLocalMediaError', { + [type === Preview.STREAMS.AUDIO ? 'mic' : 'camera']: `${ex.name}: ${ex.message}` + }); + console.error(`${ex.name}: ${ex.message}`); + }); + }); + }; + this.stopStream = type => { + if (this.stream) { + const trackType = this.getTrackType(type); + const tracks = this.stream[trackType](); + for (const track of tracks) { + track.stop(); + } + } + }; + this.toggleStream = type => { + let _this$props$resetErro, _this$props; + const stream = type === Preview.STREAMS.AUDIO ? 'audio' : 'video'; + this.setState(state => ({ + [stream]: !state[stream] + }), () => { + if (this.props.onToggle) { + this.props.onToggle(this.state.audio, this.state.video); + } + return this.state[stream] ? this.startStream(type) : this.stopStream(type); + }); + (_this$props$resetErro = (_this$props = this.props).resetError) == null || _this$props$resetErro.call(_this$props, type === Preview.STREAMS.AUDIO ? Av.Audio : Av.Camera); + }; + this.renderAvatar = () => { + if ((0,_utils_jsx3__ .P)()) { + return JSX_("div", { + className: "avatar-guest" + }, JSX_("i", { + className: "sprite-fm-uni icon-owner" + })); + } + if (is_chatlink) { + const { + avatarUrl, + color, + shortName + } = this.state.avatarMeta || {}; + return JSX_("div", { + className: ` + avatar-wrapper + ${color ? `color${color}` : ''} + ` + }, avatarUrl && JSX_("img", { + src: avatarUrl, + alt: "" + }), color && JSX_("span", null, shortName)); + } + return JSX_(_contacts_jsx2__ .eu, { + contact: M.u[u_handle] + }); + }; + this.state.audio = this.props.audio || this.state.audio; + if (this.props.video) { + this.state.video = this.props.video; + this.startStream(Preview.STREAMS.VIDEO); + this.props.onToggle(this.state.audio, this.state.video); + } + } + componentWillUnmount() { + this.stopStream(); + } + componentDidMount() { + if (this.props.onToggle) { + this.props.onToggle(this.state.audio, this.state.video); + } + this.setState({ + avatarMeta: is_chatlink ? generateAvatarMeta(u_handle) : undefined + }); + } + render() { + const { + NAMESPACE + } = Preview; + const { + hasToRenderPermissionsWarning, + renderPermissionsWarning + } = this.props; + const { + audio, + video + } = this.state; + const SIMPLETIP_PROPS = { + label: undefined, + position: 'top', + className: 'theme-dark-forced' + }; + return JSX_("div", { + ref: this.domRef, + className: ` + ${NAMESPACE} + local-stream-mirrored + ` + }, video && JSX_("div", { + className: `${NAMESPACE}-video-overlay` + }), JSX_("video", { + className: video ? 'streaming' : '', + muted: true, + autoPlay: true, + ref: this.videoRef + }), !video && this.renderAvatar(), JSX_("div", { + className: `${NAMESPACE}-controls` + }, JSX_("div", { + className: "preview-control-wrapper" + }, JSX_(_button_jsx4__ .A, { + simpletip: { + ...SIMPLETIP_PROPS, + label: audio ? l[16214] : l[16708] + }, + className: ` + mega-button + round + theme-light-forced + ${NAMESPACE}-control + ${audio ? '' : 'with-fill'} + `, + icon: audio ? 'icon-mic-thin-outline' : 'icon-mic-off-thin-outline', + onClick: () => { + this.toggleStream(Preview.STREAMS.AUDIO); + } + }), JSX_("span", null, l.mic_button), hasToRenderPermissionsWarning(Av.Audio) ? renderPermissionsWarning(Av.Audio) : null), JSX_("div", { + className: "preview-control-wrapper" + }, JSX_(_button_jsx4__ .A, { + simpletip: { + ...SIMPLETIP_PROPS, + label: video ? l[22894] : l[22893] + }, + className: ` + mega-button + round + theme-light-forced + ${NAMESPACE}-control + ${video ? '' : 'with-fill'} + `, + icon: video ? 'icon-video-thin-outline' : 'icon-video-off-thin-outline', + onClick: () => this.toggleStream(Preview.STREAMS.VIDEO) + }), JSX_("span", null, l.camera_button), hasToRenderPermissionsWarning(Av.Camera) ? renderPermissionsWarning(Av.Camera) : null))); + } +} +Preview.NAMESPACE = 'preview-meeting'; +Preview.STREAMS = { + AUDIO: 1, + VIDEO: 2 +}; + const __WEBPACK_DEFAULT_EXPORT__ = (0,_mixins_js1__ .Zz)(_permissionsObserver_jsx5__ .$)(Preview); + + } + +}]); \ No newline at end of file diff --git a/js/chat/bundle.cloud-browser.js b/js/chat/bundle.cloud-browser.js new file mode 100644 index 0000000000..ac014b887b --- /dev/null +++ b/js/chat/bundle.cloud-browser.js @@ -0,0 +1,2643 @@ +/** @file automatically generated, do not edit it. */ +"use strict"; +(self.webpackChunk_meganz_webclient = self.webpackChunk_meganz_webclient || []).push([[313],{ + + 6961 +(_, EXP_, REQ_) { + +// ESM COMPAT FLAG +REQ_.r(EXP_); + +// EXPORTS +REQ_.d(EXP_, { + "default": () => cloudBrowserModalDialog +}); + +// EXTERNAL MODULE: external "React" +const external_React_ = REQ_(1594); +const REaCt = REQ_.n(external_React_); +// EXTERNAL MODULE: ./js/ui/modalDialogs.jsx + 1 modules +const modalDialogs = REQ_(8120); +;// ./js/ui/jsx/fm/viewModeSelector.jsx + +const VIEW_MODE = { + 'GRID': 1, + 'LIST': undefined +}; +const ViewModeSelector = ({ + viewMode, + onChange +}) => { + return JSX_("div", { + className: "chat-fm-view-mode-selector" + }, JSX_("i", { + className: ` + sprite-fm-mono + icon-view-medium-list + ${viewMode ? '' : 'active'} + `, + title: l[5553], + onClick: () => onChange == null ? void 0 : onChange(VIEW_MODE.LIST) + }), JSX_("i", { + className: ` + sprite-fm-mono + icon-view-grid + ${viewMode ? " active" : ""} + `, + title: l[5552], + onClick: () => onChange == null ? void 0 : onChange(VIEW_MODE.GRID) + })); +}; + const viewModeSelector = ViewModeSelector; +// EXTERNAL MODULE: ./js/chat/mixins.js +const mixins = REQ_(8264); +;// ./js/ui/jsx/fm/breadcrumbs.jsx + + +class Breadcrumbs extends mixins.w9 { + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.state = { + 'breadcrumbDropdownVisible': false + }; + this.onGlobalClickHandler = this.onGlobalClickHandler.bind(this); + this.onBreadcrumbNodeClick = this.onBreadcrumbNodeClick.bind(this); + } + getBreadcrumbNodeText(nodeId, prevNodeId) { + const backupsId = M.BackupsId || 'backups'; + switch (nodeId) { + case M.RootID: + return l[164]; + case M.RubbishID: + return l[167]; + case backupsId: + return l.restricted_folder_button; + case 'shares': + return prevNodeId && M.d[prevNodeId] ? M.d[prevNodeId].m : l[5589]; + default: + return M.d[nodeId] && M.d[nodeId].name; + } + } + getBreadcrumbDropdownContents(items) { + const contents = []; + for (const item of items) { + if (!item.name) { + continue; + } + contents.push(JSX_("a", { + className: "crumb-drop-link", + key: `drop_link_${ item.nodeId}`, + onClick: e => this.onBreadcrumbNodeClick(e, item.nodeId) + }, JSX_("i", { + className: `sprite-fm-mono icon24 ${{ + 'cloud-drive': 'icon-cloud', + 'backups': 'icon-database-filled', + 's4-object-storage': 'icon-bucket-triangle-thin-solid', + 's4-buckets': 'icon-bucket-outline' + }[item.type] || 'folder'}` + }), JSX_("span", null, item.name))); + } + return contents; + } + onBreadcrumbNodeClick(e, nodeId) { + e.preventDefault(); + e.stopPropagation(); + if (this._clickToHideListener) { + this.removeGlobalClickHandler(); + this.setState({ + 'breadcrumbDropdownVisible': false + }); + } + this.props.onNodeClick(nodeId); + } + customIsEventuallyVisible() { + return true; + } + onGlobalClickHandler(e) { + let _this$domRef; + const node = (_this$domRef = this.domRef) == null ? void 0 : _this$domRef.current; + if (node.contains(e.target) || node === e.target) { + return; + } + if (this._clickToHideListener) { + this.removeGlobalClickHandler(); + } + this.setState({ + 'breadcrumbDropdownVisible': false + }); + } + removeGlobalClickHandler() { + this._clickToHideListener = false; + document.body.removeEventListener("click", this.onGlobalClickHandler); + } + componentDidUpdate() { + super.componentDidUpdate(); + if (this.state.breadcrumbDropdownVisible) { + if (!this._clickToHideListener) { + this._clickToHideListener = true; + document.body.addEventListener("click", this.onGlobalClickHandler); + } + } else if (this._clickToHideListener) { + this.removeGlobalClickHandler(); + } + } + componentWillUnmount() { + super.componentWillUnmount(); + this.removeGlobalClickHandler(); + } + render() { + const { + className, + highlighted, + currentlyViewedEntry, + isSearch, + path + } = this.props; + const breadcrumb = []; + const extraPathItems = []; + let breadcrumbDropdownContents = []; + const entryId = isSearch ? highlighted[0] : currentlyViewedEntry; + if (entryId !== undefined) { + (path || M.getPath(entryId)).forEach((nodeId, k, path) => { + let breadcrumbClasses = ''; + let folderType = 'folder'; + if (nodeId === M.RootID) { + breadcrumbClasses += " cloud-drive"; + } else { + breadcrumbClasses += " folder"; + } + if (nodeId.length === 11 && M.u[nodeId]) { + return; + } + if (nodeId === "shares") { + breadcrumbClasses += " shared-with-me"; + } + const prevNodeId = path[k - 1]; + let nodeName = this.getBreadcrumbNodeText(nodeId, prevNodeId); + if ('utils' in s4) { + const data = s4.utils.getBreadcrumbsData(nodeId); + if (data) { + ({ + type: folderType, + localeName: nodeName + } = data); + } + } + if (!nodeName) { + return; + } + ((nodeId, k) => { + if (k < 4) { + breadcrumb.unshift(JSX_("a", { + className: `fm-breadcrumbs contains-directories ${ breadcrumbClasses}`, + key: nodeId, + onClick: e => this.onBreadcrumbNodeClick(e, nodeId) + }, JSX_("span", { + className: `right-arrow-bg simpletip`, + "data-simpletip": nodeName + }, JSX_("span", { + className: "selectable-txt" + }, nodeName)), k !== 0 && JSX_("i", { + className: "next-arrow sprite-fm-mono icon-arrow-right icon16" + }))); + } else { + folderType = nodeId === M.RootID ? 'cloud-drive' : folderType; + if (M.BackupsId && nodeId === M.BackupsId) { + folderType = 'backups'; + } + extraPathItems.push({ + name: nodeName, + type: folderType, + nodeId + }); + } + })(nodeId, k); + }); + if (extraPathItems.length > 0) { + breadcrumbDropdownContents = this.getBreadcrumbDropdownContents(extraPathItems); + } + } + return JSX_("div", { + ref: this.domRef, + className: ` + fm-breadcrumbs-wrapper + ${className || ''} + ` + }, JSX_("div", { + className: "fm-breadcrumbs-block" + }, breadcrumbDropdownContents.length ? JSX_(REaCt().Fragment, null, JSX_("div", { + className: "crumb-overflow-link" + }, JSX_("a", { + className: "breadcrumb-dropdown-link dropdown", + onClick: () => { + this.setState({ + breadcrumbDropdownVisible: !this.state.breadcrumbDropdownVisible + }); + } + }, JSX_("i", { + className: "menu-icon sprite-fm-mono icon-options icon16" + })), JSX_("i", { + className: "sprite-fm-mono icon-arrow-right icon16" + })), breadcrumb) : breadcrumb), breadcrumbDropdownContents.length ? JSX_("div", { + className: this.state.breadcrumbDropdownVisible ? 'breadcrumb-dropdown active' : 'breadcrumb-dropdown' + }, breadcrumbDropdownContents) : ''); + } +} +// EXTERNAL MODULE: ./js/ui/jsx/fm/fmView.jsx + 10 modules +const fmView = REQ_(872); +;// ./js/ui/cloudBrowserModalDialog.jsx + + + + + +const MIN_SEARCH_LENGTH = 2; +class CloudBrowserDialog extends modalDialogs.A.SafeShowDialogController { + static getFilterFunction(customFilterFn) { + return tryCatch(n => { + if (n.s4 && n.p === M.RootID && M.getS4NodeType(n) === 'container') { + return false; + } + if (!n.name || missingkeys[n.h] || M.getNodeShare(n).down) { + return false; + } + return !customFilterFn || customFilterFn(n); + }); + } + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.dialogName = 'attach-cloud-dialog'; + this.state = { + 'isActiveSearch': false, + 'selected': [], + 'highlighted': [], + 'currentlyViewedEntry': M.RootID, + 'selectedTab': M.RootID, + 'searchValue': '', + 'searchText': '' + }; + this.onAttachClicked = this.onAttachClicked.bind(this); + this.onClearSearchIconClick = this.onClearSearchIconClick.bind(this); + this.onPopupDidMount = this.onPopupDidMount.bind(this); + this.onSearchChange = this.onSearchChange.bind(this); + this.onSearchIconClick = this.onSearchIconClick.bind(this); + this.onSelected = this.onSelected.bind(this); + this.onHighlighted = this.onHighlighted.bind(this); + this.handleTabChange = this.handleTabChange.bind(this); + this.onViewModeSwitch = this.onViewModeSwitch.bind(this); + this.onBreadcrumbNodeClick = this.onBreadcrumbNodeClick.bind(this); + this.onExpand = this.onExpand.bind(this); + } + onViewModeSwitch(newMode) { + const currentViewMode = mega.config.get('cbvm') | 0; + if (newMode === currentViewMode) { + return; + } + mega.config.set('cbvm', newMode); + this.forceUpdate(); + } + getHeaderButtonsClass() { + const classes = ['fm-header-buttons']; + if (this.state.isActiveSearch) { + classes.push('active-search'); + } + return classes.join(' '); + } + getSearchIconClass() { + const classes = ['sprite-fm-mono', 'icon-preview-reveal']; + if (this.state.isActiveSearch && this.state.searchText.length > 0) { + classes.push('disabled'); + } + return classes.join(' '); + } + onSearchIconClick() { + const isActiveSearch = !this.state.isActiveSearch; + if (isActiveSearch) { + this.searchInput.focus(); + this.setState({ + isActiveSearch + }); + } + } + onClearSearchIconClick() { + this.setState({ + 'isActiveSearch': false, + 'searchValue': '', + 'searchText': '', + 'currentlyViewedEntry': this.state.selectedTab + }); + } + handleTabChange(selectedTab) { + const s4Cn = selectedTab === 's4' && M.tree.s4 && Object.keys(M.tree.s4); + this.clearSelectionAndHighlight(); + this.setState({ + selectedTab, + currentlyViewedEntry: s4Cn && s4Cn.length === 1 ? s4Cn[0] : selectedTab, + searchValue: '', + searchText: '', + isLoading: false + }); + } + onSearchBlur() { + if (this.state.searchText === '') { + this.setState({ + 'isActiveSearch': false + }); + } + } + onSearchChange(e) { + const searchValue = e.target.value; + const newState = { + searchText: searchValue, + nodeLoading: searchValue.length >= MIN_SEARCH_LENGTH + }; + if (searchValue && searchValue.length >= MIN_SEARCH_LENGTH) { + this.setState(newState); + delay('cbd:search-proc', this.searchProc.bind(this), 500); + return; + } + if (this.state.currentlyViewedEntry === 'search' && (!searchValue || searchValue.length < MIN_SEARCH_LENGTH)) { + newState.currentlyViewedEntry = this.state.selectedTab; + newState.searchValue = undefined; + } + this.setState(newState); + this.clearSelectionAndHighlight(); + } + searchProc() { + const { + searchText + } = this.state; + const newState = { + nodeLoading: true + }; + if (searchText && searchText.length >= MIN_SEARCH_LENGTH) { + this.setState(newState); + M.fmSearchNodes(searchText).then(() => { + newState.nodeLoading = false; + newState.searchValue = searchText; + newState.currentlyViewedEntry = 'search'; + this.setState(newState); + this.clearSelectionAndHighlight(); + }); + } + } + onSelected(nodes) { + this.setState({ + 'selected': nodes + }); + this.props.onSelected(nodes); + } + onHighlighted(nodes) { + this.setState({ + 'highlighted': nodes + }); + if (this.props.onHighlighted) { + this.props.onHighlighted(nodes); + } + } + clearSelectionAndHighlight() { + this.onSelected([]); + this.onHighlighted([]); + if (selectionManager) { + selectionManager.clear_selection(); + } + } + onPopupDidMount(elem) { + this.domNode = elem; + } + onAttachClicked() { + this.props.onAttachClicked(); + } + onBreadcrumbNodeClick(nodeId) { + if (nodeId === 'shares' || nodeId === 's4') { + return this.handleTabChange(nodeId); + } + if (M.getNodeByHandle(nodeId).t) { + const nodeRoot = M.getNodeRoot(nodeId); + this.setState({ + selectedTab: nodeRoot === "contacts" ? 'shares' : nodeRoot, + currentlyViewedEntry: nodeId, + selected: [], + searchValue: '', + searchText: '' + }); + } + } + onExpand(nodeId) { + this.setState({ + 'currentlyViewedEntry': nodeId, + 'searchValue': '', + 'searchText': '', + 'selected': [], + 'highlighted': [] + }); + } + render() { + assert(this.dialogBecameVisible); + const self = this; + const viewMode = mega.config.get('cbvm') | 0; + const classes = `add-from-cloud ${self.props.className} dialog-template-tool `; + let folderIsHighlighted = false; + let share = false; + let isS4Cn = false; + const isSearch = this.state.currentlyViewedEntry === 'search'; + const entryId = isSearch ? self.state.highlighted[0] : self.state.currentlyViewedEntry; + const filterFn = CloudBrowserDialog.getFilterFunction(this.props.customFilterFn); + const isIncomingShare = M.getNodeRoot(entryId) === "shares"; + this.state.highlighted.forEach(nodeId => { + if (M.getNodeByHandle(nodeId).t) { + folderIsHighlighted = true; + if (M.tree.s4 && M.tree.s4[nodeId]) { + isS4Cn = true; + } + } + share = M.getNodeShare(nodeId); + }); + const buttons = [{ + "label": this.props.cancelLabel, + "key": "cancel", + "onClick": e => { + e.preventDefault(); + e.stopPropagation(); + if (this.props.onCancel) { + this.props.onCancel(this); + } + this.props.onClose(this); + } + }]; + if (folderIsHighlighted) { + const { + highlighted + } = this.state; + const className = `${share && share.down ? 'disabled' : ''}`; + const highlightedNode = highlighted && highlighted.length && highlighted[0]; + const allowAttachFolders = this.props.allowAttachFolders && !isIncomingShare && !isS4Cn; + buttons.push({ + "label": this.props.openLabel, + "key": "select", + className: `positive ${className} ${highlighted.length > 1 ? 'disabled' : ''}`, + onClick: e => { + e.preventDefault(); + e.stopPropagation(); + if (highlighted.length > 1) { + return; + } + this.setState({ + currentlyViewedEntry: highlightedNode + }); + this.clearSelectionAndHighlight(); + this.setState({ + selected: [], + searchValue: '', + searchText: '', + highlighted: [] + }); + } + }, allowAttachFolders ? { + "label": l[8023], + "key": "attach", + className: `positive ${ className}`, + onClick: () => { + this.props.onClose(); + onIdle(() => { + const createPublicLink = h => { + M.createPublicLink(h).then(({ + link + }) => this.props.room.sendMessage(link)); + }; + const frs = []; + const files = []; + for (let i = 0; i < highlighted.length; i++) { + const node = M.getNodeByHandle(highlighted[i]); + if (node && M.isFileNode(node)) { + if (!M.getNodeShare(node).down) { + files.push(node); + } + } else if (mega.fileRequestCommon.storage.isDropExist(highlighted[i]).length) { + frs.push(highlighted[i]); + } else { + createPublicLink(highlighted[i]); + } + } + if (files.length) { + this.props.onSelected(files); + this.props.onAttachClicked(); + } + if (frs.length) { + const fldName = frs.length > 1 ? l[17626] : l[17403].replace('%1', escapeHTML(M.getNameByHandle(frs[0])) || l[1049]); + msgDialog('confirmation', l[1003], fldName, l[18229], e => { + if (e) { + mega.fileRequest.removeList(frs).then(() => { + for (let i = 0; i < frs.length; i++) { + createPublicLink(frs[i]); + } + }).catch(dump); + } + }); + } + }); + } + } : null); + } + if (!folderIsHighlighted || this.props.folderSelectable && (!this.props.noShareFolderAttach || !(isIncomingShare && folderIsHighlighted))) { + buttons.push({ + "label": this.props.selectLabel, + "key": "select", + "className": `positive ${ this.state.selected.length === 0 || share && share.down || isS4Cn ? "disabled" : ""}`, + "onClick": e => { + if (this.state.selected.length > 0) { + this.props.onSelected(this.state.selected); + this.props.onAttachClicked(); + } + e.preventDefault(); + e.stopPropagation(); + } + }); + } + let clearSearchBtn = null; + if (self.state.searchText.length >= MIN_SEARCH_LENGTH) { + clearSearchBtn = JSX_("i", { + className: "sprite-fm-mono icon-close-component", + onClick: () => { + self.onClearSearchIconClick(); + } + }); + } + const breadcrumbPath = M.getPath(entryId); + return JSX_(modalDialogs.A.ModalDialog, { + title: self.props.title || l[8011], + className: classes + (isSearch && this.state.selected.length > 0 ? 'has-breadcrumbs-bottom' : '') + this.dialogName, + onClose: () => { + self.props.onClose(self); + }, + dialogName: "add-from-cloud-dialog dialog-template-tool", + popupDidMount: self.onPopupDidMount, + buttons + }, JSX_("section", { + ref: this.domRef, + className: "content" + }, JSX_("div", { + className: "content-block" + }, JSX_("div", { + className: "fm-dialog-tabs" + }, JSX_("div", { + className: ` + fm-dialog-tab cloud + ${self.state.selectedTab === M.RootID ? 'active' : ''} + `, + onClick: () => self.handleTabChange(M.RootID) + }, l[164]), JSX_("div", { + className: ` + fm-dialog-tab incoming + ${self.state.selectedTab === 'shares' ? 'active' : ''} + `, + onClick: () => self.handleTabChange('shares') + }, l[5542]), JSX_("div", { + className: ` + fm-dialog-tab s4 + ${self.state.selectedTab === 's4' ? 'active' : ''} + ${u_attr.s4 ? '' : 'hidden'} + `, + onClick: () => self.handleTabChange('s4') + }, l.obj_storage), JSX_("div", { + className: "clear" + })), JSX_("div", { + className: "fm-picker-header" + }, JSX_("div", { + className: self.getHeaderButtonsClass() + }, JSX_(viewModeSelector, { + viewMode, + onChange: this.onViewModeSwitch + }), JSX_("div", { + className: "fm-files-search" + }, JSX_("i", { + className: self.getSearchIconClass(), + onClick: () => { + self.onSearchIconClick(); + } + }), JSX_("input", { + ref: input => { + this.searchInput = input; + }, + type: "search", + placeholder: l[102], + value: self.state.searchText, + onChange: self.onSearchChange, + onBlur: () => { + self.onSearchBlur(); + } + }), clearSearchBtn), JSX_("div", { + className: "clear" + })), !isSearch && JSX_(Breadcrumbs, { + className: "add-from-cloud", + nodeId: entryId, + path: breadcrumbPath, + onNodeClick: this.onBreadcrumbNodeClick, + isSearch, + highlighted: this.state.highlighted, + currentlyViewedEntry: this.state.currentlyViewedEntry + })), JSX_(fmView.A, { + nodeLoading: this.state.nodeLoading, + sortFoldersFirst: true, + currentlyViewedEntry: this.state.currentlyViewedEntry, + customFilterFn: filterFn, + folderSelectNotAllowed: this.props.folderSelectNotAllowed, + folderSelectable: this.props.folderSelectable, + onSelected: this.onSelected, + onHighlighted: this.onHighlighted, + onAttachClicked: this.onAttachClicked, + initialSelected: this.state.selected, + initialHighlighted: this.state.highlighted, + searchValue: this.state.searchValue, + minSearchLength: MIN_SEARCH_LENGTH, + onExpand: this.onExpand, + viewMode, + initialSortBy: ['name', 'asc'], + fmConfigSortEnabled: true, + fmConfigSortId: "cbd" + }), isSearch && breadcrumbPath.length > 0 && JSX_("div", { + className: ` + fm-breadcrumbs-wrapper add-from-cloud breadcrumbs-bottom + ` + }, JSX_("div", { + className: "fm-breadcrumbs-block" + }, JSX_(Breadcrumbs, { + nodeId: entryId, + path: breadcrumbPath, + onNodeClick: this.onBreadcrumbNodeClick, + isSearch, + highlighted: this.state.highlighted, + currentlyViewedEntry: this.state.currentlyViewedEntry + }), JSX_("div", { + className: "clear" + })))))); + } +} +CloudBrowserDialog.defaultProps = { + 'selectLabel': l[8023], + 'openLabel': l[1710], + 'cancelLabel': l.msg_dlg_cancel, + 'hideable': true, + 'className': '' +}; + const cloudBrowserModalDialog = CloudBrowserDialog; + + }, + + 872 +(_, EXP_, REQ_) { + + +// EXPORTS +REQ_.d(EXP_, { + A: () => FMView +}); + +// EXTERNAL MODULE: external "React" +const external_React_ = REQ_(1594); +const REaCt = REQ_.n(external_React_); +// EXTERNAL MODULE: ./js/chat/mixins.js +const mixins = REQ_(8264); +// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/extends.js +const esm_extends = REQ_(8168); +// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/applyDecoratedDescriptor.js +const applyDecoratedDescriptor = REQ_(793); +// EXTERNAL MODULE: ./js/ui/perfectScrollbar.jsx +const perfectScrollbar = REQ_(1301); +;// ./js/ui/jsx/megaList/megaList2.jsx + + +let _dec, _class; + + + +const MegaList2 = (_dec = (0,mixins.hG)(30, true), _class = class MegaList2 extends mixins.w9 { + constructor(props) { + super(props); + this._calculated = false; + this._firstRender = true; + this.customIsEventuallyVisible = true; + this.requiresUpdateOnResize = true; + this.adapterChangedDoRepaint = false; + assert(props.listAdapter, 'missing `listAdapter` for MegaList2'); + assert(props.nodeAdapter, 'missing `nodeAdapter` for MegaList2'); + assert(props.entries, 'missing `entries` for MegaList2'); + this.options = { + extraRows: 8, + batchPages: 0, + perfectScrollOptions: { + 'handlers': ['click-rail', 'drag-thumb', 'wheel', 'touch'], + 'minScrollbarLength': 20 + } + }; + this.onPsUserScroll = this.onPsUserScroll.bind(this); + this.thumbsLoadingHandlers = new MapSet(); + this.thumbsThatRequireLoading = new MapSet(); + this.requestThumbnailCb = this.requestThumbnailCb.bind(this); + } + specShouldComponentUpdate(nextProps) { + let invalidate = false; + if (nextProps.listAdapter.prototype.constructor.name !== this.props.listAdapter.prototype.constructor.name || nextProps.entries !== this.props.entries || nextProps.viewMode !== this.props.viewMode) { + invalidate = true; + } + if (nextProps.sortBy !== this.props.sortBy || nextProps.currentlyViewedEntry !== this.props.currentlyViewedEntry) { + invalidate = true; + this.domRef.scrollToY(0); + } + if (invalidate) { + this._calculated = false; + this.adapterChangedDoRepaint = true; + return true; + } + return null; + } + _recalculate() { + if (this._calculated) { + return this._calculated; + } + const calculated = this._calculated = Object.create(null); + lazy(calculated, 'scrollWidth', () => { + return this.domRef.getClientWidth(); + }); + lazy(calculated, 'scrollHeight', () => this.domRef.getClientHeight() - calculated.headerHeight); + lazy(calculated, 'itemWidth', () => { + if (this.props.listAdapter.itemWidth === false) { + return calculated.scrollWidth; + } + return this.props.listAdapter.itemWidth; + }); + lazy(calculated, 'itemHeight', () => { + return this.props.itemHeight || this.props.listAdapter.itemHeight; + }); + lazy(calculated, 'headerHeight', () => this.props.headerHeight || 0); + lazy(calculated, 'contentWidth', () => { + const contentWidth = this.domRef.getContentWidth(); + if (contentWidth) { + return contentWidth; + } + return calculated.itemWidth; + }); + lazy(calculated, 'itemsPerRow', () => { + if (this.props.listAdapter.itemsPerRow) { + return this.props.listAdapter.itemsPerRow; + } + return Math.max(1, Math.floor(calculated.contentWidth / calculated.itemWidth)); + }); + lazy(calculated, 'contentHeight', () => { + return Math.ceil(this.props.entries.length / calculated.itemsPerRow) * calculated.itemHeight; + }); + lazy(calculated, 'scrollLeft', () => { + return this.domRef.getScrollPositionX(); + }); + lazy(calculated, 'scrollTop', () => { + if (this.adapterChangedDoRepaint) { + return 0; + } + return this.domRef.getScrollPositionY(); + }); + lazy(calculated, 'scrolledPercentX', () => { + return 100 / calculated.scrollWidth * calculated.scrollLeft; + }); + lazy(calculated, 'scrolledPercentY', () => { + return 100 / calculated.scrollHeight * calculated.scrollTop; + }); + lazy(calculated, 'isAtTop', () => { + return calculated.scrollTop === 0; + }); + lazy(calculated, 'isAtBottom', () => { + return calculated.scrollTop === calculated.scrollHeight; + }); + lazy(calculated, 'itemsPerPage', () => { + return Math.ceil(calculated.scrollHeight / calculated.itemHeight) * calculated.itemsPerRow; + }); + lazy(calculated, 'visibleFirstItemNum', () => { + let value = 0; + value = Math.floor(Math.floor(calculated.scrollTop / calculated.itemHeight) * calculated.itemsPerRow); + if (value > 0) { + value = Math.max(0, value - this.options.extraRows * calculated.itemsPerRow); + } + return value; + }); + lazy(calculated, 'visibleLastItemNum', () => { + let value = Math.min(this.props.entries.length, Math.ceil(Math.ceil(calculated.scrollTop / calculated.itemHeight) * calculated.itemsPerRow + calculated.itemsPerPage)); + if (value < this.props.entries.length) { + value = Math.min(this.props.entries.length, value + this.options.extraRows * calculated.itemsPerRow); + } + return value; + }); + if (this.options.batchPages > 0) { + const perPage = calculated.itemsPerPage; + const visibleF = calculated.visibleFirstItemNum; + calculated.visibleFirstItemNum = Math.max(0, ((visibleF - visibleF % perPage) / perPage - 1 - this.options.batchPages) * perPage); + const visibleL = calculated.visibleLastItemNum; + calculated.visibleLastItemNum = Math.min(this.props.entries.length, ((visibleL - visibleL % perPage) / perPage + 1 + this.options.batchPages) * perPage); + } + Object.defineProperty(M, 'rmItemsInView', { + get: () => { + const c = this.domRef && this._calculated || !1; + return c.itemsPerPage + c.itemsPerRow | 0; + }, + configurable: true + }); + } + _contentUpdated() { + this._calculated = false; + this._recalculate(); + if (this.listContent && this._lastContentHeight !== this._calculated.contentHeight) { + this._lastContentHeight = this._calculated.contentHeight; + this.listContent.style.height = `${this._calculated.contentHeight }px`; + } + if (this.domRef && this._calculated.scrollHeight + this._calculated.scrollTop > this._calculated.contentHeight) { + this.domRef.scrollToY(this._calculated.contentHeight - this._calculated.scrollHeight); + } + if (this.listAdapterInstance && this.listAdapterInstance.onContentUpdated) { + this.listAdapterInstance.onContentUpdated(); + } + } + _getCalcsThatTriggerChange() { + return [this.props.entries.length, this._calculated.scrollHeight, this._calculated.itemWidth, this._calculated.itemHeight, this._calculated.contentWidth, this._calculated.itemsPerRow, this._calculated.contentHeight, this._calculated.visibleFirstItemNum, this._calculated.visibleLastItemNum]; + } + indexOfEntry(nodeHandle, prop) { + prop = prop || 'h'; + for (let i = 0; i < this.props.entries.length; i++) { + const entry = this.props.entries[i]; + if (entry[prop] === nodeHandle) { + return i; + } + } + return -1; + } + scrollToItem(nodeHandle) { + const elementIndex = this.indexOfEntry(nodeHandle); + if (elementIndex === -1) { + return false; + } + let shouldScroll = false; + const itemOffsetTop = Math.floor(elementIndex / this._calculated.itemsPerRow) * this._calculated.itemHeight; + const itemOffsetTopPlusHeight = itemOffsetTop + this._calculated.itemHeight; + if (itemOffsetTop < this._calculated.scrollTop || itemOffsetTopPlusHeight > this._calculated.scrollTop + this._calculated.scrollHeight) { + shouldScroll = true; + } + if (shouldScroll) { + this.domRef.scrollToY(itemOffsetTop); + onIdle(() => { + this.safeForceUpdate(); + }); + return true; + } + return false; + } + onPsUserScroll() { + if (!this.isMounted()) { + return; + } + const oldCalc = JSON.stringify(this._getCalcsThatTriggerChange()); + this._contentUpdated(); + const newCalc = JSON.stringify(this._getCalcsThatTriggerChange()); + if (oldCalc !== newCalc) { + this.forceUpdate(); + } + } + onResizeDoUpdate() { + super.onResizeDoUpdate(); + this._contentUpdated(); + } + componentDidMount() { + super.componentDidMount(); + this._contentUpdated(); + this.forceUpdate(); + } + componentDidUpdate() { + super.componentDidUpdate(); + this._contentUpdated(); + if (this.adapterChangedDoRepaint) { + this.adapterChangedDoRepaint = false; + this._calculated = false; + this._recalculate(); + } + if (this.thumbsThatRequireLoading.size) { + delay('chat:mega-list2:thumb-loader', () => this.enqueueThumbnailRetrieval(), 20); + } + this._firstRender = this._firstRender || this.props.viewmode !== M.viewmode; + if (this._firstRender && this.domRef) { + let _this$domRef; + this._firstRender = false; + Ps.update((_this$domRef = this.domRef) == null ? void 0 : _this$domRef.$Node); + } + } + enqueueThumbnailRetrieval() { + const loaders = new Map(this.thumbsLoadingHandlers); + const nodes = new Map(this.thumbsThatRequireLoading); + const pending = []; + const defaultCallback = (n, src, id) => { + let img = document.getElementById(id || `chat_${n.h}`); + if (img && (img = img.querySelector('img'))) { + let _img$parentNode$paren; + img.src = src; + (_img$parentNode$paren = img.parentNode.parentNode) == null || _img$parentNode$paren.classList.add('thumb'); + } + }; + const setSource = n => { + if (thumbnails.has(n.fa)) { + const src = thumbnails.get(n.fa); + const batch = [...nodes.get(n.fa)]; + for (let i = batch.length; i--;) { + const n = batch[i]; + const handlers = [...loaders.get(n.h)]; + for (let i = handlers.length; i--;) { + let callback = handlers[i]; + if (typeof callback !== 'function') { + callback = defaultCallback; + } + tryCatch(() => { + const id = callback(n, src); + if (id) { + defaultCallback(n, src, id); + } + })(); + } + } + return true; + } + }; + for (const [, [n]] of nodes) { + if (!setSource(n)) { + pending.push(n); + } + } + if (pending.length) { + fm_thumbnails('standalone', pending, setSource); + } + this.thumbsLoadingHandlers.clear(); + this.thumbsThatRequireLoading.clear(); + } + requestThumbnailCb(node, immediate, callback) { + if (node && node.fa) { + if (typeof immediate === 'function') { + callback = immediate; + immediate = 0; + } + node.seen = node.seen || -7; + this.thumbsLoadingHandlers.set(node.h, callback); + this.thumbsThatRequireLoading.set(node.fa, node); + delay('chat:mega-list2:thumb-loader', () => this.enqueueThumbnailRetrieval(), immediate || 480); + } + } + render() { + if (this.isMounted() && !this._calculated) { + this._recalculate(); + } + const { + listAdapter, + listAdapterOpts, + entries, + nodeAdapterProps, + viewMode, + header, + onContextMenu + } = this.props; + const className = `${listAdapter.containerClassName } megaList megaList2`; + const first = this._calculated.visibleFirstItemNum; + const last = this._calculated.visibleLastItemNum; + const nodes = []; + for (let i = first; i < last; i++) { + const node = entries[i]; + nodes.push(JSX_(this.props.nodeAdapter, (0,esm_extends.A)({ + key: `${i }_${ node[this.props.keyProp]}`, + h: node[this.props.keyProp], + index: i, + megaList: this, + listAdapter, + node, + calculated: this._calculated, + listAdapterOpts, + onContextMenu, + selected: this.props.selected ? this.props.selected.indexOf(node[this.props.keyProp]) > -1 : false, + highlighted: this.props.highlighted ? this.props.highlighted.indexOf(node[this.props.keyProp]) > -1 : false, + requestThumbnailCb: this.requestThumbnailCb, + keyProp: this.props.keyProp || 'h' + }, nodeAdapterProps))); + } + const listAdapterName = listAdapter.prototype.constructor.name; + return JSX_(REaCt().Fragment, null, JSX_(perfectScrollbar.O, { + key: `ps_${ listAdapterName }_${ viewMode}`, + options: this.options.perfectScrollOptions, + onUserScroll: this.onPsUserScroll, + className, + style: { + 'position': 'relative' + }, + ref: instance => { + this.domRef = instance; + } + }, JSX_(this.props.listAdapter, (0,esm_extends.A)({ + containerClassName: this.props.containerClassName, + key: `ps_${ listAdapterName }_${ this.props.viewMode }_la`, + ref: listAdapterInstance => { + this.listAdapterInstance = listAdapterInstance; + }, + listContentRef: listContent => { + this.listContent = listContent; + }, + header, + megaList: this, + calculated: this._calculated + }, listAdapterOpts), nodes))); + } +}, (0,applyDecoratedDescriptor.A)(_class.prototype, "onPsUserScroll", [_dec], Object.getOwnPropertyDescriptor(_class.prototype, "onPsUserScroll"), _class.prototype), _class); +// EXTERNAL MODULE: ./js/ui/jsx/fm/nodes/genericNodePropsComponent.jsx + 1 modules +const genericNodePropsComponent = REQ_(4285); +;// ./js/ui/jsx/fm/nodes/genericGrid.jsx + + +class GenericGrid extends genericNodePropsComponent.B { + render() { + const { + node, + calculated, + index, + listAdapter, + className, + keyProp + } = this.props; + const style = {}; + listAdapter.repositionItem(node, calculated, index, style); + const toApplySensitive = !!mega.sensitives.isSensitive(node) && (mega.sensitives.showGlobally ? 1 : 2); + let image = null; + let src = null; + let isThumbClass = ""; + if (node.fa && (is_image2(node) || is_video(node))) { + src = thumbnails.get(node.fa); + if (!src) { + this.props.requestThumbnailCb(node); + src = window.noThumbURI || ''; + } + image = src ? JSX_("img", { + alt: "", + src + }) : JSX_("img", { + alt: "" + }); + isThumbClass = " thumb"; + } else { + image = JSX_("img", null); + } + let fileStatusClass = ""; + if (node.fav) { + fileStatusClass += " icon-favourite-filled"; + } + return JSX_("a", { + className: `data-block-view megaListItem ui-droppable ui-draggable ui-draggable-handle ${ this.nodeProps.classNames.join(" ") }${className && className(node) || "" }${toApplySensitive ? toApplySensitive === 1 ? ' is-sensitive' : ' hidden-as-sensitive' : ''}`, + id: `chat_${ node[keyProp]}`, + onClick: e => { + this.props.onClick(e, this.props.node); + }, + onDoubleClick: e => { + this.props.onDoubleClick(e, this.props.node); + }, + title: this.nodeProps.title, + style + }, JSX_("span", { + className: `data-block-bg ${ isThumbClass}` + }, JSX_("span", { + className: "data-block-indicators" + }, JSX_("span", { + className: `file-status-icon indicator sprite-fm-mono${ fileStatusClass}` + }), JSX_("span", { + className: "versioning-indicator" + }, JSX_("i", { + className: "sprite-fm-mono icon-versions-previous" + })), JSX_("i", { + className: "sprite-fm-mono icon-link" + })), JSX_("span", { + className: `item-type-icon-90 icon-${ this.nodeProps.icon }-90` + }, image), JSX_("div", { + className: "video-thumb-details" + }, JSX_("i", { + className: "small-icon small-play-icon" + }), JSX_("span", null, "00:00"))), JSX_("span", { + className: "file-block-title" + }, this.nodeProps.title)); + } +} +;// ./js/ui/jsx/fm/nodes/genericTable.jsx + + + +class GenericTableHeader extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + } + render() { + const { + sortBy, + columns + } = this.props; + const columnsRendered = []; + for (let i = 0; i < columns.length; i++) { + var _colProps; + let col = columns[i]; + let colProps; + if (Array.isArray(col)) { + colProps = col[1]; + col = col[0]; + } + let sortable; + if (col.sortable) { + let classes = ""; + if (sortBy[0] === col.id) { + const ordClass = sortBy[1] === "desc" ? "icon-arrow-down" : "icon-arrow-up"; + classes = `${classes} ${ordClass}`; + } + if (col.id === 'fav') { + classes += ' hidden'; + } + sortable = JSX_("i", { + className: `sprite-fm-mono ${col.id} ${classes}` + }); + } + columnsRendered.push(JSX_("th", { + megatype: col.megatype, + className: col.headerClassName || col.megatype || "", + key: `${col.id }_${ i}`, + onClick: e => { + e.preventDefault(); + if (col.sortable) { + this.props.onClick(col.id); + } + } + }, JSX_("span", null, ((_colProps = colProps) == null ? void 0 : _colProps.label) || col.label), col.icon && JSX_("i", { + className: `sprite-fm-mono ${ col.icon}` + }), sortable)); + } + return JSX_("thead", { + ref: this.domRef + }, JSX_("tr", null, columnsRendered)); + } +} +class GenericTable extends genericNodePropsComponent.B { + render() { + let _this$nodeProps; + const { + node, + index, + listAdapterOpts, + className, + keyProp + } = this.props; + const toApplySensitive = !!mega.sensitives.isSensitive(node) && (mega.sensitives.showGlobally ? 1 : 2); + const columns = []; + for (let i = 0; i < listAdapterOpts.columns.length; i++) { + const customColumn = listAdapterOpts.columns[i]; + if (Array.isArray(customColumn)) { + columns.push(JSX_(customColumn[0], { + ...customColumn[1], + 'nodeAdapter': this, + 'h': node[keyProp], + node, + 'key': `${i }_${ customColumn[0].prototype.constructor.name}`, + keyProp + })); + } else { + columns.push(JSX_(customColumn, { + 'nodeAdapter': this, + 'h': node[keyProp], + node, + 'key': `${i }_${ customColumn.prototype.constructor.name}`, + keyProp + })); + } + } + const listClassName = listAdapterOpts.className; + return JSX_("tr", { + className: `node_${ node[keyProp] } ${ className && className(node) || "" } ${ listClassName && listClassName(node) || "" } ${ (_this$nodeProps = this.nodeProps) == null ? void 0 : _this$nodeProps.classNames.join(" ") }${toApplySensitive ? toApplySensitive === 1 ? ' is-sensitive' : ' hidden-as-sensitive' : ''}`, + id: node[keyProp], + onContextMenu: ev => { + if (this.props.onContextMenu) { + this.props.onContextMenu(ev, node[keyProp]); + } + }, + onClick: e => { + this.props.onClick(e, this.props.node); + }, + onDoubleClick: e => { + this.props.onDoubleClick(e, this.props.node); + }, + key: `${index }_${ node[keyProp]}` + }, columns); + } +} +;// ./js/ui/jsx/megaList/adapters.jsx + + +class GenericListAdapter extends mixins.w9 { + constructor(...args) { + super(...args); + this.customIsEventuallyVisible = true; + } +} +class Grid extends GenericListAdapter { + static repositionItem(node, calculated, index, style) { + style.position = "absolute"; + style.top = calculated.itemHeight * Math.floor(index / calculated.itemsPerRow); + if (calculated.itemsPerRow > 1) { + style.left = index % calculated.itemsPerRow * calculated.itemWidth; + } + } + render() { + return JSX_("div", { + className: "megaList-content", + ref: this.props.listContentRef, + style: { + 'position': 'relative' + } + }, this.props.children); + } +} +Grid.itemWidth = 212; +Grid.itemHeight = 212; +Grid.containerClassName = "file-block-scrolling megaListContainer"; +class Table extends GenericListAdapter { + onContentUpdated() { + const { + calculated + } = this.props; + const pusherHeight = calculated.visibleFirstItemNum * calculated.itemHeight | 0; + if (this.topPusher) { + this.topPusher.style.height = `${pusherHeight }px`; + } + if (this.bottomPusher) { + this.bottomPusher.style.height = `${calculated.contentHeight - pusherHeight - (calculated.visibleLastItemNum - calculated.visibleFirstItemNum) * calculated.itemHeight | 0 }px`; + } + } + componentDidUpdate() { + super.componentDidUpdate(); + this.onContentUpdated(); + } + render() { + return JSX_("table", { + width: "100%", + className: this.props.containerClassName || "grid-table table-hover fm-dialog-table" + }, this.props.header, JSX_("tbody", { + ref: this.props.listContentRef + }, JSX_("tr", { + className: "megalist-pusher top", + ref: r => { + this.topPusher = r; + } + }), this.props.children, JSX_("tr", { + className: "megalist-pusher bottom", + ref: r => { + this.bottomPusher = r; + } + }))); + } +} +Table.itemHeight = 32; +Table.itemsPerRow = 1; +Table.containerClassName = "grid-scrolling-table megaListContainer"; +// EXTERNAL MODULE: ./js/ui/jsx/fm/nodes/columns/columnFavIcon.jsx +const columnFavIcon = REQ_(6794); +;// ./js/ui/tooltips.jsx + + +class Handler extends REaCt().Component { + render() { + const { + className, + onMouseOver, + onMouseOut, + children + } = this.props; + return JSX_("span", { + className: ` + tooltip-handler + ${className || ''} + `, + onMouseOver, + onMouseOut + }, children); + } +} +class Contents extends REaCt().Component { + render() { + let className = `tooltip-contents dropdown body tooltip ${ this.props.className ? this.props.className : ""}`; + if (this.props.active) { + className += " visible"; + return JSX_("div", { + className + }, this.props.withArrow ? JSX_("i", { + className: "dropdown-white-arrow" + }) : null, this.props.children); + } else { + return null; + } + } +} +class Tooltip extends mixins.w9 { + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.state = { + 'active': false + }; + } + componentDidUpdate(oldProps, oldState) { + const self = this; + if (oldState.active === true && this.state.active === false) { + chatGlobalEventManager.removeEventListener('resize', `tooltip${ this.getUniqueId()}`); + } + if (self.state.active === true) { + self.repositionTooltip(); + chatGlobalEventManager.addEventListener('resize', `tooltip${ this.getUniqueId()}`, () => { + self.repositionTooltip(); + }); + if (this.props.onShown) { + this.props.onShown(); + } + } + } + repositionTooltip() { + let _this$domRef; + let elLeftPos, elTopPos, elWidth, elHeight; + let tooltipLeftPos, tooltipTopPos, tooltipWidth, tooltipHeight; + let docHeight; + let arrowClass; + if (!this.isMounted()) { + return; + } + const $container = $((_this$domRef = this.domRef) == null ? void 0 : _this$domRef.current); + const $el = $('.tooltip-handler', $container); + const $tooltip = $('.tooltip-contents', $container); + let {tooltipOffset} = this.props; + const arrow = this.props.withArrow; + if ($el && $tooltip) { + elWidth = $el.outerWidth(); + elHeight = $el.outerHeight(); + elLeftPos = $el.offset().left; + elTopPos = $el.offset().top; + tooltipWidth = $tooltip.outerWidth(); + tooltipHeight = $tooltip.outerHeight(); + docHeight = $(window).height(); + $tooltip.removeClass('dropdown-arrow left-arrow right-arrow up-arrow down-arrow').removeAttr('style'); + if (!tooltipOffset) { + tooltipOffset = 7; + } + if (elTopPos - tooltipHeight - tooltipOffset > 10) { + tooltipLeftPos = elLeftPos + elWidth / 2 - tooltipWidth / 2; + tooltipTopPos = elTopPos - tooltipHeight - tooltipOffset; + arrowClass = arrow ? 'dropdown-arrow down-arrow' : ''; + } else if (docHeight - (elTopPos + elHeight + tooltipHeight + tooltipOffset) > 10) { + tooltipLeftPos = elLeftPos + elWidth / 2 - tooltipWidth / 2; + tooltipTopPos = elTopPos + elHeight + tooltipOffset; + arrowClass = arrow ? 'dropdown-arrow up-arrow' : ''; + } else if (elLeftPos - tooltipWidth - tooltipOffset > 10) { + tooltipLeftPos = elLeftPos - tooltipWidth - tooltipOffset; + tooltipTopPos = elTopPos + elHeight / 2 - tooltipHeight / 2; + arrowClass = arrow ? 'dropdown-arrow right-arrow' : ''; + } else { + tooltipLeftPos = elLeftPos + elWidth + tooltipOffset; + tooltipTopPos = elTopPos + elHeight / 2 - tooltipHeight / 2; + arrowClass = arrow ? 'dropdown-arrow left-arrow' : ''; + } + $tooltip.css({ + 'left': tooltipLeftPos, + 'top': tooltipTopPos - 5 + }); + $tooltip.addClass(arrowClass); + } + } + onHandlerMouseOver() { + this.setState({ + 'active': true + }); + } + onHandlerMouseOut() { + this.setState({ + 'active': false + }); + } + render() { + const self = this; + const others = []; + let handler = null; + let contents = null; + let x = 0; + REaCt().Children.forEach(this.props.children, (child) => { + if (child.type.name === 'Handler') { + handler = REaCt().cloneElement(child, { + onMouseOver () { + self.onHandlerMouseOver(); + }, + onMouseOut () { + self.onHandlerMouseOut(); + } + }); + } else if (child.type.name === 'Contents') { + contents = REaCt().cloneElement(child, { + active: self.state.active, + withArrow: self.props.withArrow + }); + } else { + const tmp = REaCt().cloneElement(child, { + key: x++ + }); + others.push(tmp); + } + }); + return JSX_("span", { + ref: this.domRef, + className: this.props.className || '' + }, handler, contents, others); + } +} +Tooltip.defaultProps = { + 'hideable': true +}; + const tooltips = { + Tooltip, + Handler, + Contents +}; +;// ./js/ui/jsx/fm/nodes/columns/columnNodeName.jsx + + + +class ColumnNodeName extends genericNodePropsComponent.B { + constructor(...args) { + super(...args); + this.state = { + src: null + }; + } + static get label() { + return l[86]; + } + componentDidMount() { + super.componentDidMount(); + } + render() { + const { + nodeAdapter + } = this.props; + const { + node, + requestThumbnailCb + } = nodeAdapter.props; + const src = this.state.src || thumbnails.get(node.fa); + return JSX_("td", { + megatype: ColumnNodeName.megatype + }, src || is_image2(node) || is_video(node) ? JSX_(tooltips.Tooltip, { + withArrow: true, + className: "tooltip-handler-container", + onShown: () => { + if (!src) { + requestThumbnailCb(node, true, (n, src) => { + this.setState({ + src + }); + return `preview_${n.h}`; + }); + } + } + }, JSX_(tooltips.Handler, { + className: `item-type-icon icon-${fileIcon(node)}-24` + }), JSX_(tooltips.Contents, { + className: "img-preview" + }, JSX_("div", { + className: "dropdown img-wrapper img-block", + id: `preview_${node.h}` + }, JSX_("img", { + alt: "", + className: `thumbnail-placeholder ${node.h}`, + src: node.fa || src ? src || `${staticpath}/images/mega/ajax-loader-tiny.gif` : window.noThumbURI + })))) : JSX_("span", { + className: ` + item-type-icon icon-${fileIcon(node)}-24 + ` + }), JSX_("span", { + className: "tranfer-filetype-txt" + }, nodeAdapter.nodeProps.title)); + } +} +ColumnNodeName.sortable = true; +ColumnNodeName.id = 'name'; +ColumnNodeName.megatype = 'fname'; +;// ./js/ui/jsx/fm/nodes/columns/columnSize.jsx + + +class ColumnSize extends genericNodePropsComponent.B { + static get label() { + return l[87]; + } + render() { + const { + nodeAdapter + } = this.props; + return JSX_("td", { + megatype: ColumnSize.megatype, + className: "size" + }, nodeAdapter.nodeProps.size); + } +} +ColumnSize.sortable = true; +ColumnSize.id = "size"; +ColumnSize.megatype = "size"; +;// ./js/ui/jsx/fm/nodes/columns/columnTimeAdded.jsx + + +class ColumnTimeAdded extends genericNodePropsComponent.B { + static get label() { + return l[16169]; + } + render() { + const { + nodeAdapter + } = this.props; + return JSX_("td", { + megatype: ColumnTimeAdded.megatype, + className: "time ad" + }, nodeAdapter.nodeProps.timestamp); + } +} +ColumnTimeAdded.sortable = true; +ColumnTimeAdded.id = "ts"; +ColumnTimeAdded.megatype = "timeAd"; +;// ./js/ui/jsx/fm/nodes/columns/columnExtras.jsx + + +class ColumnExtras extends genericNodePropsComponent.B { + render() { + return JSX_("td", { + megatype: ColumnExtras.megatype, + className: "grid-url-field own-data extras-column" + }, JSX_("span", { + className: "versioning-indicator" + }, JSX_("i", { + className: "sprite-fm-mono icon-versions-previous" + })), JSX_("i", { + className: "sprite-fm-mono icon-link" + })); + } +} +ColumnExtras.sortable = false; +ColumnExtras.id = "extras"; +ColumnExtras.label = ""; +ColumnExtras.megatype = "extras"; +ColumnExtras.headerClassName = "grid-url-header"; +;// ./js/ui/jsx/fm/browserEntries.jsx + + + + + + + + + + + +class BrowserEntries extends mixins.w9 { + constructor(props) { + super(props); + this.state = { + 'sortBy': props.sortBy || ['name', 'asc'] + }; + this.toggleSortBy = this.toggleSortBy.bind(this); + } + UNSAFE_componentWillMount() { + this.lastCharKeyPressed = false; + this.lastCharKeyIndex = -1; + } + componentDidMount() { + super.componentDidMount(); + this.bindEvents(); + } + componentWillUnmount() { + super.componentWillUnmount(); + this.unbindEvents(); + } + componentDidUpdate(oldProps) { + if (oldProps.sortBy && (oldProps.sortBy[0] !== this.props.sortBy[0] || oldProps.sortBy[1] !== this.props.sortBy[1])) { + this.setState({ + 'sortBy': this.props.sortBy + }); + } + } + handleKeyNavigation(selectionManager, shiftKey, keyCode, viewMode) { + let curr; + const { + folderSelectNotAllowed + } = this.props; + if (shiftKey && folderSelectNotAllowed) { + curr = selectionManager.last_selected; + } + const {KEYS} = BrowserEntries; + if (viewMode) { + if (keyCode === KEYS.LEFT) { + selectionManager.select_prev(shiftKey, true); + } else if (keyCode === KEYS.RIGHT) { + selectionManager.select_next(shiftKey, true); + } else if (keyCode === KEYS.UP) { + selectionManager.select_grid_up(shiftKey, true); + } else { + selectionManager.select_grid_down(shiftKey, true); + } + } else if (keyCode === KEYS.UP) { + selectionManager.select_prev(shiftKey, true); + } else { + selectionManager.select_next(shiftKey, true); + } + if (shiftKey && folderSelectNotAllowed && $.selected.length > 1) { + const folderNodes = $.selected.filter(n => !M.isFileNode(M.getNodeByHandle(n))); + if (folderNodes.length > 1) { + if (!M.isFileNode(M.getNodeByHandle(curr))) { + array.remove(folderNodes, curr); + } + if (folderNodes.length) { + const newCurr = selectionManager.last_selected; + for (let i = 0; i < folderNodes.length; i++) { + selectionManager.remove_from_selection(folderNodes[i]); + } + if (M.isFileNode(M.getNodeByHandle(newCurr))) { + selectionManager.set_currently_selected(curr); + } else if (curr && $.selected.includes(curr)) { + selectionManager.set_currently_selected(curr); + } else if ($.selected.length) { + selectionManager.set_currently_selected($.selected[0]); + } + } + } + } + } + _invalidKeydownTarget(e) { + return e.target && (e.target.tagName === 'INPUT' || e.target.tagName === 'BUTTON' || e.target.tagName === 'TEXTAREA' && !e.target.classList.contains('messages-textarea') || e.target.tagName === 'SELECT'); + } + _isNavigationKeyDown(e, keyCode) { + const { + KEYS + } = BrowserEntries; + const { + viewMode + } = this.props; + return !e.metaKey && (!viewMode && (keyCode === KEYS.UP || keyCode === KEYS.DOWN) || viewMode && (keyCode === KEYS.UP || keyCode === KEYS.DOWN || keyCode === KEYS.LEFT || keyCode === KEYS.RIGHT)); + } + bindEvents() { + const { + KEYS + } = BrowserEntries; + $(document.body).rebind(`keydown.be${this.getUniqueId()}`, e => { + let charTyped = false; + const keyCode = e.which || e.keyCode; + const $searchField = $('div.fm-files-search input'); + const $typingArea = $('textarea.messages-textarea'); + const { + selectionManager, + viewMode + } = this.props; + if (this._invalidKeydownTarget(e)) { + return; + } + if ($searchField.is(':focus')) { + return; + } + if ($typingArea.is(':focus')) { + $typingArea.trigger('blur'); + } + if (keyCode === KEYS.A && (e.ctrlKey || e.metaKey)) { + this.handleSelectAll(); + e.preventDefault(); + e.stopPropagation(); + } else if (e.metaKey && keyCode === KEYS.UP || keyCode === KEYS.BACKSPACE) { + this.handleKeyBack(); + } else if (this._isNavigationKeyDown(e, keyCode)) { + this.handleKeyNavigation(selectionManager, e.shiftKey, keyCode, viewMode); + } else if (keyCode >= 48 && keyCode <= 57 || keyCode >= 65 && keyCode <= 123 || keyCode > 255) { + charTyped = String.fromCharCode(keyCode).toLowerCase(); + this.handleCharTyped(charTyped); + } else if (keyCode === KEYS.ENTER || e.metaKey && keyCode === KEYS.DOWN) { + this.handleAttach(); + } + mega.ui.mInfoPanel.reRenderIfVisible($.selected); + if (!charTyped) { + this.lastCharKeyPressed = false; + this.lastCharKeyIndex = -1; + } + }); + } + handleSelectAll() { + const { + selectionManager, + folderSelectNotAllowed, + entries + } = this.props; + selectionManager.select_all(); + if (folderSelectNotAllowed) { + const folders = entries.filter(h => !M.isFileNode(M.getNodeByHandle(h))); + for (let i = 0; i < folders.length; i++) { + selectionManager.remove_from_selection(folders[i].h); + } + } + } + handleKeyBack() { + const { + viewMode, + currentlyViewedEntry + } = this.props; + if (!viewMode) { + const currentFolder = M.getNode(currentlyViewedEntry); + if (currentFolder.p) { + this.expandFolder(currentFolder.p); + } + } + } + handleCharTyped(charTyped) { + const { + entries, + keyProp, + selectionManager + } = this.props; + const foundMatchingNodes = entries.filter(node => { + return node.name && node.name.substring(0, 1).toLowerCase() === charTyped; + }); + if (this.lastCharKeyPressed === charTyped) { + this.lastCharKeyIndex++; + } + this.lastCharKeyPressed = charTyped; + if (foundMatchingNodes.length > 0) { + if (!foundMatchingNodes[this.lastCharKeyIndex]) { + this.lastCharKeyIndex = 0; + } + const foundNode = foundMatchingNodes[this.lastCharKeyIndex]; + selectionManager.clear_selection(); + selectionManager.set_currently_selected(foundNode[keyProp], true); + } + } + handleAttach() { + const { + highlighted, + folderSelectNotAllowed, + entries, + keyProp, + onAttachClicked + } = this.props; + let selectedNodes = highlighted; + if (folderSelectNotAllowed) { + selectedNodes = highlighted.filter(h => { + const node = entries.find(e => e[keyProp] === h); + return node && node.t === 0; + }); + if (selectedNodes.length === 0) { + const cursorNode = highlighted[0] && M.getNodeByHandle(highlighted[0]); + if (cursorNode.t === 1) { + this.expandFolder(cursorNode[keyProp]); + return; + } else if (highlighted.length > 0) { + this.expandFolder(highlighted[0]); + return; + } + return; + } + } + onAttachClicked(selectedNodes); + } + unbindEvents() { + $(document.body).off(`keydown.be${ this.getUniqueId()}`); + } + onEntryClick(e, node) { + const { + selectionManager, + keyProp, + folderSelectNotAllowed, + highlighted = [] + } = this.props; + this.lastCharKeyPressed = false; + this.lastCharKeyIndex = -1; + e.stopPropagation(); + e.preventDefault(); + if (!e.shiftKey && !e.ctrlKey && !e.metaKey) { + selectionManager.clear_selection(); + selectionManager.set_currently_selected(node[keyProp]); + } else if (e.shiftKey) { + if ($.selected && $.selected.length) { + let selFolders; + if (folderSelectNotAllowed) { + selFolders = $.selected.filter(n => !M.isFileNode(M.getNodeByHandle(n))); + } + selectionManager.shift_select_to(node[keyProp], false, true, false); + if (folderSelectNotAllowed && $.selected.length > 1) { + const folderNodes = $.selected.filter(n => !M.isFileNode(M.getNodeByHandle(n))); + if (folderNodes.length > 1) { + array.remove(folderNodes, selFolders[0] || folderNodes[0]); + for (let i = 0; i < folderNodes.length; i++) { + selectionManager.remove_from_selection(folderNodes[i]); + } + } + } + } else { + selectionManager.set_currently_selected(node[keyProp]); + } + } else if (e.ctrlKey || e.metaKey) { + if (!highlighted || !highlighted.includes(node[keyProp])) { + if (folderSelectNotAllowed) { + if (node.t === 1 && highlighted.length > 0) { + return; + } else if (highlighted.some(nodeId => { + const node = M.getNodeByHandle(nodeId); + return node && node.t === 1; + })) { + selectionManager.clear_selection(); + } + } + selectionManager.add_to_selection(node[keyProp]); + } else if (highlighted && highlighted.includes(node[keyProp])) { + if (folderSelectNotAllowed) { + if (node.t === 1) { + return; + } else if (highlighted.some(nodeId => { + const node = M.getNodeByHandle(nodeId); + return node && node.t === 1; + })) { + selectionManager.clear(); + } + } + selectionManager.remove_from_selection(node[keyProp]); + } + } + } + expandFolder(nodeId) { + const self = this; + const node = M.getNodeByHandle(nodeId); + if (node) { + self.lastCharKeyPressed = false; + self.lastCharKeyIndex = -1; + self.setState({ + 'selected': [], + 'highlighted': [], + 'cursor': false + }); + self.props.onExpand(node); + self.forceUpdate(); + } + } + onEntryDoubleClick(e, node) { + const self = this; + self.lastCharKeyPressed = false; + self.lastCharKeyIndex = -1; + e.stopPropagation(); + e.preventDefault(); + const share = M.getNodeShare(node); + if (share && share.down) { + return; + } + if (node.t) { + self.props.onExpand(node); + self.forceUpdate(); + } else { + self.onEntryClick(e, node); + self.props.onAttachClicked(); + } + } + customIsEventuallyVisible() { + return true; + } + toggleSortBy(colId) { + const newState = {}; + if (this.state.sortBy[0] === colId) { + newState.sortBy = [colId, this.state.sortBy[1] === "asc" ? "desc" : "asc"]; + } else { + newState.sortBy = [colId, "asc"]; + } + this.setState(newState); + this.props.onSortByChanged(newState.sortBy); + } + render() { + const {viewMode} = this.props; + const listAdapterOpts = this.props.listAdapterOpts || {}; + if (!viewMode) { + listAdapterOpts.columns = [columnFavIcon.$, ColumnNodeName, ColumnSize, ColumnTimeAdded, ColumnExtras]; + } + if (this.props.listAdapterColumns) { + listAdapterOpts.columns = this.props.listAdapterColumns; + } + if (this.props.isLoading) { + return JSX_("div", { + className: "dialog-empty-block active dialog-fm folder" + }, JSX_("div", { + className: "dialog-empty-pad" + }, JSX_("i", { + className: "sprite-fm-mono icon-cloud-drive" + }), JSX_("div", { + className: "dialog-empty-header" + }, l[5533]))); + } else if (!this.props.entries.length && this.props.currentlyViewedEntry === 'search') { + return JSX_("div", { + className: "dialog-empty-block active dialog-fm folder" + }, JSX_("div", { + className: "dialog-empty-pad" + }, JSX_("i", { + className: "sprite-fm-mono icon-preview-reveal" + }), JSX_("div", { + className: "dialog-empty-header" + }, l[978]))); + } else if (!this.props.entries.length) { + const nilComp = this.props.NilComponent; + return nilComp && (typeof nilComp === "function" ? nilComp() : nilComp) || JSX_("div", { + className: "dialog-empty-block active dialog-fm folder" + }, this.props.currentlyViewedEntry === 'shares' ? JSX_("div", { + className: "dialog-empty-pad" + }, JSX_("i", { + className: "sprite-fm-mono icon-folder-incoming-share-filled" + }), JSX_("div", { + className: "dialog-empty-header" + }, l[6871])) : JSX_("div", { + className: "dialog-empty-pad" + }, JSX_("i", { + className: "sprite-fm-mono icon-folder-filled" + }), JSX_("div", { + className: "dialog-empty-header" + }, this.props.currentlyViewedEntry === M.RootID ? l[1343] : M.u[this.props.currentlyViewedEntry] ? l[6787] : l[782]))); + } + return JSX_(MegaList2, { + viewMode, + sortBy: this.state.sortBy, + currentlyViewedEntry: this.props.currentlyViewedEntry, + selected: this.props.selected, + highlighted: this.props.highlighted, + containerClassName: this.props.containerClassName, + nodeAdapterProps: { + 'onClick': (e, node) => { + this.onEntryClick(e, node); + mega.ui.mInfoPanel.reRenderIfVisible($.selected); + }, + 'onDoubleClick': (e, node) => { + this.onEntryDoubleClick(e, node); + }, + 'className': node => { + return this.props.highlighted.indexOf(node[this.props.keyProp]) > -1 ? " ui-selected" : ""; + } + }, + ref: r => { + this.megaList = r; + }, + listAdapter: viewMode ? Grid : Table, + nodeAdapter: viewMode ? GenericGrid : GenericTable, + listAdapterOpts, + entries: this.props.entries, + itemHeight: this.props.megaListItemHeight, + headerHeight: viewMode ? 0 : 56, + header: !viewMode && JSX_(GenericTableHeader, { + columns: listAdapterOpts.columns, + sortBy: this.state.sortBy, + onClick: this.toggleSortBy, + headerContainerClassName: this.props.headerContainerClassName + }), + currentdirid: this.props.currentdirid, + onContextMenu: this.props.onContextMenu, + keyProp: this.props.keyProp + }); + } +} +BrowserEntries.KEYS = { + A: 65, + UP: 38, + DOWN: 40, + LEFT: 37, + RIGHT: 39, + ENTER: 13, + BACKSPACE: 8 +}; +BrowserEntries.defaultProps = { + 'hideable': true, + 'requiresUpdateOnResize': true +}; +;// ./js/ui/jsx/fm/fmView.jsx + + + +class FMView extends mixins.w9 { + constructor(props) { + let _this$dataSource; + super(props); + this.domRef = REaCt().createRef(); + let initialSortBy = props.initialSortBy || ['name', 'asc']; + if (props.fmConfigSortEnabled) { + let _fmconfig$sortmodes; + const sortId = props.fmConfigSortId; + assert(sortId, 'missing fmConfigSortId'); + if ((_fmconfig$sortmodes = fmconfig.sortmodes) != null && (_fmconfig$sortmodes = _fmconfig$sortmodes[sortId]) != null && _fmconfig$sortmodes.n) { + let _fmconfig$sortmodes2; + initialSortBy = this._translateFmConfigSortMode((_fmconfig$sortmodes2 = fmconfig.sortmodes) == null ? void 0 : _fmconfig$sortmodes2[sortId]); + } + } + this.state = { + 'sortBy': initialSortBy, + 'selected': [], + 'highlighted': [], + 'entries': null + }; + this.dataSource = this.props.dataSource; + this.state.entries = this.getEntries(); + this.onAttachClicked = this.onAttachClicked.bind(this); + this.onContextMenu = this.onContextMenu.bind(this); + if ((_this$dataSource = this.dataSource) != null && _this$dataSource.addChangeListener) { + this._listener = this.dataSource.addChangeListener(() => { + if (!this.isMounted()) { + return; + } + this.setState({ + 'entries': this.getEntries() + }); + }); + } + this.initSelectionManager(); + } + getDataSourceNode(h) { + return this.dataSource && this.dataSource[h] || M.getNodeByHandle(h); + } + _translateFmConfigSortMode(currentSortModes) { + const sortId = this.props.fmConfigSortId; + assert(sortId, 'missing fmConfigSortId'); + const sortByArr = []; + if (currentSortModes != null && currentSortModes.n) { + sortByArr[0] = currentSortModes.n; + const sortMap = this.props.fmConfigSortMap; + const aliasKeys = sortMap && Object.keys(sortMap) || []; + for (const alias of aliasKeys) { + if (sortByArr[0] === sortMap[alias]) { + sortByArr[0] = alias; + break; + } + } + sortByArr[1] = currentSortModes.d === 1 ? "asc" : "desc"; + } + return sortByArr; + } + initSelectionManager(entries) { + this.selectionManager = new SelectionManager2_React(entries || this.state.entries, this.props.currentdirid || "cloud-drive", () => { + let _this$browserEntries; + return (_this$browserEntries = this.browserEntries) == null || (_this$browserEntries = _this$browserEntries.megaList) == null || (_this$browserEntries = _this$browserEntries._calculated) == null ? void 0 : _this$browserEntries.itemsPerRow; + }, nodeHandle => { + if (this.browserEntries && this.browserEntries.megaList) { + this.browserEntries.megaList.scrollToItem(nodeHandle); + } + }, { + 'onSelectedUpdated': selectedList => { + this.onSelectionUpdated(selectedList); + } + }); + } + onSelectionUpdated(selectedList) { + selectedList = [...selectedList]; + const highlighted = selectedList; + if (this.props.folderSelectNotAllowed && !this.props.folderSelectable) { + selectedList = selectedList.filter(nodeId => !this.getDataSourceNode(nodeId).t); + } + this.setState({ + 'selected': selectedList, + highlighted + }); + this.props.onSelected(selectedList); + this.props.onHighlighted(highlighted); + $.selected = highlighted; + } + getEntries(newState) { + const self = this; + const sortBy = newState && newState.sortBy || self.state.sortBy; + const order = sortBy[1] === "asc" ? 1 : -1; + const entries = []; + let sortFunc, filterFunc, dataSource; + const minSearchLength = self.props.minSearchLength || 3; + const showSen = mega.sensitives.showGlobally; + if (self.props.currentlyViewedEntry === "search" && self.props.searchValue && self.props.searchValue.length >= minSearchLength) { + dataSource = this.dataSource || { + ...M.tnd, + ...M.d + }; + filterFunc = M.getFilterBySearchFn(self.props.searchValue); + } else { + const tmp = M.getChildren(self.props.currentlyViewedEntry) || M.tree[self.props.currentlyViewedEntry] || this.props.dataSource; + dataSource = Object.create(null); + for (const h in tmp) { + const n = this.getDataSourceNode(h); + if (n) { + dataSource[h] = n; + } + } + } + const { + customFilterFn + } = this.props; + for (const h in dataSource) { + const n = dataSource[h]; + const e = n && (!n.h || n.h.length === 8 && crypto_keyok(n) || n.h.length === 11); + const s = e && !n.fv && (showSen || !mega.sensitives.isSensitive(n)); + if (s && (!customFilterFn || customFilterFn(n)) && (!filterFunc || filterFunc(n))) { + entries.push(n); + } + } + if (sortBy[0] === "name") { + sortFunc = M.getSortByNameFn(); + } else if (sortBy[0] === "size") { + sortFunc = M.getSortBySizeFn(); + } else if (sortBy[0] === "ts") { + sortFunc = M.getSortByDateTimeFn(); + } else if (sortBy[0] === "rts") { + sortFunc = M.getSortByRtsFn(); + } else if (sortBy[0] === "status") { + sortFunc = M.getSortByStatusFn(); + } else if (sortBy[0] === "interaction") { + sortFunc = M.getSortByInteractionFn(); + } else if (sortBy[0] === "verification") { + sortFunc = M.getSortByVerificationFn(); + } else if (sortBy[0] === "email") { + sortFunc = M.getSortByEmail(); + } else if (sortBy[0] === 'access') { + sortFunc = (a, b, o) => typeof a.r !== 'undefined' && typeof b.r !== 'undefined' && (a.r < b.r ? -1 : 1) * o; + } else { + sortFunc = M.sortByFavFn(order); + } + const folders = []; + if (this.props.sortFoldersFirst) { + for (let i = entries.length; i--;) { + if (entries[i] && entries[i].t) { + folders.unshift(entries[i]); + entries.splice(i, 1); + } + } + } + folders.sort((a, b) => { + return sortFunc(a, b, order); + }); + entries.sort((a, b) => { + return sortFunc(a, b, order); + }); + return folders.concat(entries); + } + onHighlighted(nodes) { + this.setState({ + 'highlighted': nodes + }); + if (this.props.onHighlighted) { + this.props.onHighlighted(nodes); + } + } + finishedLoading(newState) { + newState.isLoading = false; + newState.entries = this.getEntries(); + this.initSelectionManager(newState.entries); + this.setState(newState); + } + addOrUpdRawListener() { + if (this._rawListener) { + mBroadcaster.removeListener(this._rawListener); + } + this._rawListener = mBroadcaster.addListener(`fmViewUpdate:${ this.props.currentlyViewedEntry}`, () => { + this.setState({ + 'entries': this.getEntries() + }, () => { + if (this.browserEntries.isMounted()) { + this.browserEntries.forceUpdate(); + } + }); + }); + } + componentDidMount() { + let _this$dataSource2; + super.componentDidMount(); + if (!((_this$dataSource2 = this.dataSource) != null && _this$dataSource2.addChangeListener)) { + this.addOrUpdRawListener(); + } + if (this.props.fmConfigSortEnabled) { + this._sortModeListener = mBroadcaster.addListener("fmconfig:sortmodes", sortModes => { + this.onFmConfigSortModeChanged(sortModes); + }); + } + } + componentDidUpdate(prevProps) { + const { + currentlyViewedEntry: currEntry, + searchValue: currSearch + } = this.props; + const { + currentlyViewedEntry: prevEntry, + searchValue: prevSearch + } = prevProps; + const dataSourceChanged = this.props.dataSource !== prevProps.dataSource; + if (dataSourceChanged || prevEntry !== currEntry || currSearch !== prevSearch) { + let _this$dataSource3; + this.dataSource = this.props.dataSource; + const newState = { + 'selected': [], + 'highlighted': [] + }; + if (!((_this$dataSource3 = this.dataSource) != null && _this$dataSource3.addChangeListener)) { + this.addOrUpdRawListener(); + } + const handle = currEntry; + if (handle === 'shares') { + newState.isLoading = true; + this.setState(newState); + dbfetch.geta(Object.keys(M.c.shares || {})).always(() => { + this.finishedLoading(newState); + }); + return; + } + if (this.getDataSourceNode(handle).t && !M.getChildren(handle)) { + this.setState({ + 'isLoading': true + }); + dbfetch.get(handle).always(() => { + this.finishedLoading(newState); + }); + return; + } + const entries = this.getEntries(); + this.initSelectionManager(entries); + this.setState({ + entries + }); + } + } + onAttachClicked() { + this.props.onAttachClicked(); + } + onContextMenu() {} + componentWillUnmount() { + super.componentWillUnmount(); + if (this._listener) { + let _this$dataSource4; + (_this$dataSource4 = this.dataSource) == null || _this$dataSource4.removeChangeListener(this._listener); + } + if (this._rawListener) { + mBroadcaster.removeListener(this._rawListener); + } + if (this._sortModeListener) { + mBroadcaster.removeListener(this._sortModeListener); + } + $.selected = []; + this.selectionManager.destroy(); + this.selectionManager = undefined; + $('.dropdown.body.files-menu.context').css('z-index', ''); + } + onSortByChanged(newState) { + if (newState[0] === this.state.sortBy[0] && newState[1] === this.state.sortBy[1]) { + return; + } + const entries = this.getEntries({ + 'sortBy': newState + }); + this.setState({ + 'sortBy': newState, + entries, + 'selected': [], + 'highlighted': [] + }, () => { + if (this.props.onSortByChanged) { + this.props.onSortByChanged(newState); + } + if (this.props.fmConfigSortEnabled) { + const sortId = this.props.fmConfigSortId; + assert(sortId, 'fmConfigSortId missing'); + if (newState[0] === this.props.initialSortBy[0] && newState[1] === this.props.initialSortBy[1]) { + const sortModes = typeof fmconfig.sortmodes !== 'undefined' ? fmconfig.sortmodes : Object.create(null); + delete sortModes[sortId]; + mega.config.set('sortmodes', sortModes); + return; + } + const map = this.props.fmConfigSortMap || Object.create(null); + const name = map[newState[0]] || newState[0]; + const direction = newState[1] === "asc" ? 1 : -1; + fmsortmode(sortId, name, direction); + } + }); + this.initSelectionManager(entries); + } + onFmConfigSortModeChanged(sortModes) { + const currentSortMode = sortModes[this.props.fmConfigSortId]; + if (!currentSortMode) { + this.onSortByChanged(this.props.initialSortBy || ['name', 'asc']); + } else { + const newSortMode = this._translateFmConfigSortMode(currentSortMode); + if (this.state.sortBy[0] !== newSortMode[0] || this.state.sortBy[1] !== newSortMode[1]) { + this.onSortByChanged(newSortMode); + } + } + } + render() { + return JSX_("div", { + ref: this.domRef, + className: "content-container", + onClick: ev => { + $.hideContextMenu(ev); + } + }, JSX_(BrowserEntries, { + isLoading: this.state.isLoading || this.props.nodeLoading, + currentlyViewedEntry: this.props.currentlyViewedEntry, + entries: this.state.entries || [], + onExpand: node => { + this.setState({ + 'selected': [], + 'highlighted': [] + }); + this.props.onExpand(node[this.props.keyProp || 'h']); + }, + sortBy: this.state.sortBy, + folderSelectNotAllowed: this.props.folderSelectNotAllowed, + onAttachClicked: this.onAttachClicked, + viewMode: this.props.viewMode, + selected: this.state.selected, + highlighted: this.state.highlighted, + onContextMenu: this.props.onContextMenu || this.onContextMenu, + selectionManager: this.selectionManager, + ref: browserEntries => { + this.browserEntries = browserEntries; + }, + onSortByChanged: newState => { + this.onSortByChanged(newState); + }, + listAdapterColumns: this.props.listAdapterColumns, + currentdirid: this.props.currentdirid, + containerClassName: this.props.containerClassName, + headerContainerClassName: this.props.headerContainerClassName, + megaListItemHeight: this.props.megaListItemHeight, + keyProp: this.props.keyProp || 'h', + NilComponent: this.props.NilComponent, + listAdapterOpts: this.props.listAdapterOpts + })); + } +} + + }, + + 6794 +(_, EXP_, REQ_) { + + REQ_.d(EXP_, { + $: () => ColumnFavIcon + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _genericNodePropsComponent1__ = REQ_(4285); + + +class ColumnFavIcon extends _genericNodePropsComponent1__ .B { + render() { + const { + nodeAdapter + } = this.props; + const { + node + } = nodeAdapter.props; + const isFavouritable = node.r === 2; + return JSX_("td", { + megatype: ColumnFavIcon.megatype, + className: ColumnFavIcon.megatype + }, JSX_("span", { + className: `grid-status-icon sprite-fm-mono ${ missingkeys[node.h] ? " icon-info" : nodeAdapter.nodeProps.fav ? " icon-favourite-filled" : " icon-dot" }${!isFavouritable && " disabled" || ""}`, + onClick: () => { + if (isFavouritable) { + M.favourite([node.h], !node.fav); + } + } + })); + } +} +ColumnFavIcon.sortable = true; +ColumnFavIcon.id = "fav"; +ColumnFavIcon.label = ""; +ColumnFavIcon.icon = "icon-favourite-filled"; +ColumnFavIcon.megatype = "fav"; +ColumnFavIcon.headerClassName = "grid-first-th fav"; + + }, + + 4285 +(_, EXP_, REQ_) { + + +// EXPORTS +REQ_.d(EXP_, { + B: () => GenericNodePropsComponent +}); + +// EXTERNAL MODULE: ./js/chat/mixins.js +const mixins = REQ_(8264); +;// ./js/ui/jsx/fm/nodes/nodeProperties.jsx +class NodeProperties { + static get(node, changeListener) { + assert(node.h, 'missing handle for node'); + if (NodeProperties._globalCleanupTimer) { + NodeProperties._globalCleanupTimer.abort(); + } + (NodeProperties._globalCleanupTimer = tSleep(120)).then(() => { + NodeProperties.cleanup(0); + }); + let nodeProps; + if (!NodeProperties._cache.has(node.h)) { + nodeProps = new NodeProperties(node, changeListener); + NodeProperties._cache.set(node.h, nodeProps); + } + return nodeProps || NodeProperties._cache.get(node.h); + } + unuse(changeListener) { + const {node} = this; + if (!node) { + if (d) { + console.warn("This should not happen."); + } + return; + } + this.changeListeners.delete(changeListener); + let usages = NodeProperties._usages.get(this); + if (usages) { + NodeProperties._usages.set(this, --usages); + if (usages === 0 && NodeProperties._cache.size > NodeProperties.MAX_CACHE_SIZE) { + delay('nodePropCleanup', NodeProperties.cleanup, 1000); + } + } + } + static cleanup(maxCacheSize) { + maxCacheSize = typeof maxCacheSize === "undefined" ? NodeProperties.MAX_CACHE_SIZE : maxCacheSize; + const len = NodeProperties._cache.size; + let removed = 0; + for (const entry of NodeProperties._cache) { + const id = entry[0]; + const node = entry[1]; + const usage = NodeProperties._usages.get(node); + if (usage === 0) { + NodeProperties._usages.delete(node); + node._cleanup(); + NodeProperties._cache.delete(id); + removed++; + if (len - removed < maxCacheSize) { + return; + } + } + } + } + constructor(node, changeListener) { + this.node = node; + this.changeListeners = new Set(); + if (changeListener) { + this.changeListeners.add(changeListener); + } + const _onChange = () => { + this.initProps(); + for (const listener of this.changeListeners) { + listener(); + } + }; + if (this.node.addChangeListener) { + this._listener = this.node.addChangeListener(_onChange); + } else { + this._mbListener = mBroadcaster.addListener(`nodeUpdated:${ node.h}`, _onChange); + } + this.initProps(); + } + use(changeListener) { + if (changeListener) { + this.changeListeners.add(changeListener); + } + NodeProperties._usages.set(this, (NodeProperties._usages.get(this) | 0) + 1); + } + _cleanup() { + if (this._listener) { + this.node.removeChangeListener(this._listener); + } + if (this._mbListener) { + mBroadcaster.removeListener(this._mbListener); + } + oDestroy(this); + } + initProps() { + const {node} = this; + lazy(this, 'title', () => { + if (missingkeys[node.h]) { + return node.t ? l[8686] : l[8687]; + } + return M.getNameByHandle(node.h); + }); + lazy(this, 'classNames', () => { + const classNames = []; + if (node.su) { + classNames.push('inbound-share'); + } + if (node.t) { + classNames.push('folder'); + } else { + classNames.push('file'); + } + const share = this.shareData; + if (missingkeys[node.h] || share.down) { + if (share.down) { + classNames.push('taken-down'); + } + if (missingkeys[node.h]) { + classNames.push('undecryptable'); + } + } + if (share) { + classNames.push('linked'); + } + if (node.lbl && !folderlink) { + const colourLabel = M.getLabelClassFromId(node.lbl); + classNames.push('colour-label'); + classNames.push(colourLabel); + } + return classNames; + }); + lazy(this, 'icon', () => { + return fileIcon(node); + }); + lazy(this, 'isFolder', () => { + return !!node.t; + }); + lazy(this, 'shareData', () => { + return M.getNodeShare(node); + }); + lazy(this, 'isTakendown', () => { + return this.shareData && !!this.shareData.down; + }); + lazy(this, 'fav', () => { + return !!node.fav; + }); + lazy(this, 'size', () => { + return bytesToSize(node.tb || node.s); + }); + lazy(this, 'timestamp', () => { + return time2date(node.ts); + }); + lazy(this, 'root', () => { + return M.getNodeRoot(node.h); + }); + lazy(this, 'incomingShareData', () => { + const result = {}; + if (node.r === 1) { + result.accessLabel = l[56]; + result.accessIcon = 'icon-permissions-write'; + } else if (node.r === 2) { + result.accessLabel = l[57]; + result.accessIcon = 'icon-star'; + } else { + result.accessLabel = l[55]; + result.accessIcon = 'icon-read-only'; + } + return result; + }); + lazy(this, 'timestamp', () => { + return time2date(node.ts); + }); + lazy(this, 'onlineStatus', () => { + return M.onlineStatusClass(node.presence ? node.presence : "unavailable"); + }); + } +} +NodeProperties._cache = new Map(); +NodeProperties._usages = new WeakMap(); +NodeProperties._globalCleanupTimer = void 0; +NodeProperties.MAX_CACHE_SIZE = 100; +if (d) { + window.NodeProperties = NodeProperties; +} +;// ./js/ui/jsx/fm/nodes/genericNodePropsComponent.jsx + + +class GenericNodePropsComponent extends mixins.w9 { + constructor(props) { + super(props); + if (this.props.node.h) { + this.nodeProps = NodeProperties.get(this.props.node); + this.changeListener = this.changeListener.bind(this); + } + } + changeListener() { + if (this.isMounted()) { + this.safeForceUpdate(); + } + } + UNSAFE_componentWillReceiveProps(nextProps) { + if (nextProps.highlighted !== this.props.highlighted) { + this.safeForceUpdate(); + } + } + UNSAFE_componentWillMount() { + let _this$nodeProps; + if (super.UNSAFE_componentWillMount) { + super.UNSAFE_componentWillMount(); + } + (_this$nodeProps = this.nodeProps) == null || _this$nodeProps.use(this.changeListener); + } + componentWillUnmount() { + let _this$nodeProps2; + super.componentWillUnmount(); + (_this$nodeProps2 = this.nodeProps) == null || _this$nodeProps2.unuse(this.changeListener); + } +} + + } + +}]); \ No newline at end of file diff --git a/js/chat/bundle.contacts-panel.js b/js/chat/bundle.contacts-panel.js new file mode 100644 index 0000000000..fa0b919102 --- /dev/null +++ b/js/chat/bundle.contacts-panel.js @@ -0,0 +1,3381 @@ +/** @file automatically generated, do not edit it. */ +"use strict"; +(self.webpackChunk_meganz_webclient = self.webpackChunk_meganz_webclient || []).push([[253],{ + + 5392 +(_, EXP_, REQ_) { + +// ESM COMPAT FLAG +REQ_.r(EXP_); + +// EXPORTS +REQ_.d(EXP_, { + "default": () => ContactsPanel +}); + +// EXTERNAL MODULE: external "React" +const external_React_ = REQ_(1594); +const REaCt = REQ_.n(external_React_); +// EXTERNAL MODULE: ./js/chat/mixins.js +const mixins = REQ_(8264); +// EXTERNAL MODULE: ./js/ui/buttons.jsx +const buttons = REQ_(5155); +// EXTERNAL MODULE: ./js/chat/ui/contactsPanel/utils.jsx +const utils = REQ_(836); +;// ./js/chat/ui/contactsPanel/navigation.jsx + + + +const Navigation = ({ + view, + receivedRequestsCount +}) => { + return JSX_("div", { + className: "contacts-navigation" + }, JSX_("ul", null, Object.keys(utils.gR).map(key => { + let activeClass = view === utils.gR[key] ? 'active' : ''; + if (view === utils.gR.PROFILE && utils.gR[key] === utils.gR.CONTACTS) { + activeClass = 'active'; + } + if (utils.gR[key] !== utils.gR.PROFILE) { + return JSX_("li", { + key, + onClick: () => { + let page = key.toLowerCase().split("_")[0]; + page = page === 'contacts' ? '' : page; + loadSubPage(`fm/chat/contacts/${page}`); + } + }, JSX_(buttons.$, { + className: ` + mega-button + action + ${activeClass} + `, + receivedRequestsCount + }, JSX_("span", null, utils.d_[key]), receivedRequestsCount > 0 && utils.gR[key] === utils.gR.RECEIVED_REQUESTS && JSX_("div", { + className: "notifications-count" + }, receivedRequestsCount > 9 ? '9+' : receivedRequestsCount))); + } + return null; + }))); +}; + const navigation = Navigation; +// EXTERNAL MODULE: ./js/ui/utils.jsx +const ui_utils = REQ_(6411); +;// ./js/chat/ui/contactsPanel/nil.jsx + + + +class Nil extends REaCt().Component { + componentDidMount() { + setContactLink(); + } + render() { + const { + title + } = this.props; + return JSX_("div", { + className: "fm-empty-section fm-empty-contacts" + }, JSX_("div", { + className: "fm-empty-pad" + }, JSX_("i", { + className: "section-icon sprite-fm-mono icon-contacts" + }), JSX_("div", { + className: "fm-empty-cloud-txt" + }, title), JSX_("div", { + className: "fm-empty-description" + }, l[19115]), JSX_(buttons.$, { + className: "mega-button positive large fm-empty-button", + onClick: () => contactAddDialog() + }, JSX_("span", null, l[71])), JSX_("div", { + className: "empty-share-public" + }, JSX_("i", { + className: "sprite-fm-mono icon-link-circle" + }), JSX_(ui_utils.P9, null, l[19111])))); + } +} +// EXTERNAL MODULE: ./js/ui/jsx/fm/fmView.jsx + 10 modules +const fmView = REQ_(872); +// EXTERNAL MODULE: ./js/chat/ui/contacts.jsx +const contacts = REQ_(8022); +// EXTERNAL MODULE: ./js/ui/jsx/fm/nodes/genericNodePropsComponent.jsx + 1 modules +const genericNodePropsComponent = REQ_(4285); +;// ./js/ui/jsx/fm/nodes/columns/columnContactName.jsx + + + + +class ColumnContactName extends genericNodePropsComponent.B { + constructor(...args) { + super(...args); + this.Mail = (0,ui_utils.T9)(() => JSX_("span", { + className: "contact-item-email" + }, this.props.nodeAdapter.props.node.m)); + } + static get label() { + return l[86]; + } + get name() { + const { + nodeAdapter, + node + } = this.props; + if (nodeAdapter.nodeProps) { + return nodeAdapter.nodeProps.title; + } + return M.getNameByEmail(node.m); + } + _renderAvatar() { + const { + nodeAdapter + } = this.props; + const { + node + } = nodeAdapter.props; + if (nodeAdapter.nodeProps || node.name !== '') { + return JSX_(contacts.eu, { + contact: node, + className: "avatar-wrapper box-avatar" + }); + } else if (node.name === '') { + return JSX_(ui_utils.P9, null, useravatar.contact(node.m, 'box-avatar')); + } + return null; + } + render() { + return JSX_("td", null, this._renderAvatar(), JSX_("div", { + className: "contact-item" + }, JSX_("div", { + className: "contact-item-user" + }, JSX_(ui_utils.sp, null, this.name)), JSX_(this.Mail, null)), JSX_("div", { + className: "clear" + })); + } +} +ColumnContactName.sortable = true; +ColumnContactName.id = "name"; +ColumnContactName.megatype = "name"; +;// ./js/ui/jsx/fm/nodes/columns/columnContactStatus.jsx + + +class ColumnContactStatus extends genericNodePropsComponent.B { + static get label() { + return l[89]; + } + render() { + const { + nodeAdapter + } = this.props; + const {onlineStatus} = nodeAdapter.nodeProps; + return JSX_("td", { + megatype: ColumnContactStatus.megatype, + className: ColumnContactStatus.megatype + }, JSX_("div", { + className: "contact-item" + }, JSX_("div", { + className: "contact-item-status" + }, JSX_("div", { + className: `user-card-presence ${ onlineStatus[1]}` + }), onlineStatus[0]))); + } +} +ColumnContactStatus.sortable = true; +ColumnContactStatus.id = "status"; +ColumnContactStatus.megatype = "status"; +;// ./js/ui/jsx/fm/nodes/columns/columnContactLastInteraction.jsx + + +class ColumnContactLastInteraction extends genericNodePropsComponent.B { + constructor(...args) { + super(...args); + this.getLastInteractionIcon = handle => { + const { + interactions + } = this.props; + const interaction = interactions[handle]; + const { + type, + time + } = interaction || { + type: undefined, + time: undefined + }; + return JSX_("i", { + className: ` + sprite-fm-mono + ${parseInt(type, 10) === 0 ? 'icon-cloud' : ''} + ${parseInt(type, 10) === 1 ? 'icon-chat' : ''} + ${!time ? 'icon-minimise' : ''} + ` + }); + }; + this.getLastInteractionTime = handle => { + const { + interactions + } = this.props; + const interaction = interactions[handle]; + return interaction ? time2last(interaction.time) : l[1051]; + }; + } + static get label() { + return l[5904]; + } + render() { + const { + nodeAdapter + } = this.props; + const { + node + } = nodeAdapter.props; + return JSX_("td", { + megatype: ColumnContactLastInteraction.megatype, + className: ColumnContactLastInteraction.megatype + }, JSX_("div", { + className: "contact-item" + }, JSX_("div", { + className: "contact-item-time" + }, this.getLastInteractionIcon(node.h), this.getLastInteractionTime(node.h)))); + } +} +ColumnContactLastInteraction.sortable = true; +ColumnContactLastInteraction.id = "interaction"; +ColumnContactLastInteraction.megatype = "interaction"; +;// ./js/ui/jsx/fm/nodes/columns/columnContactVerifiedStatus.jsx + + + +class ColumnContactVerifiedStatus extends genericNodePropsComponent.B { + constructor(...args) { + super(...args); + this.getFingerPrintDialogLink = handle => { + const onVerifyContactClicked = handle => { + (0,utils.qH)(this.props.contacts[handle]); + }; + return JSX_("div", { + className: "verify-contact-link-container" + }, JSX_("div", { + className: "verify-contact-link", + onClick: () => onVerifyContactClicked(handle) + }, l.verify_credentials)); + }; + } + render() { + const { + nodeAdapter + } = this.props; + const { + node + } = nodeAdapter.props; + return JSX_("td", { + megatype: ColumnContactVerifiedStatus.megatype, + className: ColumnContactVerifiedStatus.megatype + }, JSX_("div", { + className: "contact-item" + }, JSX_("div", { + className: "contact-item-verification" + }, (0,utils.p4)(this.props.contacts[node.h]) ? ColumnContactVerifiedStatus.verifiedLabel : this.getFingerPrintDialogLink(node.h)))); + } +} +ColumnContactVerifiedStatus.sortable = true; +ColumnContactVerifiedStatus.id = "verification"; +ColumnContactVerifiedStatus.megatype = "verification"; +ColumnContactVerifiedStatus.label = JSX_(REaCt().Fragment, null, l.contact_ver_verification, "\xA0", JSX_("i", { + className: "simpletip sprite-fm-mono contacts-verification-icon icon-info", + "data-simpletip": l.contact_ver_tooltip_content, + "data-simpletip-class": "contacts-verification-icon-simpletip" +})); +ColumnContactVerifiedStatus.verifiedLabel = JSX_("div", { + className: "verified-contact-label-container" +}, JSX_("i", { + className: "small-icon icons-sprite tiny-green-tick" +}), l[6776]); +// EXTERNAL MODULE: ./js/ui/dropdowns.jsx +const dropdowns = REQ_(1510); +// EXTERNAL MODULE: ./js/chat/ui/meetings/utils.jsx +const meetings_utils = REQ_(3901); +// EXTERNAL MODULE: ./js/chat/ui/conversations.jsx + 2 modules +const conversations = REQ_(4904); +;// ./js/chat/ui/contactsPanel/contextMenu.jsx + + + + + + + +class ContextMenu extends REaCt().Component { + constructor(...args) { + super(...args); + this.EVENT_CLOSE = new Event('closeDropdowns'); + this.close = callback => { + if (callback && typeof callback === 'function' && !M.isInvalidUserStatus()) { + callback(); + } + document.dispatchEvent(this.EVENT_CLOSE); + }; + this.handleSetNickname = handle => this.close(() => nicknames.setNicknameDialog.init(handle)); + this.handleAddContact = handle => { + M.syncContactEmail(handle, true).then(email => { + const OPC = Object.values(M.opc); + const ALREADY_SENT = OPC && OPC.length && OPC.some(opc => opc.m === email); + this.close(() => { + if (ALREADY_SENT) { + return msgDialog('warningb', '', l[17545]); + } + msgDialog('info', l[150], l[5898]); + M.inviteContact(M.u[u_handle].m, email); + }); + }).catch(nop); + }; + } + render() { + const { + contact, + selected, + withProfile + } = this.props; + if ((0,utils.X7)(contact)) { + return JSX_(REaCt().Fragment, null, withProfile && JSX_("div", { + className: "dropdown-avatar rounded", + onClick: e => { + e.stopPropagation(); + loadSubPage(`fm/chat/contacts/${contact.h}`); + } + }, JSX_(contacts.eu, { + contact, + className: "avatar-wrapper context-avatar" + }), JSX_("div", { + className: "dropdown-profile" + }, JSX_("span", null, JSX_(ui_utils.zT, null, M.getNameByHandle(contact.u))), JSX_(contacts.i1, { + contact + }))), JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-chat", + label: l[8632], + onClick: () => this.close(() => { + if (selected && selected.length) { + return megaChat.createAndShowGroupRoomFor(selected, '', { + keyRotation: true, + createChatLink: false + }); + } + loadSubPage(`fm/chat/p/${contact.u}`); + megaChat.trigger(conversations.qY.NAV_RENDER_VIEW, conversations.Vw.CHATS); + }) + }), JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-send-files", + label: l[6834], + onClick: () => this.close(() => megaChat.openChatAndSendFilesDialog(contact.u)) + }), JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-folder-outgoing-share", + label: l[5631], + onClick: () => this.close(() => openCopyShareDialog(contact.u)) + }), JSX_("div", { + "data-simpletipposition": "top", + className: "simpletip", + "data-simpletip": !megaChat.hasSupportForCalls ? l.call_not_suported : '' + }, JSX_(dropdowns.tJ, { + submenu: megaChat.hasSupportForCalls, + disabled: !navigator.onLine || !megaChat.hasSupportForCalls, + icon: "sprite-fm-mono icon-phone", + className: "sprite-fm-mono-before icon-arrow-right-before", + label: l[19125] + }), JSX_("div", { + className: "dropdown body submenu" + }, JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-phone", + disabled: !navigator.onLine || !megaChat.hasSupportForCalls, + label: l[5896], + onClick: () => (0,meetings_utils.dQ)().then(() => this.close(() => megaChat.createAndShowPrivateRoom(contact.u).then(room => { + room.setActive(); + room.startAudioCall(); + }))).catch(() => d && console.warn('Already in a call.')) + }), JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-video-call-filled", + disabled: !navigator.onLine || !megaChat.hasSupportForCalls, + label: l[5897], + onClick: () => (0,meetings_utils.dQ)().then(() => this.close(() => megaChat.createAndShowPrivateRoom(contact.u).then(room => { + room.setActive(); + room.startVideoCall(); + }))).catch(() => d && console.warn('Already in a call.')) + }))), JSX_("hr", null), withProfile && JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-my-account", + label: l[5868], + onClick: () => loadSubPage(`fm/chat/contacts/${contact.u}`) + }), JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-rename", + label: contact.nickname === '' ? l.set_nickname_label : l.edit_nickname_label, + onClick: () => this.handleSetNickname(contact.u) + }), JSX_("hr", null), JSX_(dropdowns.tJ, { + submenu: true, + icon: "sprite-fm-mono icon-key", + className: "sprite-fm-mono-before icon-arrow-right-before", + label: l[6872] + }), JSX_("div", { + className: "dropdown body white-context-menu submenu" + }, (0,utils.p4)(contact) ? JSX_(dropdowns.tJ, { + label: l[742], + onClick: () => this.close(() => (0,utils.U_)(contact)) + }) : JSX_(dropdowns.tJ, { + label: l[1960], + onClick: () => this.close(() => (0,utils.qH)(contact)) + })), JSX_("div", { + className: "dropdown-credentials" + }, (0,utils.ym)(contact.u)), JSX_("hr", null), JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-disable", + label: l[1001], + disabled: !!contact.b, + className: "", + onClick: () => this.close(() => fmremove(contact.u)) + })); + } + return JSX_(REaCt().Fragment, null, JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-disabled-filled", + label: l[71], + onClick: () => this.handleAddContact(contact.u) + }), JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-rename", + label: contact.nickname === '' ? l.set_nickname_label : l.edit_nickname_label, + onClick: () => this.handleSetNickname(contact.u) + })); + } +} +;// ./js/ui/jsx/fm/nodes/columns/columnContactButtons.jsx + + + + + + + +class ColumnContactButtons extends genericNodePropsComponent.B { + render() { + const { + nodeAdapter + } = this.props; + const { + node, + selected + } = nodeAdapter.props; + const handle = node.h; + return JSX_("td", { + megatype: ColumnContactButtons.megatype, + className: ColumnContactButtons.megatype + }, JSX_("div", { + className: "contact-item" + }, JSX_("div", { + className: "contact-item-controls" + }, JSX_(buttons.$, { + className: "mega-button action simpletip", + icon: "sprite-fm-mono icon-phone", + attrs: { + 'data-simpletip': !megaChat.hasSupportForCalls ? l.unsupported_browser_audio : l[5896] + }, + disabled: !navigator.onLine || !megaChat.hasSupportForCalls, + onClick: () => M.isInvalidUserStatus() || (0,meetings_utils.dQ)().then(() => megaChat.createAndShowPrivateRoom(handle).then(room => { + room.setActive(); + room.startAudioCall(); + })).catch(() => d && console.warn('Already in a call.')) + }), JSX_(buttons.$, { + className: "mega-button action simpletip", + icon: "sprite-fm-mono icon-chat", + attrs: { + 'data-simpletip': l[8632] + }, + onClick: () => { + loadSubPage(`fm/chat/p/${handle}`); + megaChat.trigger(conversations.qY.NAV_RENDER_VIEW, conversations.Vw.CHATS); + } + }), JSX_(buttons.$, { + className: "mega-button action simpletip", + icon: "sprite-fm-mono icon-send-files", + attrs: { + 'data-simpletip': l[6834] + }, + onClick: () => M.isInvalidUserStatus() || megaChat.openChatAndSendFilesDialog(handle) + }), JSX_(buttons.$, { + ref: node => { + this.props.onContextMenuRef(handle, node); + }, + className: "mega-button action contact-more", + icon: "sprite-fm-mono icon-options" + }, JSX_(dropdowns.ms, { + className: "context", + noArrow: true, + positionMy: "left bottom", + positionAt: "right bottom", + positionLeft: this.props.contextMenuPosition || null, + horizOffset: 4, + onActiveChange: opened => { + this.props.onActiveChange(opened); + } + }, JSX_(ContextMenu, { + contact: node, + selected, + withProfile: true + })))))); + } +} +ColumnContactButtons.sortable = false; +ColumnContactButtons.id = "grid-url-header-nw"; +ColumnContactButtons.label = ""; +ColumnContactButtons.megatype = "grid-url-header-nw"; +// EXTERNAL MODULE: ./js/chat/ui/updateObserver.jsx +const updateObserver = REQ_(4372); +;// ./js/chat/ui/contactsPanel/contactList.jsx + + + + + + + + + + +class ContactList extends mixins.w9 { + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.contextMenuRefs = []; + this.state = { + selected: [], + searchValue: null, + interactions: {}, + contextMenuPosition: null + }; + this.onSelected = this.onSelected.bind(this); + this.onHighlighted = this.onHighlighted.bind(this); + this.onExpand = this.onExpand.bind(this); + this.onAttachClicked = this.onAttachClicked.bind(this); + this.getLastInteractions = this.getLastInteractions.bind(this); + } + getLastInteractions() { + const { + contacts + } = this.props; + const promises = []; + const push = handle => { + promises.push(Promise.resolve(getLastInteractionWith(handle, true, true)).then(ts => [ts, handle])); + }; + for (const handle in contacts) { + if (contacts[handle].c === 1) { + push(handle); + } + } + Promise.allSettled(promises).then(res => { + if (this.isMounted()) { + const interactions = {}; + for (let i = res.length; i--;) { + if (res[i].status !== 'fulfilled') { + if (d && res[i].reason !== false) { + console.warn('getLastInteractions', res[i].reason); + } + } else { + const [ts, u] = res[i].value; + const [type, time] = ts.split(':'); + interactions[u] = { + u, + type, + time + }; + } + } + this.setState({ + interactions + }); + } + }).catch(ex => { + console.error("Failed to handle last interactions!", ex); + }); + } + handleContextMenu(ev, handle) { + ev.preventDefault(); + ev.persist(); + if (this.state.selected.length > 1) { + return null; + } + const $$REF = this.contextMenuRefs[handle]; + if ($$REF && $$REF.isMounted()) { + let _$$REF$domRef; + const refNodePosition = ((_$$REF$domRef = $$REF.domRef) == null ? void 0 : _$$REF$domRef.current) && $$REF.domRef.current.getBoundingClientRect().x; + this.setState({ + contextMenuPosition: ev.clientX > refNodePosition ? null : ev.clientX + }, () => $$REF.onClick(ev)); + } + } + onSelected(handle) { + this.setState({ + 'selected': handle + }); + } + onHighlighted(handle) { + this.setState({ + 'highlighted': handle + }); + } + onExpand(handle) { + loadSubPage(`/fm/chat/contacts/${ handle}`); + } + onAttachClicked() { + if (this.state.selected[0]) { + this.onExpand(this.state.selected[0]); + } + } + componentDidMount() { + super.componentDidMount(); + this.getLastInteractions(); + } + render() { + const { + contacts + } = this.props; + if (contacts && contacts.length > 1) { + return JSX_("div", { + ref: this.domRef, + className: "contacts-list" + }, JSX_(fmView.A, { + dataSource: contacts, + customFilterFn: r => { + return r.c === 1; + }, + currentlyViewedEntry: "contacts", + onSelected: this.onSelected, + onHighlighted: this.onHighlighted, + searchValue: this.state.searchValue, + onExpand: this.onExpand, + onAttachClicked: this.onAttachClicked, + viewMode: 0, + currentdirid: "contacts", + megaListItemHeight: 59, + headerContainerClassName: "contacts-table contacts-table-head", + containerClassName: "contacts-table contacts-table-results", + onContextMenu: (ev, handle) => this.handleContextMenu(ev, handle), + listAdapterColumns: [ColumnContactName, ColumnContactStatus, [ColumnContactLastInteraction, { + interactions: this.state.interactions + }], [ColumnContactVerifiedStatus, { + contacts + }], [ColumnContactButtons, { + onContextMenuRef: (handle, node) => { + this.contextMenuRefs[handle] = node; + }, + onActiveChange: opened => { + if (!opened) { + this.setState({ + contextMenuPosition: null + }); + } + }, + contextMenuPosition: this.state.contextMenuPosition + }]], + initialSortBy: ['status', 'asc'], + fmConfigSortEnabled: true, + fmConfigSortId: "contacts", + NilComponent: JSX_(Nil, { + title: l[5737] + }) + })); + } + return JSX_(Nil, { + title: l[5737] + }); + } +} +ContactList.updateListener = 'getLastInteractions'; +ContactList.updateInterval = 6e4; + const contactList = (0,mixins.Zz)(updateObserver.Y)(ContactList); +;// ./js/ui/jsx/fm/nodes/columns/columnContactRequestsEmail.jsx + + + +class ColumnContactRequestsEmail extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + } + static get label() { + return l[95]; + } + render() { + const { + nodeAdapter, + currView + } = this.props; + const { + node + } = nodeAdapter.props; + return JSX_("td", { + ref: this.domRef + }, currView && currView === 'opc' ? JSX_("span", null, JSX_("i", { + className: "sprite-fm-uni icon-send-requests" + })) : JSX_(ui_utils.P9, null, useravatar.contact(node.m, 'box-avatar')), JSX_("div", { + className: "contact-item" + }, JSX_("div", { + className: "contact-item-user" + }, node.m)), JSX_("div", { + className: "clear" + })); + } +} +ColumnContactRequestsEmail.sortable = true; +ColumnContactRequestsEmail.id = "email"; +ColumnContactRequestsEmail.megatype = "email"; +;// ./js/ui/jsx/fm/nodes/columns/columnContactRequestsTs.jsx + + +class ColumnContactRequestsTs extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + } + static get label() { + return l[19506]; + } + render() { + const { + nodeAdapter + } = this.props; + const { + node + } = nodeAdapter.props; + let timestamp = node.rts || node.ts; + if (timestamp) { + timestamp = time2last(timestamp); + } else { + timestamp = node.dts ? l[6112] : ""; + } + return JSX_("td", { + ref: this.domRef + }, JSX_("div", { + className: "contact-item" + }, JSX_("div", { + className: "contact-item-time" + }, timestamp)), JSX_("div", { + className: "clear" + })); + } +} +ColumnContactRequestsTs.sortable = true; +ColumnContactRequestsTs.id = "ts"; +ColumnContactRequestsTs.megatype = "ts"; +;// ./js/ui/jsx/fm/nodes/columns/columnContactRequestsRcvdBtns.jsx + + + +class ColumnContactRequestsRcvdBtns extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + } + render() { + const { + nodeAdapter + } = this.props; + const { + node + } = nodeAdapter.props; + return JSX_("td", { + ref: this.domRef, + megatype: ColumnContactRequestsRcvdBtns.megatype, + className: ColumnContactRequestsRcvdBtns.megatype + }, JSX_("div", { + className: "contact-item-controls" + }, JSX_(buttons.$, { + className: "mega-button action contact-reject", + icon: "sprite-fm-mono icon-close-component", + label: l[20981], + onClick: () => this.props.onReject(node.p) + }), JSX_(buttons.$, { + className: "mega-button action contact-block", + icon: "sprite-fm-mono icon-disable", + label: l[20980], + onClick: () => this.props.onBlock(node.p) + }), JSX_(buttons.$, { + className: "mega-button action contact-accept", + icon: "sprite-fm-mono icon-check", + label: l[5856], + onClick: () => this.props.onAccept(node.p) + }))); + } +} +ColumnContactRequestsRcvdBtns.sortable = true; +ColumnContactRequestsRcvdBtns.id = "grid-url-header-nw"; +ColumnContactRequestsRcvdBtns.label = ""; +ColumnContactRequestsRcvdBtns.megatype = "grid-url-header-nw contact-controls-container"; +;// ./js/chat/ui/contactsPanel/receivedRequests.jsx + + + + + + + +const ReceivedRequests = ({ + received +}) => { + const nameOrEmailColumn = received.mixed ? ColumnContactName : [ColumnContactRequestsEmail, { + currView: "ipc" + }]; + return JSX_("div", { + className: "contacts-list" + }, JSX_(fmView.A, { + sortFoldersFirst: false, + dataSource: received.data, + customFilterFn: r => { + return !r.dts; + }, + currentlyViewedEntry: "ipc", + onSelected: nop, + onHighlighted: nop, + onExpand: nop, + onAttachClicked: nop, + viewMode: 0, + currentdirid: "ipc", + megaListItemHeight: 59, + headerContainerClassName: "contacts-table requests-table contacts-table-head", + containerClassName: "contacts-table requests-table contacts-table-results", + listAdapterColumns: [nameOrEmailColumn, [ColumnContactRequestsTs, { + label: l[19505] + }], [ColumnContactRequestsRcvdBtns, { + onReject: handle => { + M.denyPendingContactRequest(handle).catch(dump); + }, + onBlock: handle => { + M.ignorePendingContactRequest(handle).catch(dump); + }, + onAccept: handle => { + M.acceptPendingContactRequest(handle).catch(dump); + } + }]], + keyProp: "p", + nodeAdapterProps: { + 'className': node => { + return ` + ${node.dts || node.s && node.s === 3 ? 'deleted' : ''} + ${node.s && node.s === 1 ? 'ignored' : ''} + `; + } + }, + NilComponent: () => { + return JSX_(Nil, { + title: l[6196] + }); + }, + initialSortBy: ['email', 'asc'], + fmConfigSortEnabled: true, + fmConfigSortId: "ipc" + })); +}; + const receivedRequests = ReceivedRequests; +;// ./js/ui/jsx/fm/nodes/columns/columnContactRequestsSentBtns.jsx + + + +class ColumnContactRequestsSentBtns extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + this.reinviteAllowed = rts => { + const UTC_DATE_NOW = Math.floor(Date.now() / 1000); + return UTC_DATE_NOW > rts + 1209600; + }; + } + render() { + const { + nodeAdapter + } = this.props; + const { + node + } = nodeAdapter.props; + return JSX_("td", { + ref: this.domRef, + megatype: ColumnContactRequestsSentBtns.megatype, + className: ColumnContactRequestsSentBtns.megatype + }, JSX_("div", { + className: "contact-item-controls contact-request-sent" + }, !node.dts && this.reinviteAllowed(node.rts) && JSX_(buttons.$, { + className: "mega-button action", + icon: "sprite-fm-mono icon-rewind", + label: l[5861], + onClick: () => this.props.onReinvite(node.m) + }), !node.dts && JSX_(buttons.$, { + className: "mega-button action contact-reject", + icon: "sprite-fm-mono icon-close-component", + label: l.msg_dlg_cancel, + onClick: () => this.props.onReject(node.m) + }))); + } +} +ColumnContactRequestsSentBtns.sortable = true; +ColumnContactRequestsSentBtns.id = "grid-url-header-nw"; +ColumnContactRequestsSentBtns.label = ""; +ColumnContactRequestsSentBtns.megatype = "grid-url-header-nw contact-controls-container"; +;// ./js/ui/jsx/fm/nodes/columns/columnContactRequestsRts.jsx + + +class ColumnContactRequestsRts extends ColumnContactRequestsTs { + static get label() { + return l[19506]; + } +} +ColumnContactRequestsRts.sortable = true; +ColumnContactRequestsRts.id = "rts"; +ColumnContactRequestsRts.megatype = "rts"; +;// ./js/chat/ui/contactsPanel/sentRequests.jsx + + + + + + +const SentRequests = ({ + sent +}) => { + return JSX_("div", { + className: "contacts-list" + }, JSX_(fmView.A, { + sortFoldersFirst: false, + dataSource: sent, + currentlyViewedEntry: "opc", + onSelected: nop, + onHighlighted: nop, + onExpand: nop, + onAttachClicked: nop, + viewMode: 0, + currentdirid: "opc", + megaListItemHeight: 59, + headerContainerClassName: "contacts-table requests-table contacts-table-head", + containerClassName: "contacts-table requests-table contacts-table-results", + listAdapterColumns: [[ColumnContactRequestsEmail, { + currView: "opc" + }], ColumnContactRequestsRts, [ColumnContactRequestsSentBtns, { + onReject: email => { + M.cancelPendingContactRequest(email).catch(ex => { + if (ex === EARGS) { + msgDialog('info', '', 'This pending contact is already deleted.'); + } else { + tell(ex); + } + }); + }, + onReinvite: email => { + M.reinvitePendingContactRequest(email).then(() => contactsInfoDialog(l[19126], email, l[19127])).catch(tell); + } + }]], + NilComponent: () => { + return JSX_(Nil, { + title: l[6196] + }); + }, + listAdapterOpts: { + 'className': node => node.dts && ' disabled' + }, + keyProp: "p", + initialSortBy: ['email', 'asc'], + fmConfigSortEnabled: true, + fmConfigSortMap: { + 'rts': 'rTimeStamp' + }, + fmConfigSortId: "opc" + })); +}; + const sentRequests = SentRequests; +// EXTERNAL MODULE: ./js/ui/jsx/fm/nodes/columns/columnFavIcon.jsx +const columnFavIcon = REQ_(6794); +;// ./js/ui/jsx/fm/nodes/columns/columnSharedFolderName.jsx + + +class ColumnSharedFolderName extends genericNodePropsComponent.B { + static get label() { + return l[86]; + } + render() { + const { + nodeAdapter + } = this.props; + const { + node + } = nodeAdapter.props; + return JSX_("td", { + megatype: ColumnSharedFolderName.megatype, + className: ColumnSharedFolderName.megatype + }, JSX_("div", { + className: "item-type-icon-90 icon-folder-incoming-90 sprite-fm-uni-after icon-warning-after" + }), JSX_("div", { + className: "shared-folder-info-block" + }, JSX_("div", { + className: "shared-folder-name" + }, missingkeys[node.h] ? l[8686] : nodeAdapter.nodeProps.title), JSX_("div", { + className: "shared-folder-info" + }, fm_contains(node.tf, node.td)))); + } +} +ColumnSharedFolderName.sortable = true; +ColumnSharedFolderName.id = "name"; +ColumnSharedFolderName.megatype = "name"; +;// ./js/ui/jsx/fm/nodes/columns/columnSharedFolderAccess.jsx + + +class ColumnSharedFolderAccess extends genericNodePropsComponent.B { + static get label() { + return l[5906]; + } + render() { + const { + nodeAdapter + } = this.props; + return JSX_("td", { + megatype: ColumnSharedFolderAccess.megatype, + className: ColumnSharedFolderAccess.megatype + }, JSX_("div", { + className: "shared-folder-access" + }, JSX_("i", { + className: ` + sprite-fm-mono + ${nodeAdapter.nodeProps.incomingShareData.accessIcon} + ` + }), JSX_("span", null, nodeAdapter.nodeProps.incomingShareData.accessLabel))); + } +} +ColumnSharedFolderAccess.sortable = true; +ColumnSharedFolderAccess.id = 'access'; +ColumnSharedFolderAccess.megatype = 'access'; +;// ./js/ui/jsx/fm/nodes/columns/columnSharedFolderButtons.jsx + + + +class ColumnSharedFolderButtons extends genericNodePropsComponent.B { + render() { + const { + nodeAdapter + } = this.props; + const { + node + } = nodeAdapter.props; + const handle = node.h; + return JSX_("td", { + megatype: ColumnSharedFolderButtons.megatype, + className: ColumnSharedFolderButtons.megatype + }, JSX_("div", { + className: "contact-item" + }, JSX_("div", { + className: "contact-item-controls" + }, JSX_(buttons.$, { + className: "mega-button action contact-more", + icon: "sprite-fm-mono icon-options", + onClick: (button, e) => { + e.persist(); + $.selected = [handle]; + e.preventDefault(); + e.stopPropagation(); + e.delegateTarget = $(e.target).parents('td')[0]; + e.currentTarget = $(e.target).parents('tr'); + if (!$(e.target).hasClass('active')) { + M.contextMenuUI(e, 1); + $(this).addClass('active'); + } else { + $.hideContextMenu(); + $(e.target).removeClass('active'); + } + } + })))); + } +} +ColumnSharedFolderButtons.sortable = true; +ColumnSharedFolderButtons.id = "grid-url-header-nw"; +ColumnSharedFolderButtons.label = ""; +ColumnSharedFolderButtons.megatype = "grid-url-header-nw"; +// EXTERNAL MODULE: ./js/chat/ui/link.jsx +const ui_link = REQ_(4649); +;// ./js/chat/ui/contactsPanel/contactProfile.jsx + + + + + + + + + + + + + + + + +class ContactProfile extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + this.state = { + selected: [], + loading: true + }; + this.onAttachClicked = () => { + const { + selected + } = this.state; + if (selected[0]) { + this.onExpand(selected[0]); + } + }; + this.onExpand = handle => loadSubPage(`fm/${handle}`); + this.Breadcrumb = () => { + const { + handle + } = this.props; + return JSX_("div", { + className: "profile-breadcrumb" + }, JSX_("ul", null, JSX_("li", null, JSX_(ui_link.A, { + to: "/fm/chat/contacts" + }, utils.d_.CONTACTS), JSX_("i", { + className: "sprite-fm-mono icon-arrow-right" + })), JSX_("li", null, JSX_(ui_utils.zT, null, M.getNameByHandle(handle))))); + }; + this.Credentials = () => { + const { + handle + } = this.props; + const contact = M.u[handle]; + if (handle && contact && contact.c === 1) { + const IS_VERIFIED = (0,utils.p4)(contact); + return JSX_("div", { + className: "profile-credentials" + }, JSX_("span", { + className: "credentials-head" + }, l[6872]), JSX_("div", { + className: "credentials-fingerprints" + }, (0,utils.ym)(handle)), JSX_("button", { + className: ` + mega-button + small + ${IS_VERIFIED ? '' : 'positive'} + `, + onClick: () => (IS_VERIFIED ? utils.U_ : utils.qH)(contact) + }, IS_VERIFIED ? l[742] : l.verify_credentials)); + } + return null; + }; + this.handleContextMenu = (e, handle) => { + e.persist(); + e.preventDefault(); + e.stopPropagation(); + e.delegateTarget = e.target.tagName === "TR" ? e.target : $(e.target).parents('tr')[0]; + e.currentTarget = $(e.delegateTarget); + $.selected = [handle]; + M.contextMenuUI(e, 1); + }; + } + UNSAFE_componentWillMount() { + if (super.UNSAFE_componentWillMount) { + super.UNSAFE_componentWillMount(); + } + const { + handle + } = this.props; + if (handle) { + const contact = M.u[handle]; + if (contact) { + this._listener = contact.addChangeListener(() => { + if (contact && contact.c === 1) { + this.safeForceUpdate(); + } else { + loadSubPage("/fm/chat/contacts"); + return 0xDEAD; + } + }); + } + } + } + componentWillUnmount() { + super.componentWillUnmount(); + if (this._listener) { + const { + handle + } = this.props; + const contact = M.u[handle]; + contact.removeChangeListener(this._listener); + } + } + componentDidMount() { + super.componentDidMount(); + dbfetch.geta(Object.keys(M.c.shares || {}), new MegaPromise()).finally(() => { + if (this.isMounted()) { + this.setState({ + 'loading': false + }); + } + }); + } + getSharedFoldersView() { + return this.state.loading ? null : JSX_(fmView.A, { + currentlyViewedEntry: this.props.handle, + onSelected: handle => this.setState({ + selected: handle + }), + onHighlighted: nop, + searchValue: this.state.searchValue, + onExpand: this.onExpand, + onAttachClicked: this.onAttachClicked, + viewMode: 0, + currentdirid: "shares", + megaListItemHeight: 65, + headerContainerClassName: "grid-table-header", + containerClassName: "grid-table shared-with-me", + onContextMenu: (ev, handle) => this.handleContextMenu(ev, handle), + listAdapterColumns: [columnFavIcon.$, [ColumnSharedFolderName, { + 'label': `${l.shared_folders_from.replace('%NAME', M.getNameByHandle(this.props.handle))}` + }], ColumnSharedFolderAccess, ColumnSharedFolderButtons] + }); + } + render() { + const { + handle + } = this.props; + if (handle) { + const contact = M.u[handle]; + if (!contact || contact.c !== 1) { + return JSX_(Nil, { + title: l.contact_not_found + }); + } + const HAS_RELATIONSHIP = (0,utils.X7)(contact); + return JSX_("div", { + ref: this.domRef, + className: "contacts-profile" + }, JSX_(this.Breadcrumb, null), JSX_("div", { + className: "profile-content" + }, JSX_("div", { + className: "profile-head" + }, HAS_RELATIONSHIP && JSX_(this.Credentials, null), JSX_(contacts.eu, { + contact, + className: "profile-photo avatar-wrapper contacts-medium-avatar" + }), JSX_("div", { + className: "profile-info" + }, JSX_("h2", null, JSX_(ui_utils.zT, null, M.getNameByHandle(handle)), JSX_(contacts.i1, { + contact + })), JSX_("span", null, contact.m)), HAS_RELATIONSHIP && JSX_("div", { + className: "profile-controls" + }, JSX_(buttons.$, { + className: "mega-button round simpletip", + icon: "sprite-fm-mono icon-chat-filled", + attrs: { + 'data-simpletip': l[8632] + }, + onClick: () => { + loadSubPage(`fm/chat/p/${handle}`); + megaChat.trigger(conversations.qY.NAV_RENDER_VIEW, conversations.Vw.CHATS); + } + }), JSX_(buttons.$, { + className: "mega-button round simpletip", + icon: "sprite-fm-mono icon-send-files", + attrs: { + 'data-simpletip': l[6834] + }, + onClick: () => { + if (M.isInvalidUserStatus()) { + return; + } + megaChat.openChatAndSendFilesDialog(handle); + } + }), JSX_(buttons.$, { + className: "mega-button round", + icon: "sprite-fm-mono icon-options" + }, JSX_(dropdowns.ms, { + className: "context", + noArrow: true, + positionMy: "left bottom", + positionAt: "right bottom", + horizOffset: 4 + }, JSX_(ContextMenu, { + contact + }))))), JSX_("div", { + className: "profile-shared-folders" + }, this.getSharedFoldersView()))); + } + return null; + } +} +;// ./js/chat/ui/contactsPanel/contactsPanel.jsx + + + + + + + + +class ContactsPanel extends mixins.w9 { + get view() { + switch (megaChat.routingSubSection) { + case null: + return utils.gR.CONTACTS; + case "contact": + return utils.gR.PROFILE; + case "received": + return utils.gR.RECEIVED_REQUESTS; + case "sent": + return utils.gR.SENT_REQUESTS; + default: + console.error("Shouldn't happen."); + return false; + } + } + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.requestReceivedListener = null; + this.state = { + receivedRequestsCount: 0 + }; + this.handleToggle = ({ + keyCode + }) => { + if (keyCode === 27) { + const HAS_DIALOG_OPENED = $.dialog || ['.contact-nickname-dialog', '.fingerprint-dialog', '.context'].some(selector => { + const dialog = document.querySelector(selector); + return dialog && dialog.offsetHeight > 0; + }); + return HAS_DIALOG_OPENED ? keyCode : loadSubPage('fm/chat'); + } + }; + this.handleAcceptAllRequests = () => { + const { + data: received + } = this.props.received; + const receivedKeys = Object.keys(received || {}); + if (receivedKeys.length) { + for (let i = receivedKeys.length; i--;) { + M.acceptPendingContactRequest(receivedKeys[i]).catch(dump); + } + } + }; + this.renderView = () => { + const { + contacts, + received, + sent + } = this.props; + const { + view + } = this; + switch (view) { + case utils.gR.CONTACTS: + return JSX_(contactList, { + contacts + }); + case utils.gR.PROFILE: + return JSX_(ContactProfile, { + handle: view === utils.gR.PROFILE && megaChat.routingParams + }); + case utils.gR.RECEIVED_REQUESTS: + return JSX_(receivedRequests, { + received + }); + case utils.gR.SENT_REQUESTS: + return JSX_(sentRequests, { + sent + }); + } + }; + this.state.receivedRequestsCount = Object.keys(M.ipc).length; + } + componentWillUnmount() { + super.componentWillUnmount(); + document.documentElement.removeEventListener(utils.qY.KEYDOWN, this.handleToggle); + if (this.requestReceivedListener) { + mBroadcaster.removeListener(this.requestReceivedListener); + } + } + componentDidMount() { + super.componentDidMount(); + document.documentElement.addEventListener(utils.qY.KEYDOWN, this.handleToggle); + this.requestReceivedListener = mBroadcaster.addListener('fmViewUpdate:ipc', () => this.setState({ + receivedRequestsCount: Object.keys(M.ipc).length + })); + } + render() { + const { + view, + state + } = this; + const { + receivedRequestsCount + } = state; + return JSX_("div", { + ref: this.domRef, + className: "contacts-panel" + }, JSX_(navigation, { + view, + contacts: this.props.contacts, + receivedRequestsCount + }), view !== utils.gR.PROFILE && JSX_("div", { + className: "contacts-actions" + }, view === utils.gR.RECEIVED_REQUESTS && receivedRequestsCount > 1 && JSX_("button", { + className: "mega-button action", + onClick: this.handleAcceptAllRequests + }, JSX_("i", { + className: "sprite-fm-mono icon-check" + }), JSX_("span", null, l[19062]))), JSX_("div", { + className: "contacts-content" + }, this.renderView())); + } +} + + }, + + 872 +(_, EXP_, REQ_) { + + +// EXPORTS +REQ_.d(EXP_, { + A: () => FMView +}); + +// EXTERNAL MODULE: external "React" +const external_React_ = REQ_(1594); +const REaCt = REQ_.n(external_React_); +// EXTERNAL MODULE: ./js/chat/mixins.js +const mixins = REQ_(8264); +// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/extends.js +const esm_extends = REQ_(8168); +// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/applyDecoratedDescriptor.js +const applyDecoratedDescriptor = REQ_(793); +// EXTERNAL MODULE: ./js/ui/perfectScrollbar.jsx +const perfectScrollbar = REQ_(1301); +;// ./js/ui/jsx/megaList/megaList2.jsx + + +let _dec, _class; + + + +const MegaList2 = (_dec = (0,mixins.hG)(30, true), _class = class MegaList2 extends mixins.w9 { + constructor(props) { + super(props); + this._calculated = false; + this._firstRender = true; + this.customIsEventuallyVisible = true; + this.requiresUpdateOnResize = true; + this.adapterChangedDoRepaint = false; + assert(props.listAdapter, 'missing `listAdapter` for MegaList2'); + assert(props.nodeAdapter, 'missing `nodeAdapter` for MegaList2'); + assert(props.entries, 'missing `entries` for MegaList2'); + this.options = { + extraRows: 8, + batchPages: 0, + perfectScrollOptions: { + 'handlers': ['click-rail', 'drag-thumb', 'wheel', 'touch'], + 'minScrollbarLength': 20 + } + }; + this.onPsUserScroll = this.onPsUserScroll.bind(this); + this.thumbsLoadingHandlers = new MapSet(); + this.thumbsThatRequireLoading = new MapSet(); + this.requestThumbnailCb = this.requestThumbnailCb.bind(this); + } + specShouldComponentUpdate(nextProps) { + let invalidate = false; + if (nextProps.listAdapter.prototype.constructor.name !== this.props.listAdapter.prototype.constructor.name || nextProps.entries !== this.props.entries || nextProps.viewMode !== this.props.viewMode) { + invalidate = true; + } + if (nextProps.sortBy !== this.props.sortBy || nextProps.currentlyViewedEntry !== this.props.currentlyViewedEntry) { + invalidate = true; + this.domRef.scrollToY(0); + } + if (invalidate) { + this._calculated = false; + this.adapterChangedDoRepaint = true; + return true; + } + return null; + } + _recalculate() { + if (this._calculated) { + return this._calculated; + } + const calculated = this._calculated = Object.create(null); + lazy(calculated, 'scrollWidth', () => { + return this.domRef.getClientWidth(); + }); + lazy(calculated, 'scrollHeight', () => this.domRef.getClientHeight() - calculated.headerHeight); + lazy(calculated, 'itemWidth', () => { + if (this.props.listAdapter.itemWidth === false) { + return calculated.scrollWidth; + } + return this.props.listAdapter.itemWidth; + }); + lazy(calculated, 'itemHeight', () => { + return this.props.itemHeight || this.props.listAdapter.itemHeight; + }); + lazy(calculated, 'headerHeight', () => this.props.headerHeight || 0); + lazy(calculated, 'contentWidth', () => { + const contentWidth = this.domRef.getContentWidth(); + if (contentWidth) { + return contentWidth; + } + return calculated.itemWidth; + }); + lazy(calculated, 'itemsPerRow', () => { + if (this.props.listAdapter.itemsPerRow) { + return this.props.listAdapter.itemsPerRow; + } + return Math.max(1, Math.floor(calculated.contentWidth / calculated.itemWidth)); + }); + lazy(calculated, 'contentHeight', () => { + return Math.ceil(this.props.entries.length / calculated.itemsPerRow) * calculated.itemHeight; + }); + lazy(calculated, 'scrollLeft', () => { + return this.domRef.getScrollPositionX(); + }); + lazy(calculated, 'scrollTop', () => { + if (this.adapterChangedDoRepaint) { + return 0; + } + return this.domRef.getScrollPositionY(); + }); + lazy(calculated, 'scrolledPercentX', () => { + return 100 / calculated.scrollWidth * calculated.scrollLeft; + }); + lazy(calculated, 'scrolledPercentY', () => { + return 100 / calculated.scrollHeight * calculated.scrollTop; + }); + lazy(calculated, 'isAtTop', () => { + return calculated.scrollTop === 0; + }); + lazy(calculated, 'isAtBottom', () => { + return calculated.scrollTop === calculated.scrollHeight; + }); + lazy(calculated, 'itemsPerPage', () => { + return Math.ceil(calculated.scrollHeight / calculated.itemHeight) * calculated.itemsPerRow; + }); + lazy(calculated, 'visibleFirstItemNum', () => { + let value = 0; + value = Math.floor(Math.floor(calculated.scrollTop / calculated.itemHeight) * calculated.itemsPerRow); + if (value > 0) { + value = Math.max(0, value - this.options.extraRows * calculated.itemsPerRow); + } + return value; + }); + lazy(calculated, 'visibleLastItemNum', () => { + let value = Math.min(this.props.entries.length, Math.ceil(Math.ceil(calculated.scrollTop / calculated.itemHeight) * calculated.itemsPerRow + calculated.itemsPerPage)); + if (value < this.props.entries.length) { + value = Math.min(this.props.entries.length, value + this.options.extraRows * calculated.itemsPerRow); + } + return value; + }); + if (this.options.batchPages > 0) { + const perPage = calculated.itemsPerPage; + const visibleF = calculated.visibleFirstItemNum; + calculated.visibleFirstItemNum = Math.max(0, ((visibleF - visibleF % perPage) / perPage - 1 - this.options.batchPages) * perPage); + const visibleL = calculated.visibleLastItemNum; + calculated.visibleLastItemNum = Math.min(this.props.entries.length, ((visibleL - visibleL % perPage) / perPage + 1 + this.options.batchPages) * perPage); + } + Object.defineProperty(M, 'rmItemsInView', { + get: () => { + const c = this.domRef && this._calculated || !1; + return c.itemsPerPage + c.itemsPerRow | 0; + }, + configurable: true + }); + } + _contentUpdated() { + this._calculated = false; + this._recalculate(); + if (this.listContent && this._lastContentHeight !== this._calculated.contentHeight) { + this._lastContentHeight = this._calculated.contentHeight; + this.listContent.style.height = `${this._calculated.contentHeight }px`; + } + if (this.domRef && this._calculated.scrollHeight + this._calculated.scrollTop > this._calculated.contentHeight) { + this.domRef.scrollToY(this._calculated.contentHeight - this._calculated.scrollHeight); + } + if (this.listAdapterInstance && this.listAdapterInstance.onContentUpdated) { + this.listAdapterInstance.onContentUpdated(); + } + } + _getCalcsThatTriggerChange() { + return [this.props.entries.length, this._calculated.scrollHeight, this._calculated.itemWidth, this._calculated.itemHeight, this._calculated.contentWidth, this._calculated.itemsPerRow, this._calculated.contentHeight, this._calculated.visibleFirstItemNum, this._calculated.visibleLastItemNum]; + } + indexOfEntry(nodeHandle, prop) { + prop = prop || 'h'; + for (let i = 0; i < this.props.entries.length; i++) { + const entry = this.props.entries[i]; + if (entry[prop] === nodeHandle) { + return i; + } + } + return -1; + } + scrollToItem(nodeHandle) { + const elementIndex = this.indexOfEntry(nodeHandle); + if (elementIndex === -1) { + return false; + } + let shouldScroll = false; + const itemOffsetTop = Math.floor(elementIndex / this._calculated.itemsPerRow) * this._calculated.itemHeight; + const itemOffsetTopPlusHeight = itemOffsetTop + this._calculated.itemHeight; + if (itemOffsetTop < this._calculated.scrollTop || itemOffsetTopPlusHeight > this._calculated.scrollTop + this._calculated.scrollHeight) { + shouldScroll = true; + } + if (shouldScroll) { + this.domRef.scrollToY(itemOffsetTop); + onIdle(() => { + this.safeForceUpdate(); + }); + return true; + } + return false; + } + onPsUserScroll() { + if (!this.isMounted()) { + return; + } + const oldCalc = JSON.stringify(this._getCalcsThatTriggerChange()); + this._contentUpdated(); + const newCalc = JSON.stringify(this._getCalcsThatTriggerChange()); + if (oldCalc !== newCalc) { + this.forceUpdate(); + } + } + onResizeDoUpdate() { + super.onResizeDoUpdate(); + this._contentUpdated(); + } + componentDidMount() { + super.componentDidMount(); + this._contentUpdated(); + this.forceUpdate(); + } + componentDidUpdate() { + super.componentDidUpdate(); + this._contentUpdated(); + if (this.adapterChangedDoRepaint) { + this.adapterChangedDoRepaint = false; + this._calculated = false; + this._recalculate(); + } + if (this.thumbsThatRequireLoading.size) { + delay('chat:mega-list2:thumb-loader', () => this.enqueueThumbnailRetrieval(), 20); + } + this._firstRender = this._firstRender || this.props.viewmode !== M.viewmode; + if (this._firstRender && this.domRef) { + let _this$domRef; + this._firstRender = false; + Ps.update((_this$domRef = this.domRef) == null ? void 0 : _this$domRef.$Node); + } + } + enqueueThumbnailRetrieval() { + const loaders = new Map(this.thumbsLoadingHandlers); + const nodes = new Map(this.thumbsThatRequireLoading); + const pending = []; + const defaultCallback = (n, src, id) => { + let img = document.getElementById(id || `chat_${n.h}`); + if (img && (img = img.querySelector('img'))) { + let _img$parentNode$paren; + img.src = src; + (_img$parentNode$paren = img.parentNode.parentNode) == null || _img$parentNode$paren.classList.add('thumb'); + } + }; + const setSource = n => { + if (thumbnails.has(n.fa)) { + const src = thumbnails.get(n.fa); + const batch = [...nodes.get(n.fa)]; + for (let i = batch.length; i--;) { + const n = batch[i]; + const handlers = [...loaders.get(n.h)]; + for (let i = handlers.length; i--;) { + let callback = handlers[i]; + if (typeof callback !== 'function') { + callback = defaultCallback; + } + tryCatch(() => { + const id = callback(n, src); + if (id) { + defaultCallback(n, src, id); + } + })(); + } + } + return true; + } + }; + for (const [, [n]] of nodes) { + if (!setSource(n)) { + pending.push(n); + } + } + if (pending.length) { + fm_thumbnails('standalone', pending, setSource); + } + this.thumbsLoadingHandlers.clear(); + this.thumbsThatRequireLoading.clear(); + } + requestThumbnailCb(node, immediate, callback) { + if (node && node.fa) { + if (typeof immediate === 'function') { + callback = immediate; + immediate = 0; + } + node.seen = node.seen || -7; + this.thumbsLoadingHandlers.set(node.h, callback); + this.thumbsThatRequireLoading.set(node.fa, node); + delay('chat:mega-list2:thumb-loader', () => this.enqueueThumbnailRetrieval(), immediate || 480); + } + } + render() { + if (this.isMounted() && !this._calculated) { + this._recalculate(); + } + const { + listAdapter, + listAdapterOpts, + entries, + nodeAdapterProps, + viewMode, + header, + onContextMenu + } = this.props; + const className = `${listAdapter.containerClassName } megaList megaList2`; + const first = this._calculated.visibleFirstItemNum; + const last = this._calculated.visibleLastItemNum; + const nodes = []; + for (let i = first; i < last; i++) { + const node = entries[i]; + nodes.push(JSX_(this.props.nodeAdapter, (0,esm_extends.A)({ + key: `${i }_${ node[this.props.keyProp]}`, + h: node[this.props.keyProp], + index: i, + megaList: this, + listAdapter, + node, + calculated: this._calculated, + listAdapterOpts, + onContextMenu, + selected: this.props.selected ? this.props.selected.indexOf(node[this.props.keyProp]) > -1 : false, + highlighted: this.props.highlighted ? this.props.highlighted.indexOf(node[this.props.keyProp]) > -1 : false, + requestThumbnailCb: this.requestThumbnailCb, + keyProp: this.props.keyProp || 'h' + }, nodeAdapterProps))); + } + const listAdapterName = listAdapter.prototype.constructor.name; + return JSX_(REaCt().Fragment, null, JSX_(perfectScrollbar.O, { + key: `ps_${ listAdapterName }_${ viewMode}`, + options: this.options.perfectScrollOptions, + onUserScroll: this.onPsUserScroll, + className, + style: { + 'position': 'relative' + }, + ref: instance => { + this.domRef = instance; + } + }, JSX_(this.props.listAdapter, (0,esm_extends.A)({ + containerClassName: this.props.containerClassName, + key: `ps_${ listAdapterName }_${ this.props.viewMode }_la`, + ref: listAdapterInstance => { + this.listAdapterInstance = listAdapterInstance; + }, + listContentRef: listContent => { + this.listContent = listContent; + }, + header, + megaList: this, + calculated: this._calculated + }, listAdapterOpts), nodes))); + } +}, (0,applyDecoratedDescriptor.A)(_class.prototype, "onPsUserScroll", [_dec], Object.getOwnPropertyDescriptor(_class.prototype, "onPsUserScroll"), _class.prototype), _class); +// EXTERNAL MODULE: ./js/ui/jsx/fm/nodes/genericNodePropsComponent.jsx + 1 modules +const genericNodePropsComponent = REQ_(4285); +;// ./js/ui/jsx/fm/nodes/genericGrid.jsx + + +class GenericGrid extends genericNodePropsComponent.B { + render() { + const { + node, + calculated, + index, + listAdapter, + className, + keyProp + } = this.props; + const style = {}; + listAdapter.repositionItem(node, calculated, index, style); + const toApplySensitive = !!mega.sensitives.isSensitive(node) && (mega.sensitives.showGlobally ? 1 : 2); + let image = null; + let src = null; + let isThumbClass = ""; + if (node.fa && (is_image2(node) || is_video(node))) { + src = thumbnails.get(node.fa); + if (!src) { + this.props.requestThumbnailCb(node); + src = window.noThumbURI || ''; + } + image = src ? JSX_("img", { + alt: "", + src + }) : JSX_("img", { + alt: "" + }); + isThumbClass = " thumb"; + } else { + image = JSX_("img", null); + } + let fileStatusClass = ""; + if (node.fav) { + fileStatusClass += " icon-favourite-filled"; + } + return JSX_("a", { + className: `data-block-view megaListItem ui-droppable ui-draggable ui-draggable-handle ${ this.nodeProps.classNames.join(" ") }${className && className(node) || "" }${toApplySensitive ? toApplySensitive === 1 ? ' is-sensitive' : ' hidden-as-sensitive' : ''}`, + id: `chat_${ node[keyProp]}`, + onClick: e => { + this.props.onClick(e, this.props.node); + }, + onDoubleClick: e => { + this.props.onDoubleClick(e, this.props.node); + }, + title: this.nodeProps.title, + style + }, JSX_("span", { + className: `data-block-bg ${ isThumbClass}` + }, JSX_("span", { + className: "data-block-indicators" + }, JSX_("span", { + className: `file-status-icon indicator sprite-fm-mono${ fileStatusClass}` + }), JSX_("span", { + className: "versioning-indicator" + }, JSX_("i", { + className: "sprite-fm-mono icon-versions-previous" + })), JSX_("i", { + className: "sprite-fm-mono icon-link" + })), JSX_("span", { + className: `item-type-icon-90 icon-${ this.nodeProps.icon }-90` + }, image), JSX_("div", { + className: "video-thumb-details" + }, JSX_("i", { + className: "small-icon small-play-icon" + }), JSX_("span", null, "00:00"))), JSX_("span", { + className: "file-block-title" + }, this.nodeProps.title)); + } +} +;// ./js/ui/jsx/fm/nodes/genericTable.jsx + + + +class GenericTableHeader extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + } + render() { + const { + sortBy, + columns + } = this.props; + const columnsRendered = []; + for (let i = 0; i < columns.length; i++) { + var _colProps; + let col = columns[i]; + let colProps; + if (Array.isArray(col)) { + colProps = col[1]; + col = col[0]; + } + let sortable; + if (col.sortable) { + let classes = ""; + if (sortBy[0] === col.id) { + const ordClass = sortBy[1] === "desc" ? "icon-arrow-down" : "icon-arrow-up"; + classes = `${classes} ${ordClass}`; + } + if (col.id === 'fav') { + classes += ' hidden'; + } + sortable = JSX_("i", { + className: `sprite-fm-mono ${col.id} ${classes}` + }); + } + columnsRendered.push(JSX_("th", { + megatype: col.megatype, + className: col.headerClassName || col.megatype || "", + key: `${col.id }_${ i}`, + onClick: e => { + e.preventDefault(); + if (col.sortable) { + this.props.onClick(col.id); + } + } + }, JSX_("span", null, ((_colProps = colProps) == null ? void 0 : _colProps.label) || col.label), col.icon && JSX_("i", { + className: `sprite-fm-mono ${ col.icon}` + }), sortable)); + } + return JSX_("thead", { + ref: this.domRef + }, JSX_("tr", null, columnsRendered)); + } +} +class GenericTable extends genericNodePropsComponent.B { + render() { + let _this$nodeProps; + const { + node, + index, + listAdapterOpts, + className, + keyProp + } = this.props; + const toApplySensitive = !!mega.sensitives.isSensitive(node) && (mega.sensitives.showGlobally ? 1 : 2); + const columns = []; + for (let i = 0; i < listAdapterOpts.columns.length; i++) { + const customColumn = listAdapterOpts.columns[i]; + if (Array.isArray(customColumn)) { + columns.push(JSX_(customColumn[0], { + ...customColumn[1], + 'nodeAdapter': this, + 'h': node[keyProp], + node, + 'key': `${i }_${ customColumn[0].prototype.constructor.name}`, + keyProp + })); + } else { + columns.push(JSX_(customColumn, { + 'nodeAdapter': this, + 'h': node[keyProp], + node, + 'key': `${i }_${ customColumn.prototype.constructor.name}`, + keyProp + })); + } + } + const listClassName = listAdapterOpts.className; + return JSX_("tr", { + className: `node_${ node[keyProp] } ${ className && className(node) || "" } ${ listClassName && listClassName(node) || "" } ${ (_this$nodeProps = this.nodeProps) == null ? void 0 : _this$nodeProps.classNames.join(" ") }${toApplySensitive ? toApplySensitive === 1 ? ' is-sensitive' : ' hidden-as-sensitive' : ''}`, + id: node[keyProp], + onContextMenu: ev => { + if (this.props.onContextMenu) { + this.props.onContextMenu(ev, node[keyProp]); + } + }, + onClick: e => { + this.props.onClick(e, this.props.node); + }, + onDoubleClick: e => { + this.props.onDoubleClick(e, this.props.node); + }, + key: `${index }_${ node[keyProp]}` + }, columns); + } +} +;// ./js/ui/jsx/megaList/adapters.jsx + + +class GenericListAdapter extends mixins.w9 { + constructor(...args) { + super(...args); + this.customIsEventuallyVisible = true; + } +} +class Grid extends GenericListAdapter { + static repositionItem(node, calculated, index, style) { + style.position = "absolute"; + style.top = calculated.itemHeight * Math.floor(index / calculated.itemsPerRow); + if (calculated.itemsPerRow > 1) { + style.left = index % calculated.itemsPerRow * calculated.itemWidth; + } + } + render() { + return JSX_("div", { + className: "megaList-content", + ref: this.props.listContentRef, + style: { + 'position': 'relative' + } + }, this.props.children); + } +} +Grid.itemWidth = 212; +Grid.itemHeight = 212; +Grid.containerClassName = "file-block-scrolling megaListContainer"; +class Table extends GenericListAdapter { + onContentUpdated() { + const { + calculated + } = this.props; + const pusherHeight = calculated.visibleFirstItemNum * calculated.itemHeight | 0; + if (this.topPusher) { + this.topPusher.style.height = `${pusherHeight }px`; + } + if (this.bottomPusher) { + this.bottomPusher.style.height = `${calculated.contentHeight - pusherHeight - (calculated.visibleLastItemNum - calculated.visibleFirstItemNum) * calculated.itemHeight | 0 }px`; + } + } + componentDidUpdate() { + super.componentDidUpdate(); + this.onContentUpdated(); + } + render() { + return JSX_("table", { + width: "100%", + className: this.props.containerClassName || "grid-table table-hover fm-dialog-table" + }, this.props.header, JSX_("tbody", { + ref: this.props.listContentRef + }, JSX_("tr", { + className: "megalist-pusher top", + ref: r => { + this.topPusher = r; + } + }), this.props.children, JSX_("tr", { + className: "megalist-pusher bottom", + ref: r => { + this.bottomPusher = r; + } + }))); + } +} +Table.itemHeight = 32; +Table.itemsPerRow = 1; +Table.containerClassName = "grid-scrolling-table megaListContainer"; +// EXTERNAL MODULE: ./js/ui/jsx/fm/nodes/columns/columnFavIcon.jsx +const columnFavIcon = REQ_(6794); +;// ./js/ui/tooltips.jsx + + +class Handler extends REaCt().Component { + render() { + const { + className, + onMouseOver, + onMouseOut, + children + } = this.props; + return JSX_("span", { + className: ` + tooltip-handler + ${className || ''} + `, + onMouseOver, + onMouseOut + }, children); + } +} +class Contents extends REaCt().Component { + render() { + let className = `tooltip-contents dropdown body tooltip ${ this.props.className ? this.props.className : ""}`; + if (this.props.active) { + className += " visible"; + return JSX_("div", { + className + }, this.props.withArrow ? JSX_("i", { + className: "dropdown-white-arrow" + }) : null, this.props.children); + } else { + return null; + } + } +} +class Tooltip extends mixins.w9 { + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.state = { + 'active': false + }; + } + componentDidUpdate(oldProps, oldState) { + const self = this; + if (oldState.active === true && this.state.active === false) { + chatGlobalEventManager.removeEventListener('resize', `tooltip${ this.getUniqueId()}`); + } + if (self.state.active === true) { + self.repositionTooltip(); + chatGlobalEventManager.addEventListener('resize', `tooltip${ this.getUniqueId()}`, () => { + self.repositionTooltip(); + }); + if (this.props.onShown) { + this.props.onShown(); + } + } + } + repositionTooltip() { + let _this$domRef; + let elLeftPos, elTopPos, elWidth, elHeight; + let tooltipLeftPos, tooltipTopPos, tooltipWidth, tooltipHeight; + let docHeight; + let arrowClass; + if (!this.isMounted()) { + return; + } + const $container = $((_this$domRef = this.domRef) == null ? void 0 : _this$domRef.current); + const $el = $('.tooltip-handler', $container); + const $tooltip = $('.tooltip-contents', $container); + let {tooltipOffset} = this.props; + const arrow = this.props.withArrow; + if ($el && $tooltip) { + elWidth = $el.outerWidth(); + elHeight = $el.outerHeight(); + elLeftPos = $el.offset().left; + elTopPos = $el.offset().top; + tooltipWidth = $tooltip.outerWidth(); + tooltipHeight = $tooltip.outerHeight(); + docHeight = $(window).height(); + $tooltip.removeClass('dropdown-arrow left-arrow right-arrow up-arrow down-arrow').removeAttr('style'); + if (!tooltipOffset) { + tooltipOffset = 7; + } + if (elTopPos - tooltipHeight - tooltipOffset > 10) { + tooltipLeftPos = elLeftPos + elWidth / 2 - tooltipWidth / 2; + tooltipTopPos = elTopPos - tooltipHeight - tooltipOffset; + arrowClass = arrow ? 'dropdown-arrow down-arrow' : ''; + } else if (docHeight - (elTopPos + elHeight + tooltipHeight + tooltipOffset) > 10) { + tooltipLeftPos = elLeftPos + elWidth / 2 - tooltipWidth / 2; + tooltipTopPos = elTopPos + elHeight + tooltipOffset; + arrowClass = arrow ? 'dropdown-arrow up-arrow' : ''; + } else if (elLeftPos - tooltipWidth - tooltipOffset > 10) { + tooltipLeftPos = elLeftPos - tooltipWidth - tooltipOffset; + tooltipTopPos = elTopPos + elHeight / 2 - tooltipHeight / 2; + arrowClass = arrow ? 'dropdown-arrow right-arrow' : ''; + } else { + tooltipLeftPos = elLeftPos + elWidth + tooltipOffset; + tooltipTopPos = elTopPos + elHeight / 2 - tooltipHeight / 2; + arrowClass = arrow ? 'dropdown-arrow left-arrow' : ''; + } + $tooltip.css({ + 'left': tooltipLeftPos, + 'top': tooltipTopPos - 5 + }); + $tooltip.addClass(arrowClass); + } + } + onHandlerMouseOver() { + this.setState({ + 'active': true + }); + } + onHandlerMouseOut() { + this.setState({ + 'active': false + }); + } + render() { + const self = this; + const others = []; + let handler = null; + let contents = null; + let x = 0; + REaCt().Children.forEach(this.props.children, (child) => { + if (child.type.name === 'Handler') { + handler = REaCt().cloneElement(child, { + onMouseOver () { + self.onHandlerMouseOver(); + }, + onMouseOut () { + self.onHandlerMouseOut(); + } + }); + } else if (child.type.name === 'Contents') { + contents = REaCt().cloneElement(child, { + active: self.state.active, + withArrow: self.props.withArrow + }); + } else { + const tmp = REaCt().cloneElement(child, { + key: x++ + }); + others.push(tmp); + } + }); + return JSX_("span", { + ref: this.domRef, + className: this.props.className || '' + }, handler, contents, others); + } +} +Tooltip.defaultProps = { + 'hideable': true +}; + const tooltips = { + Tooltip, + Handler, + Contents +}; +;// ./js/ui/jsx/fm/nodes/columns/columnNodeName.jsx + + + +class ColumnNodeName extends genericNodePropsComponent.B { + constructor(...args) { + super(...args); + this.state = { + src: null + }; + } + static get label() { + return l[86]; + } + componentDidMount() { + super.componentDidMount(); + } + render() { + const { + nodeAdapter + } = this.props; + const { + node, + requestThumbnailCb + } = nodeAdapter.props; + const src = this.state.src || thumbnails.get(node.fa); + return JSX_("td", { + megatype: ColumnNodeName.megatype + }, src || is_image2(node) || is_video(node) ? JSX_(tooltips.Tooltip, { + withArrow: true, + className: "tooltip-handler-container", + onShown: () => { + if (!src) { + requestThumbnailCb(node, true, (n, src) => { + this.setState({ + src + }); + return `preview_${n.h}`; + }); + } + } + }, JSX_(tooltips.Handler, { + className: `item-type-icon icon-${fileIcon(node)}-24` + }), JSX_(tooltips.Contents, { + className: "img-preview" + }, JSX_("div", { + className: "dropdown img-wrapper img-block", + id: `preview_${node.h}` + }, JSX_("img", { + alt: "", + className: `thumbnail-placeholder ${node.h}`, + src: node.fa || src ? src || `${staticpath}/images/mega/ajax-loader-tiny.gif` : window.noThumbURI + })))) : JSX_("span", { + className: ` + item-type-icon icon-${fileIcon(node)}-24 + ` + }), JSX_("span", { + className: "tranfer-filetype-txt" + }, nodeAdapter.nodeProps.title)); + } +} +ColumnNodeName.sortable = true; +ColumnNodeName.id = 'name'; +ColumnNodeName.megatype = 'fname'; +;// ./js/ui/jsx/fm/nodes/columns/columnSize.jsx + + +class ColumnSize extends genericNodePropsComponent.B { + static get label() { + return l[87]; + } + render() { + const { + nodeAdapter + } = this.props; + return JSX_("td", { + megatype: ColumnSize.megatype, + className: "size" + }, nodeAdapter.nodeProps.size); + } +} +ColumnSize.sortable = true; +ColumnSize.id = "size"; +ColumnSize.megatype = "size"; +;// ./js/ui/jsx/fm/nodes/columns/columnTimeAdded.jsx + + +class ColumnTimeAdded extends genericNodePropsComponent.B { + static get label() { + return l[16169]; + } + render() { + const { + nodeAdapter + } = this.props; + return JSX_("td", { + megatype: ColumnTimeAdded.megatype, + className: "time ad" + }, nodeAdapter.nodeProps.timestamp); + } +} +ColumnTimeAdded.sortable = true; +ColumnTimeAdded.id = "ts"; +ColumnTimeAdded.megatype = "timeAd"; +;// ./js/ui/jsx/fm/nodes/columns/columnExtras.jsx + + +class ColumnExtras extends genericNodePropsComponent.B { + render() { + return JSX_("td", { + megatype: ColumnExtras.megatype, + className: "grid-url-field own-data extras-column" + }, JSX_("span", { + className: "versioning-indicator" + }, JSX_("i", { + className: "sprite-fm-mono icon-versions-previous" + })), JSX_("i", { + className: "sprite-fm-mono icon-link" + })); + } +} +ColumnExtras.sortable = false; +ColumnExtras.id = "extras"; +ColumnExtras.label = ""; +ColumnExtras.megatype = "extras"; +ColumnExtras.headerClassName = "grid-url-header"; +;// ./js/ui/jsx/fm/browserEntries.jsx + + + + + + + + + + + +class BrowserEntries extends mixins.w9 { + constructor(props) { + super(props); + this.state = { + 'sortBy': props.sortBy || ['name', 'asc'] + }; + this.toggleSortBy = this.toggleSortBy.bind(this); + } + UNSAFE_componentWillMount() { + this.lastCharKeyPressed = false; + this.lastCharKeyIndex = -1; + } + componentDidMount() { + super.componentDidMount(); + this.bindEvents(); + } + componentWillUnmount() { + super.componentWillUnmount(); + this.unbindEvents(); + } + componentDidUpdate(oldProps) { + if (oldProps.sortBy && (oldProps.sortBy[0] !== this.props.sortBy[0] || oldProps.sortBy[1] !== this.props.sortBy[1])) { + this.setState({ + 'sortBy': this.props.sortBy + }); + } + } + handleKeyNavigation(selectionManager, shiftKey, keyCode, viewMode) { + let curr; + const { + folderSelectNotAllowed + } = this.props; + if (shiftKey && folderSelectNotAllowed) { + curr = selectionManager.last_selected; + } + const {KEYS} = BrowserEntries; + if (viewMode) { + if (keyCode === KEYS.LEFT) { + selectionManager.select_prev(shiftKey, true); + } else if (keyCode === KEYS.RIGHT) { + selectionManager.select_next(shiftKey, true); + } else if (keyCode === KEYS.UP) { + selectionManager.select_grid_up(shiftKey, true); + } else { + selectionManager.select_grid_down(shiftKey, true); + } + } else if (keyCode === KEYS.UP) { + selectionManager.select_prev(shiftKey, true); + } else { + selectionManager.select_next(shiftKey, true); + } + if (shiftKey && folderSelectNotAllowed && $.selected.length > 1) { + const folderNodes = $.selected.filter(n => !M.isFileNode(M.getNodeByHandle(n))); + if (folderNodes.length > 1) { + if (!M.isFileNode(M.getNodeByHandle(curr))) { + array.remove(folderNodes, curr); + } + if (folderNodes.length) { + const newCurr = selectionManager.last_selected; + for (let i = 0; i < folderNodes.length; i++) { + selectionManager.remove_from_selection(folderNodes[i]); + } + if (M.isFileNode(M.getNodeByHandle(newCurr))) { + selectionManager.set_currently_selected(curr); + } else if (curr && $.selected.includes(curr)) { + selectionManager.set_currently_selected(curr); + } else if ($.selected.length) { + selectionManager.set_currently_selected($.selected[0]); + } + } + } + } + } + _invalidKeydownTarget(e) { + return e.target && (e.target.tagName === 'INPUT' || e.target.tagName === 'BUTTON' || e.target.tagName === 'TEXTAREA' && !e.target.classList.contains('messages-textarea') || e.target.tagName === 'SELECT'); + } + _isNavigationKeyDown(e, keyCode) { + const { + KEYS + } = BrowserEntries; + const { + viewMode + } = this.props; + return !e.metaKey && (!viewMode && (keyCode === KEYS.UP || keyCode === KEYS.DOWN) || viewMode && (keyCode === KEYS.UP || keyCode === KEYS.DOWN || keyCode === KEYS.LEFT || keyCode === KEYS.RIGHT)); + } + bindEvents() { + const { + KEYS + } = BrowserEntries; + $(document.body).rebind(`keydown.be${this.getUniqueId()}`, e => { + let charTyped = false; + const keyCode = e.which || e.keyCode; + const $searchField = $('div.fm-files-search input'); + const $typingArea = $('textarea.messages-textarea'); + const { + selectionManager, + viewMode + } = this.props; + if (this._invalidKeydownTarget(e)) { + return; + } + if ($searchField.is(':focus')) { + return; + } + if ($typingArea.is(':focus')) { + $typingArea.trigger('blur'); + } + if (keyCode === KEYS.A && (e.ctrlKey || e.metaKey)) { + this.handleSelectAll(); + e.preventDefault(); + e.stopPropagation(); + } else if (e.metaKey && keyCode === KEYS.UP || keyCode === KEYS.BACKSPACE) { + this.handleKeyBack(); + } else if (this._isNavigationKeyDown(e, keyCode)) { + this.handleKeyNavigation(selectionManager, e.shiftKey, keyCode, viewMode); + } else if (keyCode >= 48 && keyCode <= 57 || keyCode >= 65 && keyCode <= 123 || keyCode > 255) { + charTyped = String.fromCharCode(keyCode).toLowerCase(); + this.handleCharTyped(charTyped); + } else if (keyCode === KEYS.ENTER || e.metaKey && keyCode === KEYS.DOWN) { + this.handleAttach(); + } + mega.ui.mInfoPanel.reRenderIfVisible($.selected); + if (!charTyped) { + this.lastCharKeyPressed = false; + this.lastCharKeyIndex = -1; + } + }); + } + handleSelectAll() { + const { + selectionManager, + folderSelectNotAllowed, + entries + } = this.props; + selectionManager.select_all(); + if (folderSelectNotAllowed) { + const folders = entries.filter(h => !M.isFileNode(M.getNodeByHandle(h))); + for (let i = 0; i < folders.length; i++) { + selectionManager.remove_from_selection(folders[i].h); + } + } + } + handleKeyBack() { + const { + viewMode, + currentlyViewedEntry + } = this.props; + if (!viewMode) { + const currentFolder = M.getNode(currentlyViewedEntry); + if (currentFolder.p) { + this.expandFolder(currentFolder.p); + } + } + } + handleCharTyped(charTyped) { + const { + entries, + keyProp, + selectionManager + } = this.props; + const foundMatchingNodes = entries.filter(node => { + return node.name && node.name.substring(0, 1).toLowerCase() === charTyped; + }); + if (this.lastCharKeyPressed === charTyped) { + this.lastCharKeyIndex++; + } + this.lastCharKeyPressed = charTyped; + if (foundMatchingNodes.length > 0) { + if (!foundMatchingNodes[this.lastCharKeyIndex]) { + this.lastCharKeyIndex = 0; + } + const foundNode = foundMatchingNodes[this.lastCharKeyIndex]; + selectionManager.clear_selection(); + selectionManager.set_currently_selected(foundNode[keyProp], true); + } + } + handleAttach() { + const { + highlighted, + folderSelectNotAllowed, + entries, + keyProp, + onAttachClicked + } = this.props; + let selectedNodes = highlighted; + if (folderSelectNotAllowed) { + selectedNodes = highlighted.filter(h => { + const node = entries.find(e => e[keyProp] === h); + return node && node.t === 0; + }); + if (selectedNodes.length === 0) { + const cursorNode = highlighted[0] && M.getNodeByHandle(highlighted[0]); + if (cursorNode.t === 1) { + this.expandFolder(cursorNode[keyProp]); + return; + } else if (highlighted.length > 0) { + this.expandFolder(highlighted[0]); + return; + } + return; + } + } + onAttachClicked(selectedNodes); + } + unbindEvents() { + $(document.body).off(`keydown.be${ this.getUniqueId()}`); + } + onEntryClick(e, node) { + const { + selectionManager, + keyProp, + folderSelectNotAllowed, + highlighted = [] + } = this.props; + this.lastCharKeyPressed = false; + this.lastCharKeyIndex = -1; + e.stopPropagation(); + e.preventDefault(); + if (!e.shiftKey && !e.ctrlKey && !e.metaKey) { + selectionManager.clear_selection(); + selectionManager.set_currently_selected(node[keyProp]); + } else if (e.shiftKey) { + if ($.selected && $.selected.length) { + let selFolders; + if (folderSelectNotAllowed) { + selFolders = $.selected.filter(n => !M.isFileNode(M.getNodeByHandle(n))); + } + selectionManager.shift_select_to(node[keyProp], false, true, false); + if (folderSelectNotAllowed && $.selected.length > 1) { + const folderNodes = $.selected.filter(n => !M.isFileNode(M.getNodeByHandle(n))); + if (folderNodes.length > 1) { + array.remove(folderNodes, selFolders[0] || folderNodes[0]); + for (let i = 0; i < folderNodes.length; i++) { + selectionManager.remove_from_selection(folderNodes[i]); + } + } + } + } else { + selectionManager.set_currently_selected(node[keyProp]); + } + } else if (e.ctrlKey || e.metaKey) { + if (!highlighted || !highlighted.includes(node[keyProp])) { + if (folderSelectNotAllowed) { + if (node.t === 1 && highlighted.length > 0) { + return; + } else if (highlighted.some(nodeId => { + const node = M.getNodeByHandle(nodeId); + return node && node.t === 1; + })) { + selectionManager.clear_selection(); + } + } + selectionManager.add_to_selection(node[keyProp]); + } else if (highlighted && highlighted.includes(node[keyProp])) { + if (folderSelectNotAllowed) { + if (node.t === 1) { + return; + } else if (highlighted.some(nodeId => { + const node = M.getNodeByHandle(nodeId); + return node && node.t === 1; + })) { + selectionManager.clear(); + } + } + selectionManager.remove_from_selection(node[keyProp]); + } + } + } + expandFolder(nodeId) { + const self = this; + const node = M.getNodeByHandle(nodeId); + if (node) { + self.lastCharKeyPressed = false; + self.lastCharKeyIndex = -1; + self.setState({ + 'selected': [], + 'highlighted': [], + 'cursor': false + }); + self.props.onExpand(node); + self.forceUpdate(); + } + } + onEntryDoubleClick(e, node) { + const self = this; + self.lastCharKeyPressed = false; + self.lastCharKeyIndex = -1; + e.stopPropagation(); + e.preventDefault(); + const share = M.getNodeShare(node); + if (share && share.down) { + return; + } + if (node.t) { + self.props.onExpand(node); + self.forceUpdate(); + } else { + self.onEntryClick(e, node); + self.props.onAttachClicked(); + } + } + customIsEventuallyVisible() { + return true; + } + toggleSortBy(colId) { + const newState = {}; + if (this.state.sortBy[0] === colId) { + newState.sortBy = [colId, this.state.sortBy[1] === "asc" ? "desc" : "asc"]; + } else { + newState.sortBy = [colId, "asc"]; + } + this.setState(newState); + this.props.onSortByChanged(newState.sortBy); + } + render() { + const {viewMode} = this.props; + const listAdapterOpts = this.props.listAdapterOpts || {}; + if (!viewMode) { + listAdapterOpts.columns = [columnFavIcon.$, ColumnNodeName, ColumnSize, ColumnTimeAdded, ColumnExtras]; + } + if (this.props.listAdapterColumns) { + listAdapterOpts.columns = this.props.listAdapterColumns; + } + if (this.props.isLoading) { + return JSX_("div", { + className: "dialog-empty-block active dialog-fm folder" + }, JSX_("div", { + className: "dialog-empty-pad" + }, JSX_("i", { + className: "sprite-fm-mono icon-cloud-drive" + }), JSX_("div", { + className: "dialog-empty-header" + }, l[5533]))); + } else if (!this.props.entries.length && this.props.currentlyViewedEntry === 'search') { + return JSX_("div", { + className: "dialog-empty-block active dialog-fm folder" + }, JSX_("div", { + className: "dialog-empty-pad" + }, JSX_("i", { + className: "sprite-fm-mono icon-preview-reveal" + }), JSX_("div", { + className: "dialog-empty-header" + }, l[978]))); + } else if (!this.props.entries.length) { + const nilComp = this.props.NilComponent; + return nilComp && (typeof nilComp === "function" ? nilComp() : nilComp) || JSX_("div", { + className: "dialog-empty-block active dialog-fm folder" + }, this.props.currentlyViewedEntry === 'shares' ? JSX_("div", { + className: "dialog-empty-pad" + }, JSX_("i", { + className: "sprite-fm-mono icon-folder-incoming-share-filled" + }), JSX_("div", { + className: "dialog-empty-header" + }, l[6871])) : JSX_("div", { + className: "dialog-empty-pad" + }, JSX_("i", { + className: "sprite-fm-mono icon-folder-filled" + }), JSX_("div", { + className: "dialog-empty-header" + }, this.props.currentlyViewedEntry === M.RootID ? l[1343] : M.u[this.props.currentlyViewedEntry] ? l[6787] : l[782]))); + } + return JSX_(MegaList2, { + viewMode, + sortBy: this.state.sortBy, + currentlyViewedEntry: this.props.currentlyViewedEntry, + selected: this.props.selected, + highlighted: this.props.highlighted, + containerClassName: this.props.containerClassName, + nodeAdapterProps: { + 'onClick': (e, node) => { + this.onEntryClick(e, node); + mega.ui.mInfoPanel.reRenderIfVisible($.selected); + }, + 'onDoubleClick': (e, node) => { + this.onEntryDoubleClick(e, node); + }, + 'className': node => { + return this.props.highlighted.indexOf(node[this.props.keyProp]) > -1 ? " ui-selected" : ""; + } + }, + ref: r => { + this.megaList = r; + }, + listAdapter: viewMode ? Grid : Table, + nodeAdapter: viewMode ? GenericGrid : GenericTable, + listAdapterOpts, + entries: this.props.entries, + itemHeight: this.props.megaListItemHeight, + headerHeight: viewMode ? 0 : 56, + header: !viewMode && JSX_(GenericTableHeader, { + columns: listAdapterOpts.columns, + sortBy: this.state.sortBy, + onClick: this.toggleSortBy, + headerContainerClassName: this.props.headerContainerClassName + }), + currentdirid: this.props.currentdirid, + onContextMenu: this.props.onContextMenu, + keyProp: this.props.keyProp + }); + } +} +BrowserEntries.KEYS = { + A: 65, + UP: 38, + DOWN: 40, + LEFT: 37, + RIGHT: 39, + ENTER: 13, + BACKSPACE: 8 +}; +BrowserEntries.defaultProps = { + 'hideable': true, + 'requiresUpdateOnResize': true +}; +;// ./js/ui/jsx/fm/fmView.jsx + + + +class FMView extends mixins.w9 { + constructor(props) { + let _this$dataSource; + super(props); + this.domRef = REaCt().createRef(); + let initialSortBy = props.initialSortBy || ['name', 'asc']; + if (props.fmConfigSortEnabled) { + let _fmconfig$sortmodes; + const sortId = props.fmConfigSortId; + assert(sortId, 'missing fmConfigSortId'); + if ((_fmconfig$sortmodes = fmconfig.sortmodes) != null && (_fmconfig$sortmodes = _fmconfig$sortmodes[sortId]) != null && _fmconfig$sortmodes.n) { + let _fmconfig$sortmodes2; + initialSortBy = this._translateFmConfigSortMode((_fmconfig$sortmodes2 = fmconfig.sortmodes) == null ? void 0 : _fmconfig$sortmodes2[sortId]); + } + } + this.state = { + 'sortBy': initialSortBy, + 'selected': [], + 'highlighted': [], + 'entries': null + }; + this.dataSource = this.props.dataSource; + this.state.entries = this.getEntries(); + this.onAttachClicked = this.onAttachClicked.bind(this); + this.onContextMenu = this.onContextMenu.bind(this); + if ((_this$dataSource = this.dataSource) != null && _this$dataSource.addChangeListener) { + this._listener = this.dataSource.addChangeListener(() => { + if (!this.isMounted()) { + return; + } + this.setState({ + 'entries': this.getEntries() + }); + }); + } + this.initSelectionManager(); + } + getDataSourceNode(h) { + return this.dataSource && this.dataSource[h] || M.getNodeByHandle(h); + } + _translateFmConfigSortMode(currentSortModes) { + const sortId = this.props.fmConfigSortId; + assert(sortId, 'missing fmConfigSortId'); + const sortByArr = []; + if (currentSortModes != null && currentSortModes.n) { + sortByArr[0] = currentSortModes.n; + const sortMap = this.props.fmConfigSortMap; + const aliasKeys = sortMap && Object.keys(sortMap) || []; + for (const alias of aliasKeys) { + if (sortByArr[0] === sortMap[alias]) { + sortByArr[0] = alias; + break; + } + } + sortByArr[1] = currentSortModes.d === 1 ? "asc" : "desc"; + } + return sortByArr; + } + initSelectionManager(entries) { + this.selectionManager = new SelectionManager2_React(entries || this.state.entries, this.props.currentdirid || "cloud-drive", () => { + let _this$browserEntries; + return (_this$browserEntries = this.browserEntries) == null || (_this$browserEntries = _this$browserEntries.megaList) == null || (_this$browserEntries = _this$browserEntries._calculated) == null ? void 0 : _this$browserEntries.itemsPerRow; + }, nodeHandle => { + if (this.browserEntries && this.browserEntries.megaList) { + this.browserEntries.megaList.scrollToItem(nodeHandle); + } + }, { + 'onSelectedUpdated': selectedList => { + this.onSelectionUpdated(selectedList); + } + }); + } + onSelectionUpdated(selectedList) { + selectedList = [...selectedList]; + const highlighted = selectedList; + if (this.props.folderSelectNotAllowed && !this.props.folderSelectable) { + selectedList = selectedList.filter(nodeId => !this.getDataSourceNode(nodeId).t); + } + this.setState({ + 'selected': selectedList, + highlighted + }); + this.props.onSelected(selectedList); + this.props.onHighlighted(highlighted); + $.selected = highlighted; + } + getEntries(newState) { + const self = this; + const sortBy = newState && newState.sortBy || self.state.sortBy; + const order = sortBy[1] === "asc" ? 1 : -1; + const entries = []; + let sortFunc, filterFunc, dataSource; + const minSearchLength = self.props.minSearchLength || 3; + const showSen = mega.sensitives.showGlobally; + if (self.props.currentlyViewedEntry === "search" && self.props.searchValue && self.props.searchValue.length >= minSearchLength) { + dataSource = this.dataSource || { + ...M.tnd, + ...M.d + }; + filterFunc = M.getFilterBySearchFn(self.props.searchValue); + } else { + const tmp = M.getChildren(self.props.currentlyViewedEntry) || M.tree[self.props.currentlyViewedEntry] || this.props.dataSource; + dataSource = Object.create(null); + for (const h in tmp) { + const n = this.getDataSourceNode(h); + if (n) { + dataSource[h] = n; + } + } + } + const { + customFilterFn + } = this.props; + for (const h in dataSource) { + const n = dataSource[h]; + const e = n && (!n.h || n.h.length === 8 && crypto_keyok(n) || n.h.length === 11); + const s = e && !n.fv && (showSen || !mega.sensitives.isSensitive(n)); + if (s && (!customFilterFn || customFilterFn(n)) && (!filterFunc || filterFunc(n))) { + entries.push(n); + } + } + if (sortBy[0] === "name") { + sortFunc = M.getSortByNameFn(); + } else if (sortBy[0] === "size") { + sortFunc = M.getSortBySizeFn(); + } else if (sortBy[0] === "ts") { + sortFunc = M.getSortByDateTimeFn(); + } else if (sortBy[0] === "rts") { + sortFunc = M.getSortByRtsFn(); + } else if (sortBy[0] === "status") { + sortFunc = M.getSortByStatusFn(); + } else if (sortBy[0] === "interaction") { + sortFunc = M.getSortByInteractionFn(); + } else if (sortBy[0] === "verification") { + sortFunc = M.getSortByVerificationFn(); + } else if (sortBy[0] === "email") { + sortFunc = M.getSortByEmail(); + } else if (sortBy[0] === 'access') { + sortFunc = (a, b, o) => typeof a.r !== 'undefined' && typeof b.r !== 'undefined' && (a.r < b.r ? -1 : 1) * o; + } else { + sortFunc = M.sortByFavFn(order); + } + const folders = []; + if (this.props.sortFoldersFirst) { + for (let i = entries.length; i--;) { + if (entries[i] && entries[i].t) { + folders.unshift(entries[i]); + entries.splice(i, 1); + } + } + } + folders.sort((a, b) => { + return sortFunc(a, b, order); + }); + entries.sort((a, b) => { + return sortFunc(a, b, order); + }); + return folders.concat(entries); + } + onHighlighted(nodes) { + this.setState({ + 'highlighted': nodes + }); + if (this.props.onHighlighted) { + this.props.onHighlighted(nodes); + } + } + finishedLoading(newState) { + newState.isLoading = false; + newState.entries = this.getEntries(); + this.initSelectionManager(newState.entries); + this.setState(newState); + } + addOrUpdRawListener() { + if (this._rawListener) { + mBroadcaster.removeListener(this._rawListener); + } + this._rawListener = mBroadcaster.addListener(`fmViewUpdate:${ this.props.currentlyViewedEntry}`, () => { + this.setState({ + 'entries': this.getEntries() + }, () => { + if (this.browserEntries.isMounted()) { + this.browserEntries.forceUpdate(); + } + }); + }); + } + componentDidMount() { + let _this$dataSource2; + super.componentDidMount(); + if (!((_this$dataSource2 = this.dataSource) != null && _this$dataSource2.addChangeListener)) { + this.addOrUpdRawListener(); + } + if (this.props.fmConfigSortEnabled) { + this._sortModeListener = mBroadcaster.addListener("fmconfig:sortmodes", sortModes => { + this.onFmConfigSortModeChanged(sortModes); + }); + } + } + componentDidUpdate(prevProps) { + const { + currentlyViewedEntry: currEntry, + searchValue: currSearch + } = this.props; + const { + currentlyViewedEntry: prevEntry, + searchValue: prevSearch + } = prevProps; + const dataSourceChanged = this.props.dataSource !== prevProps.dataSource; + if (dataSourceChanged || prevEntry !== currEntry || currSearch !== prevSearch) { + let _this$dataSource3; + this.dataSource = this.props.dataSource; + const newState = { + 'selected': [], + 'highlighted': [] + }; + if (!((_this$dataSource3 = this.dataSource) != null && _this$dataSource3.addChangeListener)) { + this.addOrUpdRawListener(); + } + const handle = currEntry; + if (handle === 'shares') { + newState.isLoading = true; + this.setState(newState); + dbfetch.geta(Object.keys(M.c.shares || {})).always(() => { + this.finishedLoading(newState); + }); + return; + } + if (this.getDataSourceNode(handle).t && !M.getChildren(handle)) { + this.setState({ + 'isLoading': true + }); + dbfetch.get(handle).always(() => { + this.finishedLoading(newState); + }); + return; + } + const entries = this.getEntries(); + this.initSelectionManager(entries); + this.setState({ + entries + }); + } + } + onAttachClicked() { + this.props.onAttachClicked(); + } + onContextMenu() {} + componentWillUnmount() { + super.componentWillUnmount(); + if (this._listener) { + let _this$dataSource4; + (_this$dataSource4 = this.dataSource) == null || _this$dataSource4.removeChangeListener(this._listener); + } + if (this._rawListener) { + mBroadcaster.removeListener(this._rawListener); + } + if (this._sortModeListener) { + mBroadcaster.removeListener(this._sortModeListener); + } + $.selected = []; + this.selectionManager.destroy(); + this.selectionManager = undefined; + $('.dropdown.body.files-menu.context').css('z-index', ''); + } + onSortByChanged(newState) { + if (newState[0] === this.state.sortBy[0] && newState[1] === this.state.sortBy[1]) { + return; + } + const entries = this.getEntries({ + 'sortBy': newState + }); + this.setState({ + 'sortBy': newState, + entries, + 'selected': [], + 'highlighted': [] + }, () => { + if (this.props.onSortByChanged) { + this.props.onSortByChanged(newState); + } + if (this.props.fmConfigSortEnabled) { + const sortId = this.props.fmConfigSortId; + assert(sortId, 'fmConfigSortId missing'); + if (newState[0] === this.props.initialSortBy[0] && newState[1] === this.props.initialSortBy[1]) { + const sortModes = typeof fmconfig.sortmodes !== 'undefined' ? fmconfig.sortmodes : Object.create(null); + delete sortModes[sortId]; + mega.config.set('sortmodes', sortModes); + return; + } + const map = this.props.fmConfigSortMap || Object.create(null); + const name = map[newState[0]] || newState[0]; + const direction = newState[1] === "asc" ? 1 : -1; + fmsortmode(sortId, name, direction); + } + }); + this.initSelectionManager(entries); + } + onFmConfigSortModeChanged(sortModes) { + const currentSortMode = sortModes[this.props.fmConfigSortId]; + if (!currentSortMode) { + this.onSortByChanged(this.props.initialSortBy || ['name', 'asc']); + } else { + const newSortMode = this._translateFmConfigSortMode(currentSortMode); + if (this.state.sortBy[0] !== newSortMode[0] || this.state.sortBy[1] !== newSortMode[1]) { + this.onSortByChanged(newSortMode); + } + } + } + render() { + return JSX_("div", { + ref: this.domRef, + className: "content-container", + onClick: ev => { + $.hideContextMenu(ev); + } + }, JSX_(BrowserEntries, { + isLoading: this.state.isLoading || this.props.nodeLoading, + currentlyViewedEntry: this.props.currentlyViewedEntry, + entries: this.state.entries || [], + onExpand: node => { + this.setState({ + 'selected': [], + 'highlighted': [] + }); + this.props.onExpand(node[this.props.keyProp || 'h']); + }, + sortBy: this.state.sortBy, + folderSelectNotAllowed: this.props.folderSelectNotAllowed, + onAttachClicked: this.onAttachClicked, + viewMode: this.props.viewMode, + selected: this.state.selected, + highlighted: this.state.highlighted, + onContextMenu: this.props.onContextMenu || this.onContextMenu, + selectionManager: this.selectionManager, + ref: browserEntries => { + this.browserEntries = browserEntries; + }, + onSortByChanged: newState => { + this.onSortByChanged(newState); + }, + listAdapterColumns: this.props.listAdapterColumns, + currentdirid: this.props.currentdirid, + containerClassName: this.props.containerClassName, + headerContainerClassName: this.props.headerContainerClassName, + megaListItemHeight: this.props.megaListItemHeight, + keyProp: this.props.keyProp || 'h', + NilComponent: this.props.NilComponent, + listAdapterOpts: this.props.listAdapterOpts + })); + } +} + + }, + + 6794 +(_, EXP_, REQ_) { + + REQ_.d(EXP_, { + $: () => ColumnFavIcon + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _genericNodePropsComponent1__ = REQ_(4285); + + +class ColumnFavIcon extends _genericNodePropsComponent1__ .B { + render() { + const { + nodeAdapter + } = this.props; + const { + node + } = nodeAdapter.props; + const isFavouritable = node.r === 2; + return JSX_("td", { + megatype: ColumnFavIcon.megatype, + className: ColumnFavIcon.megatype + }, JSX_("span", { + className: `grid-status-icon sprite-fm-mono ${ missingkeys[node.h] ? " icon-info" : nodeAdapter.nodeProps.fav ? " icon-favourite-filled" : " icon-dot" }${!isFavouritable && " disabled" || ""}`, + onClick: () => { + if (isFavouritable) { + M.favourite([node.h], !node.fav); + } + } + })); + } +} +ColumnFavIcon.sortable = true; +ColumnFavIcon.id = "fav"; +ColumnFavIcon.label = ""; +ColumnFavIcon.icon = "icon-favourite-filled"; +ColumnFavIcon.megatype = "fav"; +ColumnFavIcon.headerClassName = "grid-first-th fav"; + + }, + + 4285 +(_, EXP_, REQ_) { + + +// EXPORTS +REQ_.d(EXP_, { + B: () => GenericNodePropsComponent +}); + +// EXTERNAL MODULE: ./js/chat/mixins.js +const mixins = REQ_(8264); +;// ./js/ui/jsx/fm/nodes/nodeProperties.jsx +class NodeProperties { + static get(node, changeListener) { + assert(node.h, 'missing handle for node'); + if (NodeProperties._globalCleanupTimer) { + NodeProperties._globalCleanupTimer.abort(); + } + (NodeProperties._globalCleanupTimer = tSleep(120)).then(() => { + NodeProperties.cleanup(0); + }); + let nodeProps; + if (!NodeProperties._cache.has(node.h)) { + nodeProps = new NodeProperties(node, changeListener); + NodeProperties._cache.set(node.h, nodeProps); + } + return nodeProps || NodeProperties._cache.get(node.h); + } + unuse(changeListener) { + const {node} = this; + if (!node) { + if (d) { + console.warn("This should not happen."); + } + return; + } + this.changeListeners.delete(changeListener); + let usages = NodeProperties._usages.get(this); + if (usages) { + NodeProperties._usages.set(this, --usages); + if (usages === 0 && NodeProperties._cache.size > NodeProperties.MAX_CACHE_SIZE) { + delay('nodePropCleanup', NodeProperties.cleanup, 1000); + } + } + } + static cleanup(maxCacheSize) { + maxCacheSize = typeof maxCacheSize === "undefined" ? NodeProperties.MAX_CACHE_SIZE : maxCacheSize; + const len = NodeProperties._cache.size; + let removed = 0; + for (const entry of NodeProperties._cache) { + const id = entry[0]; + const node = entry[1]; + const usage = NodeProperties._usages.get(node); + if (usage === 0) { + NodeProperties._usages.delete(node); + node._cleanup(); + NodeProperties._cache.delete(id); + removed++; + if (len - removed < maxCacheSize) { + return; + } + } + } + } + constructor(node, changeListener) { + this.node = node; + this.changeListeners = new Set(); + if (changeListener) { + this.changeListeners.add(changeListener); + } + const _onChange = () => { + this.initProps(); + for (const listener of this.changeListeners) { + listener(); + } + }; + if (this.node.addChangeListener) { + this._listener = this.node.addChangeListener(_onChange); + } else { + this._mbListener = mBroadcaster.addListener(`nodeUpdated:${ node.h}`, _onChange); + } + this.initProps(); + } + use(changeListener) { + if (changeListener) { + this.changeListeners.add(changeListener); + } + NodeProperties._usages.set(this, (NodeProperties._usages.get(this) | 0) + 1); + } + _cleanup() { + if (this._listener) { + this.node.removeChangeListener(this._listener); + } + if (this._mbListener) { + mBroadcaster.removeListener(this._mbListener); + } + oDestroy(this); + } + initProps() { + const {node} = this; + lazy(this, 'title', () => { + if (missingkeys[node.h]) { + return node.t ? l[8686] : l[8687]; + } + return M.getNameByHandle(node.h); + }); + lazy(this, 'classNames', () => { + const classNames = []; + if (node.su) { + classNames.push('inbound-share'); + } + if (node.t) { + classNames.push('folder'); + } else { + classNames.push('file'); + } + const share = this.shareData; + if (missingkeys[node.h] || share.down) { + if (share.down) { + classNames.push('taken-down'); + } + if (missingkeys[node.h]) { + classNames.push('undecryptable'); + } + } + if (share) { + classNames.push('linked'); + } + if (node.lbl && !folderlink) { + const colourLabel = M.getLabelClassFromId(node.lbl); + classNames.push('colour-label'); + classNames.push(colourLabel); + } + return classNames; + }); + lazy(this, 'icon', () => { + return fileIcon(node); + }); + lazy(this, 'isFolder', () => { + return !!node.t; + }); + lazy(this, 'shareData', () => { + return M.getNodeShare(node); + }); + lazy(this, 'isTakendown', () => { + return this.shareData && !!this.shareData.down; + }); + lazy(this, 'fav', () => { + return !!node.fav; + }); + lazy(this, 'size', () => { + return bytesToSize(node.tb || node.s); + }); + lazy(this, 'timestamp', () => { + return time2date(node.ts); + }); + lazy(this, 'root', () => { + return M.getNodeRoot(node.h); + }); + lazy(this, 'incomingShareData', () => { + const result = {}; + if (node.r === 1) { + result.accessLabel = l[56]; + result.accessIcon = 'icon-permissions-write'; + } else if (node.r === 2) { + result.accessLabel = l[57]; + result.accessIcon = 'icon-star'; + } else { + result.accessLabel = l[55]; + result.accessIcon = 'icon-read-only'; + } + return result; + }); + lazy(this, 'timestamp', () => { + return time2date(node.ts); + }); + lazy(this, 'onlineStatus', () => { + return M.onlineStatusClass(node.presence ? node.presence : "unavailable"); + }); + } +} +NodeProperties._cache = new Map(); +NodeProperties._usages = new WeakMap(); +NodeProperties._globalCleanupTimer = void 0; +NodeProperties.MAX_CACHE_SIZE = 100; +if (d) { + window.NodeProperties = NodeProperties; +} +;// ./js/ui/jsx/fm/nodes/genericNodePropsComponent.jsx + + +class GenericNodePropsComponent extends mixins.w9 { + constructor(props) { + super(props); + if (this.props.node.h) { + this.nodeProps = NodeProperties.get(this.props.node); + this.changeListener = this.changeListener.bind(this); + } + } + changeListener() { + if (this.isMounted()) { + this.safeForceUpdate(); + } + } + UNSAFE_componentWillReceiveProps(nextProps) { + if (nextProps.highlighted !== this.props.highlighted) { + this.safeForceUpdate(); + } + } + UNSAFE_componentWillMount() { + let _this$nodeProps; + if (super.UNSAFE_componentWillMount) { + super.UNSAFE_componentWillMount(); + } + (_this$nodeProps = this.nodeProps) == null || _this$nodeProps.use(this.changeListener); + } + componentWillUnmount() { + let _this$nodeProps2; + super.componentWillUnmount(); + (_this$nodeProps2 = this.nodeProps) == null || _this$nodeProps2.unuse(this.changeListener); + } +} + + } + +}]); \ No newline at end of file diff --git a/js/chat/bundle.core-ui.js b/js/chat/bundle.core-ui.js new file mode 100644 index 0000000000..67b4ce589b --- /dev/null +++ b/js/chat/bundle.core-ui.js @@ -0,0 +1,11098 @@ +/** @file automatically generated, do not edit it. */ +"use strict"; +(self.webpackChunk_meganz_webclient = self.webpackChunk_meganz_webclient || []).push([[493],{ + + 8491 +(_, EXP_, REQ_) { + +REQ_.r(EXP_); + REQ_.d(EXP_, { + "default": () => ChatToaster + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _mixins1__ = REQ_(8264); + const _meetings_utils_jsx2__ = REQ_(3901); + const _ui_buttons3__ = REQ_(5155); + + + + +const NAMESPACE = 'chat-toast'; +class ChatToaster extends react0___default().Component { + constructor(props) { + super(props); + this.uid = `${this.constructor.name}--${(0,_mixins1__ .LP)()}`; + this.domRef = react0___default().createRef(); + this.state = { + toast: null, + endTime: 0, + fmToastId: null, + persistentToast: null + }; + this.toasts = []; + this.persistentToasts = []; + } + enqueueToast(e) { + if (this.props.showDualNotifications && e.data.options && e.data.options.persistent) { + this.persistentToasts.push(e.data); + } else { + this.toasts.push(e.data); + } + this.pollToasts(); + } + pollToasts() { + const { + toast: shownToast, + persistentToast: shownPersistentToast + } = this.state; + const { + isRootToaster, + showDualNotifications, + onShownToast + } = this.props; + const now = Date.now(); + if (this.toasts.length + this.persistentToasts.length) { + if (this.domRef.current && (!isRootToaster && (0,_meetings_utils_jsx2__ .Av)() || M.chat)) { + if (this.toasts.length && !shownToast) { + this.dispatchToast(this.toasts.shift(), now); + } + if (showDualNotifications && this.persistentToasts.length && !shownPersistentToast) { + const persistentToast = this.persistentToasts.shift(); + this.setState({ + persistentToast + }, () => this.pollToasts()); + if (typeof onShownToast === 'function') { + onShownToast(persistentToast); + } + } + } else if (isRootToaster && this.toasts.length && !shownToast) { + const toast = this.toasts.shift(); + this.dispatchToast(toast, now, { + fmToastId: 'tmp' + }); + this.dispatchFMToast(toast); + } + } + } + dispatchFMToast(toast, redraw) { + window.toaster.alerts.medium(...toast.renderFM()).then(fmToastId => { + if (!redraw) { + toast.onShown(fmToastId); + } + this.setState({ + fmToastId + }); + if (toast.updater && typeof toast.updater === 'function') { + toast.updater(); + toast.updateInterval = setInterval(() => { + toast.updater(); + const value = toast.render(); + if (!value) { + window.toaster.alerts.hide(fmToastId); + return this.onClose(toast.options && toast.options.persistent); + } + if (value !== $('span', `#${fmToastId}`).text()) { + $('span', `#${fmToastId}`).text(value); + } + }, 250); + } + }); + } + dispatchToast(toast, now, options = {}) { + const { + fmToastId, + endTime, + silent + } = options; + const { + onShownToast, + onHideToast + } = this.props; + this.setState({ + toast, + endTime: endTime || now + toast.getTTL(), + fmToastId + }, () => { + if (!silent) { + toast.onShown(); + } + this.timeout = setTimeout(() => { + delete this.timeout; + this.setState({ + toast: null, + endTime: 0 + }, () => this.pollToasts()); + if (typeof toast.onEnd === 'function') { + toast.onEnd(); + } + if (typeof onHideToast === 'function') { + onHideToast(toast); + } + if (toast.updateInterval) { + clearInterval(toast.updateInterval); + delete toast.updateInterval; + } + }, endTime ? endTime - now : toast.getTTL()); + }); + if (typeof onShownToast === 'function') { + onShownToast(toast); + } + } + onClose(persistent) { + const { + showDualNotifications, + onHideToast + } = this.props; + const { + toast, + persistentToast + } = this.state; + if (showDualNotifications && persistent) { + if (typeof persistentToast.onEnd === 'function') { + persistentToast.onEnd(); + } + this.setState({ + persistentToast: null + }, () => this.pollToasts()); + if (typeof onHideToast === 'function') { + onHideToast(persistentToast); + } + return; + } + if (toast.updateInterval) { + clearInterval(toast.updateInterval); + delete toast.updateInterval; + } + clearTimeout(this.timeout); + delete this.timeout; + if (typeof toast.onEnd === 'function') { + toast.onEnd(); + } + if (typeof onHideToast === 'function') { + onHideToast(toast); + } + this.setState({ + toast: null, + endTime: 0 + }, () => this.pollToasts()); + } + flush() { + const { + toast, + persistentToast, + fmToastId + } = this.state; + this.endToastIntervals(); + if (fmToastId && fmToastId !== 'tmp') { + window.toaster.alerts.hide(fmToastId); + } + this.toasts = []; + this.persistentToasts = []; + if (this.timeout) { + clearTimeout(this.timeout); + delete this.timeout; + } + if (toast) { + this.onClose(toast.persistent); + } + if (persistentToast) { + this.onClose(true); + } + this.setState({ + toast: null, + endTime: 0, + fmToastId: null, + persistentToast: null + }); + } + endToastIntervals() { + if (!this.props.isRootToaster) { + return; + } + for (const toast of this.toasts) { + if (toast.updateInterval) { + clearInterval(toast.updateInterval); + } + } + for (const toast of this.persistentToasts) { + if (toast.updateInterval) { + clearInterval(toast.updateInterval); + } + } + } + componentDidMount() { + megaChat.rebind(`onChatToast.toaster${this.uid}`, e => this.enqueueToast(e)); + megaChat.rebind(`onChatToastFlush.toaster${this.uid}`, () => this.flush()); + onIdle(() => this.pollToasts()); + if (this.props.isRootToaster) { + this.bpcListener = mBroadcaster.addListener('beforepagechange', tpage => { + const { + toast, + endTime, + fmToastId + } = this.state; + const now = Date.now(); + if (toast && endTime - 500 > now) { + const toChat = tpage.includes('chat') && tpage !== 'securechat'; + if (toChat && !M.chat) { + clearTimeout(this.timeout); + window.toaster.alerts.hide(fmToastId); + if (toast.updateInterval) { + clearInterval(toast.updateInterval); + delete toast.updateInterval; + } + this.dispatchToast(toast, now, { + endTime, + silent: true + }); + } else if (!toChat && M.chat) { + clearTimeout(this.timeout); + this.dispatchToast(toast, now, { + fmToastId: 'tmp', + endTime, + silent: true + }); + this.dispatchFMToast(toast, true); + } + } else if (toast && typeof toast.onEnd === 'function') { + toast.onEnd(); + } + }); + } + } + componentWillUnmount() { + megaChat.off(`onChatToast.toaster${this.uid}`); + megaChat.off(`onChatToastFlush.toaster${this.uid}`); + if (this.bpcListener) { + mBroadcaster.removeListener(this.bpcListener); + } + if (this.timeout) { + clearTimeout(this.timeout); + } + this.endToastIntervals(); + } + render() { + const { + hidden, + isRootToaster, + showDualNotifications + } = this.props; + const { + toast, + fmToastId, + persistentToast + } = this.state; + return !hidden && !fmToastId && JSX_("div", { + ref: this.domRef, + className: `chat-toast-bar ${isRootToaster ? 'toaster-root' : ''}` + }, showDualNotifications && persistentToast && JSX_(ChatToastMsg, { + toast: persistentToast, + isRootToaster, + usePersistentStyle: true, + onClose: p => this.onClose(p) + }), toast && JSX_(ChatToastMsg, { + toast, + isRootToaster, + isDualToast: !!persistentToast, + onClose: p => this.onClose(p) + })); + } +} +class ChatToastMsg extends react0___default().Component { + constructor(...args) { + super(...args); + this.state = { + value: '' + }; + } + componentDidMount() { + const { + toast, + onClose + } = this.props; + if (toast.updater && typeof toast.updater === 'function') { + toast.updater(); + this.updateInterval = setInterval(() => { + toast.updater(); + const value = toast.render(); + if (!value) { + return onClose(toast.options && toast.options.persistent); + } + if (value !== this.state.value) { + this.setState({ + value + }); + } + }, 250); + } + const value = toast.render(); + if (value) { + this.setState({ + value + }); + } else { + onClose(toast.options && toast.options.persistent); + } + } + componentWillUnmount() { + if (this.updateInterval) { + clearInterval(this.updateInterval); + } + } + render() { + const { + toast, + isRootToaster, + isDualToast, + usePersistentStyle, + onClose + } = this.props; + const { + value + } = this.state; + if (usePersistentStyle && toast.options.persistent) { + return JSX_("div", { + className: `${NAMESPACE} chat-persistent-toast` + }, value || toast.render()); + } + const closeButton = toast.close && JSX_(_ui_buttons3__ .$, { + className: "chat-toast-close", + icon: "sprite-fm-mono icon-close-component", + onClick: onClose + }); + const icon = toast.icon && JSX_("i", { + className: toast.icon + }); + if (isRootToaster) { + return JSX_("div", { + className: `${NAMESPACE} chat-toast-wrapper root-toast` + }, JSX_("div", { + className: "toast-value-wrapper" + }, icon, JSX_("div", { + className: "toast-value" + }, value || toast.render())), closeButton); + } + return JSX_("div", { + className: `${NAMESPACE} chat-toast-wrapper theme-light-forced ${isDualToast ? 'dual-toast' : ''}` + }, JSX_("div", { + className: "toast-value" + }, value || toast.render())); + } +} + + }, + + 2558 +(_, EXP_, REQ_) { + + +// EXPORTS +REQ_.d(EXP_, { + A: () => composedTextArea +}); + +// EXTERNAL MODULE: external "React" +const external_React_ = REQ_(1594); +const REaCt = REQ_.n(external_React_); +;// ./js/chat/ui/whosTyping.jsx + +class WhosTyping extends REaCt().Component { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + this.state = { + currentlyTyping: {} + }; + } + componentDidMount() { + const { + chatRoom + } = this.props; + chatRoom.rebind('onParticipantTyping.whosTyping', (e, user_handle, bCastCode) => { + if (user_handle === u_handle) { + return; + } + const u_h = user_handle; + if (u_h === u_handle) { + return; + } else if (!M.u[u_h]) { + return; + } + const currentlyTyping = { + ...this.state.currentlyTyping + }; + if (currentlyTyping[u_h]) { + currentlyTyping[u_h].abort(); + } + if (bCastCode === 1) { + const timer = tSleep(5); + timer.then(() => { + this.stoppedTyping(u_h); + }); + currentlyTyping[u_h] = timer; + this.setState({ + currentlyTyping + }); + } else { + this.stoppedTyping(u_h); + } + this.forceUpdate(); + }); + } + componentWillUnmount() { + this.props.chatRoom.off('onParticipantTyping.whosTyping'); + } + stoppedTyping(u_h) { + if (this.domRef.current) { + const { + currentlyTyping + } = this.state; + if (currentlyTyping[u_h]) { + const newState = { + ...currentlyTyping + }; + if (!newState[u_h].aborted) { + newState[u_h].abort(); + } + delete newState[u_h]; + this.setState({ + currentlyTyping: newState + }); + } + } + } + render() { + const users = Object.keys(this.state.currentlyTyping); + if (users.length > 0) { + const names = users.map(u_h => M.getNameByHandle(u_h)).filter(String); + let namesDisplay = ""; + let areMultipleUsersTyping = false; + if (names.length > 1) { + areMultipleUsersTyping = true; + namesDisplay = [names.splice(0, names.length - 1).join(", "), names[0]]; + } else { + areMultipleUsersTyping = false; + namesDisplay = [names[0]]; + } + return JSX_("div", { + ref: this.domRef, + className: "typing-block" + }, JSX_("div", { + className: "typing-text" + }, areMultipleUsersTyping ? l[8872].replace("%1", namesDisplay[0]).replace("%2", namesDisplay[1]) : l[8629].replace("%1", namesDisplay[0])), JSX_("div", { + className: "typing-bounce" + }, JSX_("div", { + className: "typing-bounce1" + }), JSX_("div", { + className: "typing-bounce2" + }), JSX_("div", { + className: "typing-bounce3" + }))); + } + return null; + } +} +// EXTERNAL MODULE: ./js/chat/ui/typingArea.jsx + 5 modules +const typingArea = REQ_(4762); +// EXTERNAL MODULE: ./js/ui/buttons.jsx +const buttons = REQ_(5155); +// EXTERNAL MODULE: ./js/ui/dropdowns.jsx +const dropdowns = REQ_(1510); +;// ./js/chat/ui/composedTextArea.jsx + + + + + +const ComposedTextArea = ({ + chatRoom, + parent, + containerRef, + typingAreaText, + onTypingAreaChanged +}) => JSX_("div", { + className: "chat-textarea-block" +}, JSX_(WhosTyping, { + chatRoom +}), JSX_(typingArea.T, { + chatRoom, + className: "main-typing-area", + containerRef, + disabled: chatRoom.isReadOnly(), + persist: true, + text: typingAreaText, + onValueChanged: onTypingAreaChanged, + onUpEditPressed: () => { + const keys = chatRoom.messagesBuff.messages.keys(); + for (let i = keys.length; i--;) { + const message = chatRoom.messagesBuff.messages[keys[i]]; + const contact = M.u[message.userId]; + if (!contact) { + continue; + } + if (message.isEditable() && !message.requiresManualRetry && !message.deleted && (!message.type || message instanceof Message) && (!message.isManagement || !message.isManagement())) { + parent.historyPanel.editMessage(message.messageId); + return true; + } + } + return false; + }, + onResized: () => { + parent.historyPanel.handleWindowResize(); + }, + onConfirm: messageContents => { + const { + messagesListScrollable + } = parent.historyPanel; + if (messageContents && messageContents.length > 0) { + if (!chatRoom.scrolledToBottom) { + chatRoom.scrolledToBottom = true; + parent.lastScrollPosition = 0; + chatRoom.rebind('onMessagesBuffAppend.pull', () => { + if (messagesListScrollable) { + messagesListScrollable.scrollToBottom(false); + delay('messagesListScrollable', () => { + messagesListScrollable.enable(); + }, 1500); + } + }); + chatRoom.sendMessage(messageContents); + messagesListScrollable == null || messagesListScrollable.disable(); + messagesListScrollable == null || messagesListScrollable.scrollToBottom(true); + } else { + chatRoom.sendMessage(messageContents); + } + } + } +}, JSX_(buttons.$, { + className: "popup-button left", + icon: "sprite-fm-mono icon-add", + disabled: chatRoom.isReadOnly() +}, JSX_(dropdowns.ms, { + className: "wide-dropdown attach-to-chat-popup light", + noArrow: "true", + positionMy: "left top", + positionAt: "left bottom", + vertOffset: 4, + wrapper: "#fmholder" +}, JSX_("div", { + className: "dropdown info-txt" +}, l[23753] || 'Send...'), JSX_(dropdowns.tJ, { + className: "link-button", + icon: "sprite-fm-mono icon-cloud", + label: l[19794] || 'My Cloud Drive', + disabled: mega.paywall, + onClick: () => chatRoom.trigger('openAttachCloudDialog') +}), JSX_(dropdowns.tJ, { + className: "link-button", + icon: "sprite-fm-mono icon-session-history", + label: l[19795] || 'My computer', + disabled: mega.paywall, + onClick: () => chatRoom.uploadFromComputer() +}), !is_eplusplus && !is_chatlink && !chatRoom.isNote && JSX_(REaCt().Fragment, null, JSX_("hr", null), JSX_(dropdowns.tJ, { + className: "link-button", + icon: "sprite-fm-mono icon-send-contact", + label: l.share_contact_button, + onClick: () => chatRoom.trigger('openSendContactDialog') +})))))); + const composedTextArea = ComposedTextArea; + + }, + + 5677 +(_, EXP_, REQ_) { + + +// EXPORTS +REQ_.d(EXP_, { + ConversationPanels: () => ConversationPanels, + e: () => allContactsInChat, + z: () => excludedParticipants +}); + +// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/applyDecoratedDescriptor.js +const applyDecoratedDescriptor = REQ_(793); +// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/extends.js +const esm_extends = REQ_(8168); +// EXTERNAL MODULE: external "React" +const external_React_ = REQ_(1594); +const REaCt = REQ_.n(external_React_); +// EXTERNAL MODULE: ./js/ui/utils.jsx +const utils = REQ_(6411); +// EXTERNAL MODULE: ./js/chat/mixins.js +const mixins = REQ_(8264); +// EXTERNAL MODULE: ./js/ui/buttons.jsx +const buttons = REQ_(5155); +// EXTERNAL MODULE: ./js/ui/modalDialogs.jsx + 1 modules +const modalDialogs = REQ_(8120); +// EXTERNAL MODULE: ./js/ui/dropdowns.jsx +const ui_dropdowns = REQ_(1510); +// EXTERNAL MODULE: ./js/chat/ui/contacts.jsx +const ui_contacts = REQ_(8022); +// EXTERNAL MODULE: ./js/chat/chatRoom.jsx +const chat_chatRoom = REQ_(7057); +;// ./js/ui/historyRetentionDialog.jsx + + + + +const LIMIT = { + CHARS: 2, + HOURS: 24, + DAYS: 31, + WEEKS: 4, + MONTHS: 12 +}; +class HistoryRetentionDialog extends external_React_.Component { + constructor(props) { + super(props); + this.dialogName = 'msg-retention-dialog'; + this.inputRef = REaCt().createRef(); + this.state = { + selectedTimeFormat: chat_chatRoom.zd.HOURS, + timeRange: undefined + }; + this.handleRadioChange = e => { + const selectedTimeFormat = e.target.value; + this.setState(prevState => ({ + selectedTimeFormat, + timeRange: this.filterTimeRange(prevState.timeRange, selectedTimeFormat) + })); + }; + this.handleOnTimeCheck = e => { + const checkingValue = e.type === 'paste' ? e.clipboardData.getData('text') : e.key; + if (e.keyCode !== 8 && isNaN(checkingValue)) { + e.preventDefault(); + } + }; + this.handleOnTimeChange = e => { + const timeValue = e.target.value; + this.setState(prevState => ({ + timeRange: this.filterTimeRange(timeValue, prevState.selectedTimeFormat) + })); + }; + const { + chatRoom + } = props; + this.state.timeRange = chatRoom.getRetentionTimeFormatted(); + if (this.state.timeRange === 0) { + this.state.timeRange = ''; + } + this.state.selectedTimeFormat = chatRoom.getRetentionFormat(); + this.state.selectedTimeFormat = this.state.selectedTimeFormat === chat_chatRoom.zd.DISABLED ? chat_chatRoom.zd.HOURS : this.state.selectedTimeFormat; + } + hasInput() { + return this.state.timeRange && parseInt(this.state.timeRange, 10) >= 1; + } + getMaxTimeRange(selectedTimeFormat) { + switch (selectedTimeFormat) { + case chat_chatRoom.zd.HOURS: + return LIMIT.HOURS; + case chat_chatRoom.zd.DAYS: + return LIMIT.DAYS; + case chat_chatRoom.zd.WEEKS: + return LIMIT.WEEKS; + case chat_chatRoom.zd.MONTHS: + return LIMIT.MONTHS; + } + } + getParsedLabel(label, timeRange) { + timeRange = timeRange ? parseInt(timeRange, 10) : this.getMaxTimeRange(label); + switch (label) { + case chat_chatRoom.zd.HOURS: + return mega.icu.format(l.plural_hour, timeRange); + case chat_chatRoom.zd.DAYS: + return mega.icu.format(l.plural_day, timeRange); + case chat_chatRoom.zd.WEEKS: + return mega.icu.format(l.plural_week, timeRange); + case chat_chatRoom.zd.MONTHS: + return mega.icu.format(l.plural_month, timeRange); + } + } + filterTimeRange(timeRange, selectedTimeFormat) { + if (timeRange.length > LIMIT.CHARS) { + return timeRange.substring(0, LIMIT.CHARS); + } + timeRange = parseInt(timeRange, 10); + if (timeRange === 0 || isNaN(timeRange)) { + return ''; + } + return Math.min(this.getMaxTimeRange(selectedTimeFormat), timeRange); + } + handleOnSubmit(e) { + if (!this.hasInput()) { + return; + } + e.preventDefault(); + e.stopPropagation(); + const { + chatRoom, + onClose + } = this.props; + const { + selectedTimeFormat, + timeRange + } = this.state; + let time = 0; + switch (selectedTimeFormat) { + case chat_chatRoom.zd.HOURS: + time = hoursToSeconds(Number(timeRange)); + break; + case chat_chatRoom.zd.DAYS: + time = daysToSeconds(Number(timeRange)); + break; + case chat_chatRoom.zd.WEEKS: + time = daysToSeconds(Number(timeRange) * 7); + break; + case chat_chatRoom.zd.MONTHS: + time = daysToSeconds(Number(timeRange) * 30); + break; + } + chatRoom.setRetention(time); + onClose(); + } + renderCustomRadioButton() { + return [chat_chatRoom.zd.HOURS, chat_chatRoom.zd.DAYS, chat_chatRoom.zd.WEEKS, chat_chatRoom.zd.MONTHS].map(label => { + return JSX_(CustomRadioButton, { + checked: this.state.selectedTimeFormat === label, + label: this.getParsedLabel(label, this.state.timeRange), + name: "time-selector", + value: label, + onChange: this.handleRadioChange, + key: label + }); + }); + } + componentDidMount() { + M.safeShowDialog(this.dialogName, () => { + $(document.body).rebind('keydown.historyRetentionDialog', e => { + const key = e.keyCode || e.which; + if (key === 13) { + this.handleOnSubmit(e); + } + }); + }); + } + componentWillUnmount() { + $(document.body).off('keydown.historyRetentionDialog'); + if ($.dialog === this.dialogName) { + closeDialog(); + } + } + render() { + const { + chatRoom, + onClose + } = this.props; + const { + selectedTimeFormat, + timeRange + } = this.state; + return JSX_(modalDialogs.A.ModalDialog, (0,esm_extends.A)({}, this.state, { + chatRoom, + onClose, + dialogName: this.dialogName, + dialogType: "tool", + onClick: () => this.inputRef.current.focus() + }), JSX_("header", null, JSX_("h2", { + id: "msg-retention-dialog-title" + }, l[23434])), JSX_("section", { + className: "content" + }, JSX_("div", { + className: "content-block" + }, JSX_("p", null, l[23435])), JSX_("div", { + className: "content-block form" + }, JSX_("div", { + className: "form-section" + }, JSX_("span", { + className: "form-section-placeholder" + }, this.getParsedLabel(selectedTimeFormat, timeRange)), JSX_("input", { + type: "number", + min: "0", + step: "1", + max: this.getMaxTimeRange(selectedTimeFormat), + className: "form-section-time", + placeholder: this.getMaxTimeRange(selectedTimeFormat), + ref: this.inputRef, + autoFocus: true, + value: timeRange, + onChange: this.handleOnTimeChange, + onKeyPress: this.handleOnTimeCheck, + onPaste: this.handleOnTimeCheck + })), JSX_("div", { + className: "form-section" + }, JSX_("div", { + className: "form-section-radio" + }, this.renderCustomRadioButton())))), JSX_("footer", null, JSX_("div", { + className: "footer-container" + }, JSX_("button", { + className: "mega-button", + onClick: onClose + }, JSX_("span", null, l.msg_dlg_cancel)), JSX_("button", { + className: ` + mega-button positive + ${this.hasInput() ? '' : 'disabled'} + `, + onClick: e => this.handleOnSubmit(e) + }, JSX_("span", null, l[726]))))); + } +} +function CustomRadioButton({ + checked = false, + label, + name, + value, + onChange +}) { + return JSX_("label", { + key: value, + className: "radio-txt" + }, label, JSX_("div", { + className: `custom-radio small green-active ${ checked ? "radioOn" : "radioOff"}` + }, JSX_("input", { + type: "radio", + name, + value, + checked, + onChange + }))); +} +// EXTERNAL MODULE: ./js/ui/perfectScrollbar.jsx +const perfectScrollbar = REQ_(1301); +;// ./js/ui/accordion.jsx + + +class AccordionPanel extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + } + render() { + const { + accordionClass, + expanded, + title, + className, + children, + onToggle + } = this.props; + return JSX_("div", { + ref: this.domRef, + className: ` + chat-dropdown + container + ${accordionClass || ''} + ` + }, JSX_("div", { + className: ` + chat-dropdown + header + ${expanded ? 'expanded' : ''} + `, + onClick: onToggle + }, JSX_("span", null, title), JSX_("i", { + className: "sprite-fm-mono icon-arrow-down" + })), expanded ? JSX_("div", { + className: ` + chat-dropdown + content + have-animation + ${className | ''} + ` + }, children) : null); + } +} +class Accordion extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + this.state = { + expandedPanel: this.props.expandedPanel + }; + } + onToggle(e, key) { + const obj = {}; + obj[key] = !(this.state.expandedPanel || {})[key]; + this.setState({ + 'expandedPanel': obj + }); + this.props.onToggle && this.props.onToggle(key); + } + render() { + const self = this; + const classes = `accordion-panels ${ self.props.className ? self.props.className : ''}`; + const accordionPanels = []; + let x = 0; + REaCt().Children.forEach(self.props.children, child => { + if (!child) { + return; + } + if (child.type.name === 'AccordionPanel' || child.type.name && child.type.name.indexOf('AccordionPanel') > -1) { + accordionPanels.push(REaCt().cloneElement(child, { + key: child.key, + expanded: !!self.state.expandedPanel[child.key], + accordion: self, + onToggle (e) { + self.onToggle(e, child.key); + } + })); + } else { + accordionPanels.push(REaCt().cloneElement(child, { + key: x++, + accordion: self + })); + } + }); + return JSX_("div", { + ref: this.domRef, + className: classes + }, accordionPanels); + } +} + +;// ./js/chat/ui/participantsList.jsx + + + + + +class ParticipantsList extends mixins.w9 { + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.state = { + 'scrollPositionY': 0, + 'scrollHeight': 144 + }; + this.doResizesOnComponentUpdate = SoonFc(10, function () { + let _self$domRef; + const self = this; + if (!self.isMounted()) { + return; + } + let fitHeight = self.contactsListScroll.getContentHeight(); + if (!fitHeight) { + return null; + } + const $node = $((_self$domRef = self.domRef) == null ? void 0 : _self$domRef.current); + const $parentContainer = $node.closest('.chat-right-pad'); + const maxHeight = $parentContainer.outerHeight(true) - $('.chat-right-head', $parentContainer).outerHeight(true) - 72; + if (fitHeight < $('.buttons-block', $parentContainer).outerHeight(true)) { + fitHeight = Math.max(fitHeight, 53); + } else if (maxHeight < fitHeight) { + fitHeight = Math.max(maxHeight, 53); + } + fitHeight = Math.min(self.calculateListHeight($parentContainer), fitHeight); + const $contactsList = $('.chat-contacts-list', $parentContainer); + if ($contactsList.height() !== fitHeight) { + $('.chat-contacts-list', $parentContainer).height(fitHeight); + if (self.contactsListScroll) { + self.contactsListScroll.reinitialise(); + } + } + if (self.state.scrollHeight !== fitHeight) { + self.setState({ + 'scrollHeight': fitHeight + }); + } + self.onUserScroll(); + }); + } + onUserScroll() { + if (!this.contactsListScroll) { + return; + } + const scrollPosY = this.contactsListScroll.getScrollPositionY(); + if (this.state.scrollPositionY !== scrollPosY) { + this.setState({ + 'scrollPositionY': scrollPosY + }); + } + } + calculateListHeight($parentContainer) { + const room = this.props.chatRoom; + return ($parentContainer ? $parentContainer : $('.conversationsApp')).outerHeight() - 144 - 10 - (room.type === "public" && room.observers > 0 ? 48 : 0) - (room.isReadOnly() ? 12 : 0); + } + componentDidUpdate() { + const self = this; + if (!self.isMounted()) { + return; + } + if (!self.contactsListScroll) { + return null; + } + self.doResizesOnComponentUpdate(); + } + render() { + const { + chatRoom + } = this.props; + if (!chatRoom) { + return null; + } + return JSX_("div", { + ref: this.domRef, + className: "chat-contacts-list" + }, JSX_(perfectScrollbar.O, { + chatRoom, + members: chatRoom.members, + ref: ref => { + this.contactsListScroll = ref; + }, + disableCheckingVisibility: true, + onUserScroll: SoonFc(this.onUserScroll.bind(this), 76), + requiresUpdateOnResize: true, + isVisible: chatRoom.isCurrentlyActive, + options: { + suppressScrollX: true + } + }, JSX_(ParticipantsListInner, { + chatRoom, + members: chatRoom.members, + scrollPositionY: this.state.scrollPositionY, + scrollHeight: this.state.scrollHeight, + disableCheckingVisibility: true + }))); + } +} +ParticipantsList.defaultProps = { + 'requiresUpdateOnResize': true, + 'contactCardHeight': 36 +}; +class ParticipantsListInner extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + } + render() { + const room = this.props.chatRoom; + const {contactCardHeight} = this.props; + const {scrollPositionY} = this.props; + const {scrollHeight} = this.props; + const { + OPERATOR, + FULL, + READONLY + } = ChatRoom.MembersSet.PRIVILEGE_STATE; + if (!room) { + return null; + } + if (!room.isCurrentlyActive) { + return false; + } + const contacts = room.getParticipantsExceptMe(); + const contactsList = []; + const firstVisibleUserNum = Math.floor(scrollPositionY / contactCardHeight); + const visibleUsers = Math.ceil(scrollHeight / contactCardHeight); + const contactListInnerStyles = { + 'height': contacts.length * contactCardHeight + }; + if ((room.type === "group" || room.type === "public") && !room.stateIsLeftOrLeaving() && room.members.hasOwnProperty(u_handle)) { + contacts.unshift(u_handle); + contactListInnerStyles.height += contactCardHeight; + } + const onRemoveClicked = contactHash => { + room.trigger('onRemoveUserRequest', [contactHash]); + }; + const onSetPrivClicked = (contactHash, priv) => { + if (room.members[contactHash] !== priv) { + room.trigger('alterUserPrivilege', [contactHash, priv]); + } + }; + for (let i = 0; i < contacts.length; i++) { + const contactHash = contacts[i]; + if (!(contactHash in M.u)) { + continue; + } + const contact = M.u[contactHash]; + if (i < firstVisibleUserNum || i > firstVisibleUserNum + visibleUsers) { + continue; + } + const dropdowns = []; + let dropdownIconClasses = "small-icon tiny-icon icons-sprite grey-dots"; + const dropdownRemoveButton = []; + if (room.type === "public" || room.type === "group" && room.members) { + if (room.iAmOperator() && contactHash !== u_handle) { + dropdownRemoveButton.push(JSX_(ui_dropdowns.tJ, { + className: "red", + key: "remove", + icon: "sprite-fm-mono icon-disabled-filled", + label: l[8867], + onClick: onRemoveClicked.bind(this, contactHash) + })); + } + if (room.iAmOperator()) { + dropdowns.push(JSX_("div", { + key: "setPermLabel", + className: "dropdown-items-info" + }, l[8868])); + dropdowns.push(JSX_(ui_dropdowns.tJ, { + key: "privOperator", + icon: "sprite-fm-mono icon-admin-outline", + label: l[8875], + className: ` + tick-item + ${room.members[contactHash] === OPERATOR ? 'active' : ''} + `, + disabled: contactHash === u_handle, + onClick: () => onSetPrivClicked(contactHash, OPERATOR) + })); + dropdowns.push(JSX_(ui_dropdowns.tJ, { + key: "privFullAcc", + icon: "sprite-fm-mono icon-chat", + className: ` + tick-item + ${room.members[contactHash] === FULL ? 'active' : ''} + `, + disabled: contactHash === u_handle, + label: l[8874], + onClick: () => onSetPrivClicked(contactHash, FULL) + })); + dropdowns.push(JSX_(ui_dropdowns.tJ, { + key: "privReadOnly", + icon: "sprite-fm-mono icon-read-only", + className: ` + tick-item + ${room.members[contactHash] === READONLY ? 'active' : ''} + `, + disabled: contactHash === u_handle, + label: l[8873], + onClick: () => onSetPrivClicked(contactHash, READONLY) + })); + } + const baseClassName = 'sprite-fm-mono'; + switch (room.members[contactHash]) { + case OPERATOR: + dropdownIconClasses = `${baseClassName} icon-admin`; + break; + case FULL: + dropdownIconClasses = `${baseClassName} icon-chat-filled`; + break; + case READONLY: + dropdownIconClasses = `${baseClassName} icon-read-only`; + break; + default: + break; + } + contactsList.push(JSX_(ui_contacts.nB, { + key: contact.u, + contact, + chatRoom: room, + className: "right-chat-contact-card", + dropdownPositionMy: "left top", + dropdownPositionAt: "left top", + dropdowns, + dropdownDisabled: contactHash === u_handle || is_chatlink || is_eplusplus, + dropdownButtonClasses: "contacts-icon", + dropdownRemoveButton, + dropdownIconClasses, + noLoading: true, + isInCall: room.uniqueCallParts && room.uniqueCallParts[contactHash], + style: { + width: 234, + position: 'absolute', + top: i * contactCardHeight + } + })); + } + } + return JSX_("div", { + ref: this.domRef, + className: "chat-contacts-list-inner default-bg", + style: contactListInnerStyles + }, contactsList); + } +} +ParticipantsListInner.defaultProps = { + requiresUpdateOnResize: true, + contactCardHeight: 32, + scrollPositionY: 0, + scrollHeight: 128, + chatRoom: undefined +}; + +// EXTERNAL MODULE: ./js/chat/ui/messages/generic.jsx + 14 modules +const generic = REQ_(8025); +;// ./js/chat/ui/sharedFilesAccordionPanel.jsx + +let _dec, _class; + + + +class SharedFileItem extends mixins.u9 { + render() { + const self = this; + const {message} = this.props; + const contact = Message.getContactForMessage(message); + const name = M.getNameByHandle(contact.u); + const timestamp = time2date(message.delay); + const {node} = this.props; + const {icon} = this.props; + return JSX_("div", { + className: `chat-shared-block ${ self.props.isLoading ? "is-loading" : ""}`, + key: `${message.messageId }_${ node.h}`, + onClick: () => this.props.isPreviewable ? M.viewMediaFile(node) : M.addDownload([node]), + onDoubleClick: () => M.addDownload([node]) + }, JSX_("div", { + className: `icon-or-thumb ${thumbnails.has(node.fa) ? "thumb" : ""}` + }, JSX_("div", { + className: `item-type-icon-90 icon-${icon}-90` + }), JSX_("div", { + className: "img-wrapper", + id: this.props.imgId + }, JSX_("img", { + alt: "", + src: thumbnails.get(node.fa) || "" + }))), JSX_("div", { + className: "chat-shared-info" + }, JSX_("span", { + className: "txt" + }, node.name), JSX_("span", { + className: "txt small" + }, JSX_(utils.zT, null, name)), JSX_("span", { + className: "txt small grey" + }, timestamp))); + } +} +const SharedFilesAccordionPanel = (_dec = utils.Ay.SoonFcWrap(350), _class = class SharedFilesAccordionPanel extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + } + eventuallyRenderThumbnails() { + if (this.allShownNodes) { + const pending = []; + const nodes = new Map(this.allShownNodes); + const render = n => { + if (thumbnails.has(n.fa)) { + const src = thumbnails.get(n.fa); + const batch = [...nodes.get(n.fa)]; + for (let i = batch.length; i--;) { + const n = batch[i]; + let img = document.getElementById(`sharedFiles!${n.ch}`); + if (img && (img = img.querySelector('img'))) { + img.src = src; + if (img = Object(img.parentNode).parentNode) { + img.classList.add('thumb'); + } + } + } + return true; + } + }; + for (const [, [n]] of nodes) { + if (!render(n)) { + pending.push(n); + } + } + this.allShownNodes.clear(); + if (pending.length) { + fm_thumbnails('standalone', pending, render); + } + } + } + UNSAFE_componentWillMount() { + this.allShownNodes = new MapSet(); + } + componentWillUnmount() { + super.componentWillUnmount(); + delete this.allShownNodes; + } + componentDidUpdate() { + this.eventuallyRenderThumbnails(); + } + render() { + const self = this; + const room = self.props.chatRoom; + const mb = room.messagesBuff; + let contents = null; + let currentPage = mb.sharedFilesPage; + const startPos = currentPage * 12; + const endPos = startPos + 12; + let totalPages = mb.haveMoreSharedFiles ? "..." : Math.ceil(mb.sharedFiles.length / 12); + totalPages = mb.sharedFiles.length && !totalPages ? 1 : totalPages; + const haveMore = mb.haveMoreSharedFiles || currentPage + 1 < totalPages; + const files = []; + if (!mb.haveMoreSharedFiles && currentPage === totalPages) { + currentPage = mb.sharedFilesPage = Math.max(totalPages - 1, 0); + } + if (this.props.expanded) { + let prev = null; + let next = null; + if (currentPage > 0) { + prev = JSX_("div", { + className: "chat-share-nav button prev", + onClick () { + mb.sharedFilesPage--; + self.safeForceUpdate(); + } + }); + } + if (haveMore) { + next = JSX_("div", { + className: "chat-share-nav button next", + onClick () { + if (self.isLoadingMore) { + return; + } + if (mb.sharedFiles.length < endPos + 12) { + self.isLoadingMore = true; + mb.retrieveSharedFilesHistory(12).catch(dump).finally(() => { + self.isLoadingMore = false; + mb.sharedFilesPage++; + if (!mb.haveMoreSharedFiles && mb.sharedFilesPage > totalPages) { + mb.sharedFilesPage = totalPages - 1; + } + Soon(() => { + self.safeForceUpdate(); + }); + }); + } else { + mb.sharedFilesPage++; + } + Soon(() => { + self.safeForceUpdate(); + }); + } + }); + } + if (!mb.sharedFilesLoadedOnce) { + mb.retrieveSharedFilesHistory(12).then(() => this.safeForceUpdate()).catch(dump); + } + let sharedNodesContainer = null; + if (mb.isRetrievingSharedFiles && !self.isLoadingMore) { + sharedNodesContainer = JSX_("div", { + className: "chat-dropdown empty-txt loading-initial" + }, JSX_("div", { + className: "loading-spinner light small" + }, JSX_("div", { + className: "main-loader" + }))); + } else if (!mb.haveMoreSharedFiles && !mb.sharedFiles.length) { + sharedNodesContainer = JSX_("div", { + className: "chat-dropdown empty-txt" + }, l[19985]); + } else { + const keys = clone(mb.sharedFiles.keys()).reverse(); + for (let i = startPos; i < endPos; i++) { + var message = mb.sharedFiles[keys[i]]; + if (!message) { + continue; + } + const nodes = message.getAttachmentMeta(); + nodes.forEach((node) => { + const imgId = `sharedFiles!${ node.ch}`; + const { + icon, + showThumbnail, + isPreviewable + } = M.getMediaProperties(node); + files.push(JSX_(SharedFileItem, { + message, + key: `${node.h}_${message.messageId}`, + isLoading: self.isLoadingMore, + node, + icon, + imgId, + showThumbnail, + isPreviewable, + chatRoom: room, + contact: Message.getContactForMessage(message) + })); + if (showThumbnail) { + self.allShownNodes.set(node.fa, node); + } + }); + } + sharedNodesContainer = JSX_("div", null, files); + } + contents = JSX_("div", { + className: "chat-dropdown content have-animation" + }, room.hasMessages() ? sharedNodesContainer : JSX_("div", { + className: "chat-dropdown empty-txt" + }, l[19985]), self.isLoadingMore ? JSX_("div", { + className: "loading-spinner light small" + }, JSX_("div", { + className: "main-loader" + })) : null, files.length > 0 ? JSX_("div", { + className: "chat-share-nav body" + }, prev, next, JSX_("div", { + className: "chat-share-nav pages" + }, (l[19988] ? l[19988] : "Page %1").replace("%1", currentPage + 1))) : null); + } + return JSX_("div", { + ref: this.domRef, + className: "chat-dropdown container" + }, JSX_("div", { + className: ` + chat-dropdown + header + ${this.props.expanded ? 'expanded' : ''} + `, + onClick: this.props.onToggle + }, JSX_("span", null, this.props.title), JSX_("i", { + className: "sprite-fm-mono icon-arrow-down" + })), JSX_("div", { + className: ` + chat-shared-files-container + ${this.isLoadingMore ? 'is-loading' : ''} + ` + }, contents)); + } +}, (0,applyDecoratedDescriptor.A)(_class.prototype, "eventuallyRenderThumbnails", [_dec], Object.getOwnPropertyDescriptor(_class.prototype, "eventuallyRenderThumbnails"), _class.prototype), _class); + +;// ./js/chat/ui/incomingSharesAccordionPanel.jsx + + +const SharedFolderItem = ({ + node, + isLoading +}) => { + return JSX_("div", { + key: node.h, + className: ` + chat-shared-block + incoming + ${isLoading ? 'is-loading' : ''} + `, + onClick: () => M.openFolder(node.h), + onDoubleClick: () => M.openFolder(node.h) + }, JSX_("div", { + className: "item-type-icon-90 icon-folder-incoming-90" + }), JSX_("div", { + className: "chat-shared-info" + }, JSX_("span", { + className: "txt" + }, node.name), JSX_("span", { + className: "txt small" + }, fm_contains(node.tf, node.td)))); +}; +class IncSharesAccordionPanel extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + } + UNSAFE_componentWillMount() { + this.hadLoaded = false; + } + getContactHandle() { + const self = this; + const room = self.props.chatRoom; + const contactHandle = room.getParticipantsExceptMe()[0]; + if (!contactHandle || room.type !== "private") { + return {}; + } + return contactHandle; + } + render() { + const self = this; + const room = self.props.chatRoom; + const contactHandle = self.getContactHandle(); + let contents = null; + if (this.props.expanded) { + if (!this.hadLoaded) { + this.hadLoaded = true; + self.isLoadingMore = true; + dbfetch.geta(Object.keys(M.c.shares || {}), new MegaPromise()).always(() => { + self.isLoadingMore = false; + Soon(() => { + if (self.isComponentEventuallyVisible()) { + self.safeForceUpdate(); + } + }, 5000); + }); + } + let incomingSharesContainer = null; + const sharedFolders = M.c[contactHandle] && Object.keys(M.c[contactHandle]) || []; + if (!self.isLoadingMore && (!sharedFolders || sharedFolders.length === 0)) { + incomingSharesContainer = JSX_("div", { + className: "chat-dropdown empty-txt" + }, l[19986]); + } else { + const haveMore = sharedFolders.length > 10; + const defSortFn = M.getSortByNameFn(); + sharedFolders.sort((a, b) => { + const nodeA = M.d[a]; + const nodeB = M.d[b]; + return defSortFn(nodeA, nodeB, -1); + }); + const renderNodes = []; + for (let i = 0; i < Math.min(sharedFolders.length, 10); i++) { + const nodeHandle = sharedFolders[i]; + const node = M.d[nodeHandle]; + if (!node) { + continue; + } + renderNodes.push(JSX_(SharedFolderItem, { + key: node.h, + isLoading: self.isLoadingMore, + node, + chatRoom: room + })); + } + incomingSharesContainer = JSX_("div", null, renderNodes, haveMore ? JSX_("div", { + className: "chat-share-nav body" + }, JSX_("div", { + className: "chat-share-nav show-all", + onClick () { + M.openFolder(contactHandle); + } + }, JSX_("span", { + className: "item-type-icon icon-folder-incoming-24" + }, JSX_("span", { + className: "item-type-icon icon-folder-incoming-24" + })), JSX_("span", { + className: "txt" + }, l[19797] ? l[19797] : "Show All"))) : null); + } + contents = JSX_("div", { + className: "chat-dropdown content have-animation" + }, incomingSharesContainer, self.isLoadingMore ? JSX_("div", { + className: "chat-dropdown empty-txt" + }, JSX_("div", { + className: "loading-spinner light small" + }, JSX_("div", { + className: "main-loader" + }))) : null); + } + return JSX_("div", { + ref: this.domRef, + className: "chat-dropdown container" + }, JSX_("div", { + className: ` + chat-dropdown + header + ${this.props.expanded ? 'expanded' : ''} + `, + onClick: this.props.onToggle + }, JSX_("span", null, this.props.title), JSX_("i", { + className: "sprite-fm-mono icon-arrow-down" + })), JSX_("div", { + className: ` + chat-shared-files-container + ${this.isLoadingMore ? 'is-loading' : ''} + ` + }, contents)); + } +} + +;// ./js/chat/ui/chatlinkDialog.jsx + + + + +class ChatlinkDialog extends REaCt().Component { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + this.state = { + link: l[5533], + newTopic: '' + }; + this.onClose = () => { + if (this.props.onClose) { + this.props.onClose(); + } + }; + } + retrieveChatLink(forced) { + const { + chatRoom + } = this.props; + if (is_chatlink) { + return this.setState({ + link: `${getBaseUrl()}/chat/${is_chatlink.ph}#${is_chatlink.key}` + }); + } + if (!chatRoom.topic && !forced) { + delete this.loading; + return; + } + this.loading = chatRoom.updatePublicHandle(false, true).always(() => { + this.loading = false; + if (this.domRef.current) { + this.setState({ + link: chatRoom.publicLink ? `${getBaseUrl()}/${chatRoom.publicLink}` : l[20660] + }); + } + }); + } + componentDidMount() { + this.retrieveChatLink(); + M.safeShowDialog(ChatlinkDialog.NAMESPACE, () => { + if (!this.domRef.current) { + throw new Error(`${ChatlinkDialog.NAMESPACE} dialog: component not mounted.`); + } + return $(`#${ChatlinkDialog.NAMESPACE}`); + }); + } + componentWillUnmount() { + if ($.dialog === ChatlinkDialog.NAMESPACE) { + closeDialog(); + } + } + render() { + const { + chatRoom + } = this.props; + const { + newTopic, + link + } = this.state; + const closeButton = this.loading ? null : JSX_("button", { + key: "close", + className: "mega-button negative links-button", + onClick: this.onClose + }, JSX_("span", null, l[148])); + const publicLinkDetails = chatRoom.isMeeting ? l.meeting_link_details : l[20644]; + return JSX_("div", { + ref: this.domRef + }, JSX_(modalDialogs.A.ModalDialog, (0,esm_extends.A)({}, this.state, { + id: ChatlinkDialog.NAMESPACE, + title: chatRoom.iAmOperator() && !chatRoom.topic ? chatRoom.isMeeting ? l.rename_meeting : l[9080] : '', + className: ` + chat-rename-dialog + export-chat-links-dialog + group-chat-link + ${chatRoom.topic ? '' : 'requires-topic'} + `, + onClose: this.onClose, + dialogName: "chat-link-dialog", + dialogType: chatRoom.iAmOperator() && !chatRoom.topic ? 'main' : 'graphic', + chatRoom, + popupDidMount: this.onPopupDidMount + }), chatRoom.iAmOperator() && !chatRoom.topic ? JSX_("section", { + className: "content" + }, JSX_("div", { + className: "content-block" + }, JSX_("div", { + className: "export-chat-ink-warning" + }, l[20617]), JSX_("div", { + className: "rename-input-bl", + style: { + margin: '10px auto 20px auto' + } + }, JSX_("input", { + type: "text", + name: "newTopic", + value: newTopic, + style: { + paddingLeft: 8 + }, + onChange: ev => this.setState({ + newTopic: ev.target.value + }), + onKeyPress: ev => ev.which === 13 && chatRoom.setRoomTopic(newTopic).then(() => this.retrieveChatLink(true)).catch(dump), + placeholder: l[20616], + maxLength: ChatRoom.TOPIC_MAX_LENGTH + })))) : JSX_(REaCt().Fragment, null, JSX_("header", null, chatRoom.isMeeting ? JSX_("div", { + className: "chat-topic-icon meeting-icon" + }, JSX_("i", { + className: "sprite-fm-mono icon-video-call-filled" + })) : JSX_("i", { + className: "sprite-fm-uni icon-chat-group" + }), JSX_("h2", { + id: "chat-link-dialog-title" + }, JSX_(utils.zT, null, chatRoom.getRoomTitle()))), JSX_("section", { + className: "content" + }, JSX_("div", { + className: "content-block" + }, JSX_("div", { + className: "chat-link-input" + }, JSX_("i", { + className: "sprite-fm-mono icon-link-small" + }), JSX_("input", { + type: "text", + readOnly: true, + value: this.loading ? l[5533] : !chatRoom.topic ? l[20660] : link + })), JSX_("div", { + className: "info" + }, chatRoom.publicLink || is_chatlink ? publicLinkDetails : null)))), JSX_("footer", null, JSX_("div", { + className: "footer-container" + }, chatRoom.iAmOperator() && chatRoom.publicLink && JSX_("button", { + key: "deleteLink", + className: ` + mega-button + links-button + ${this.loading ? 'disabled' : ''} + `, + onClick: () => { + chatRoom.updatePublicHandle(1); + this.onClose(); + } + }, JSX_("span", null, chatRoom.isMeeting ? l.meeting_link_delete : l[20487])), chatRoom.topic ? chatRoom.publicLink || is_chatlink ? JSX_("button", { + className: ` + mega-button + positive + copy-to-clipboard + ${this.loading ? 'disabled' : ''} + `, + onClick: () => { + copyToClipboard(link, l[7654]); + if (chatRoom.isMeeting) { + eventlog(500231); + } + } + }, JSX_("span", null, l[63])) : closeButton : chatRoom.iAmOperator() ? JSX_("button", { + key: "setTopic", + className: ` + mega-button + positive + links-button + ${newTopic && newTopic.trim() ? '' : 'disabled'} + `, + onClick: () => chatRoom.setRoomTopic(newTopic).then(() => this.retrieveChatLink(true)).catch(dump) + }, JSX_("span", null, l[20615])) : closeButton)))); + } +} +ChatlinkDialog.defaultProps = { + requiresUpdateOnResize: true, + disableCheckingVisibility: true +}; +ChatlinkDialog.NAMESPACE = 'chat-link-dialog'; +// EXTERNAL MODULE: ./js/chat/ui/historyPanel.jsx + 7 modules +const historyPanel = REQ_(5522); +// EXTERNAL MODULE: ./js/chat/ui/composedTextArea.jsx + 1 modules +const composedTextArea = REQ_(2558); +;// ./js/chat/ui/pushSettingsDialog.jsx + +let _PushSettingsDialog; + + +class PushSettingsDialog extends REaCt().Component { + constructor(props) { + super(props); + this.renderOptions = () => { + return Object.keys(PushSettingsDialog.options).map(key => { + key = parseInt(key, 10) || Infinity; + return JSX_("label", { + key, + className: "radio-txt" + }, PushSettingsDialog.options[key], JSX_("div", { + className: `custom-radio small green-active ${ this.state.pushSettingsValue === key ? "radioOn" : "radioOff"}` + }, JSX_("input", { + type: "radio", + name: "time-selector", + value: key, + checked: this.state.pushSettingsValue === key, + onChange: () => this.setState({ + pushSettingsValue: key + }) + }))); + }); + }; + this.state = { + pushSettingsValue: this.props.pushSettingsValue || Infinity + }; + } + render() { + return JSX_(modalDialogs.A.ModalDialog, (0,esm_extends.A)({}, this.state, { + name: "push-settings", + title: l.dnd_mute_title, + subtitle: this.props.room.isMeeting ? l.meeting_dnd_subtitle : l[22015], + className: "push-settings-dialog", + dialogName: "push-settings-chat-dialog", + dialogType: "tool", + onClose: this.props.onClose + }), JSX_("section", { + className: "content" + }, JSX_("div", { + className: "content-block" + }, JSX_("div", null, this.renderOptions()))), JSX_("footer", null, JSX_("div", { + className: "footer-container" + }, JSX_("button", { + className: "mega-button", + onClick: this.props.onClose + }, JSX_("span", null, l.msg_dlg_cancel)), JSX_("button", { + className: "mega-button positive", + onClick: () => this.props.onConfirm(this.state.pushSettingsValue) + }, JSX_("span", null, l[726]))))); + } +} +_PushSettingsDialog = PushSettingsDialog; +PushSettingsDialog.options = { + 30: l[22012], + 60: l[19048], + 360: l[22013], + 1440: l[22014], + Infinity: l[22011] +}; +PushSettingsDialog.default = _PushSettingsDialog.options[_PushSettingsDialog.options.length - 1]; +;// ./js/chat/ui/meetings/workflow/alert.jsx + + +const NAMESPACE = 'meetings-alert'; +class Alert extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + } + componentWillUnmount() { + let _this$props$onTransit, _this$props; + super.componentWillUnmount(); + (_this$props$onTransit = (_this$props = this.props).onTransition) == null || _this$props$onTransit.call(_this$props); + } + componentDidUpdate() { + let _this$props$onTransit2, _this$props2; + super.componentDidUpdate(); + (_this$props$onTransit2 = (_this$props2 = this.props).onTransition) == null || _this$props$onTransit2.call(_this$props2, this.domRef); + } + componentDidMount() { + let _this$props$onTransit3, _this$props3; + super.componentDidMount(); + (_this$props$onTransit3 = (_this$props3 = this.props).onTransition) == null || _this$props$onTransit3.call(_this$props3, this.domRef); + } + render() { + const { + type, + className, + content, + children, + offset, + onClose + } = this.props; + if (content || children) { + return JSX_("div", { + ref: this.domRef, + className: ` + ${NAMESPACE} + ${type ? `${NAMESPACE}-${type}` : ''} + ${className || ''} + `, + style: offset ? { + marginTop: `${offset}px` + } : undefined + }, JSX_("div", { + className: `${NAMESPACE}-content` + }, content || children), onClose && JSX_("span", { + className: `${NAMESPACE}-close`, + onClick: onClose + }, JSX_("i", { + className: "sprite-fm-mono icon-close-component" + }))); + } + return null; + } +} +Alert.TYPE = { + LIGHT: 'light', + NEUTRAL: 'neutral', + MEDIUM: 'medium', + HIGH: 'high', + ERROR: 'error' +}; +// EXTERNAL MODULE: ./js/chat/ui/meetings/schedule/helpers.jsx +const helpers = REQ_(6521); +// EXTERNAL MODULE: ./js/chat/ui/meetings/hostsObserver.jsx +const hostsObserver = REQ_(7677); +// EXTERNAL MODULE: ./js/chat/ui/inviteParticipantsPanel.jsx +const inviteParticipantsPanel = REQ_(8956); +;// ./js/chat/ui/chatOverlay.jsx + + + +const chatOverlay_NAMESPACE = 'chat-overlay'; +const ChatOverlays = { + PARTICIPANT_LIMIT: 'participants-limit' +}; +class ChatOverlay extends REaCt().Component { + constructor(...args) { + super(...args); + this.MegaLogo = () => JSX_("div", { + className: `${chatOverlay_NAMESPACE}-logo` + }, JSX_("i", { + className: `sprite-fm-illustration-wide ${mega.ui.isDarkTheme() ? 'mega-logo-dark' : 'img-mega-logo-light'}` + })); + } + renderParticipantsLimit() { + return JSX_(REaCt().Fragment, null, JSX_("div", { + className: `${chatOverlay_NAMESPACE}-head` + }, JSX_(this.MegaLogo, null), JSX_("h1", null, l.join_call_user_limit_title)), JSX_("div", { + className: `${chatOverlay_NAMESPACE}-body` + }, JSX_("p", null, l.call_join_user_limit_banner), JSX_(buttons.$, { + className: "mega-button positive", + onClick: () => { + let _this$props$onClose, _this$props; + (_this$props$onClose = (_this$props = this.props).onClose) == null || _this$props$onClose.call(_this$props); + } + }, l.call_link_user_limit_button))); + } + render() { + const { + overlayType + } = this.props; + let body = null; + if (overlayType === ChatOverlays.PARTICIPANT_LIMIT) { + body = this.renderParticipantsLimit(); + } + if (!body) { + if (d) { + console.error('Invalid ChatOverlay', overlayType); + } + return null; + } + return JSX_(utils.Ay.RenderTo, { + element: document.body + }, JSX_("div", { + className: `${chatOverlay_NAMESPACE} ${overlayType}` + }, body)); + } +} + +// EXTERNAL MODULE: ./js/chat/ui/meetings/workflow/utils.jsx +const workflow_utils = REQ_(2153); +;// ./js/chat/ui/meetings/schedule/occurrences.jsx + + + + + +class Occurrences extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + this.loadingMore = false; + this.state = { + editDialog: false, + occurrenceId: undefined + }; + } + loadOccurrences() { + if (!this.loadingMore) { + const { + scheduledMeeting, + occurrences + } = this.props; + const occurrenceItems = Object.values(occurrences || {}); + const lastOccurrence = occurrenceItems[occurrenceItems.length - 1]; + if (lastOccurrence) { + this.loadingMore = true; + scheduledMeeting.getOccurrences({ + from: lastOccurrence.start + }).catch(dump).finally(() => { + this.loadingMore = false; + }); + } + } + } + renderCancelConfirmation(occurrence) { + const { + scheduledMeeting, + chatRoom + } = this.props; + const nextOccurrences = Object.values(scheduledMeeting.occurrences).filter(o => o.isUpcoming); + if (nextOccurrences.length > 1) { + return msgDialog(`confirmation:!^${l.cancel_meeting_occurrence_button}!${l.schedule_cancel_abort}`, 'cancel-occurrence', l.schedule_cancel_occur_dlg_title, l.schedule_cancel_occur_dlg_text, cb => cb && occurrence.cancel(), 1); + } + return chatRoom.hasMessages(true) ? msgDialog(`confirmation:!^${l.cancel_meeting_button}!${l.schedule_cancel_abort}`, 'cancel-occurrence', l.schedule_cancel_all_dialog_title, l.schedule_cancel_all_dialog_move, cb => cb && megaChat.plugins.meetingsManager.cancelMeeting(scheduledMeeting, scheduledMeeting.chatId), 1) : msgDialog(`confirmation:!^${l.cancel_meeting_button}!${l.schedule_cancel_abort}`, 'cancel-occurrence', l.schedule_cancel_all_dialog_title, l.schedule_cancel_all_dialog_archive, cb => cb && megaChat.plugins.meetingsManager.cancelMeeting(scheduledMeeting, scheduledMeeting.chatId), 1); + } + renderLoading() { + return JSX_("div", { + className: "loading-sketch" + }, Array.from({ + length: 10 + }, (el, i) => { + return JSX_("div", { + key: i, + className: "chat-occurrence" + }, JSX_("div", { + className: "chat-occurrence-date" + }), JSX_("div", { + className: "chat-occurrence-content" + }, JSX_("div", { + className: "chat-occurrence-title" + }), JSX_("div", { + className: "chat-occurrence-time" + }))); + })); + } + renderOccurrences() { + const { + chatRoom, + occurrences, + occurrencesLoading, + scheduledMeeting + } = this.props; + if (occurrencesLoading) { + return this.renderLoading(); + } + if (occurrences && occurrences.length > 0) { + const sortedOccurrences = Object.values(occurrences).sort((a, b) => a.start - b.start); + return JSX_(REaCt().Fragment, null, sortedOccurrences.map(occurrence => occurrence.isUpcoming ? JSX_("div", { + key: occurrence.uid, + className: ` + chat-occurrence + ${occurrence.uid} + ` + }, JSX_("div", { + className: "chat-occurrence-date" + }, (0,helpers.cK)(occurrence.start) && JSX_("span", null, l.today_occurrence_label, " -"), (0,helpers.ef)(occurrence.start) && JSX_("span", null, l.tomorrow_occurrence_label, " -"), JSX_("span", null, time2date(occurrence.start / 1000, 19))), JSX_("div", { + className: "chat-occurrence-content" + }, JSX_("div", { + className: "chat-occurrence-title" + }, scheduledMeeting.title), JSX_("div", { + className: "chat-occurrence-time" + }, toLocaleTime(occurrence.start), " - \xA0", toLocaleTime(occurrence.end)), chatRoom.iAmOperator() && JSX_("div", { + className: "chat-occurrence-controls" + }, JSX_("div", { + className: "chat-occurrence-control simpletip", + "data-simpletip": l[1342], + "data-simpletipposition": "top", + "data-simpletipoffset": "5" + }, JSX_(buttons.$, { + icon: "sprite-fm-mono icon-rename", + onClick: () => { + megaChat.trigger(megaChat.plugins.meetingsManager.EVENTS.EDIT, occurrence); + } + })), JSX_("div", { + className: "chat-occurrence-control simpletip", + "data-simpletip": l[82], + "data-simpletipposition": "top", + "data-simpletipoffset": "5" + }, JSX_(buttons.$, { + icon: "sprite-fm-mono icon-bin", + onClick: () => this.renderCancelConfirmation(occurrence) + }))))) : null)); + } + return JSX_("span", null, l.no_occurrences_remain); + } + render() { + return JSX_("div", { + ref: this.domRef, + className: "chat-occurrences-list" + }, JSX_(perfectScrollbar.O, { + chatRoom: this.props.chatRoom, + ref: ref => { + this.contactsListScroll = ref; + }, + disableCheckingVisibility: true, + onUserScroll: ps => ps.isCloseToBottom(30) && this.loadOccurrences(), + isVisible: this.isCurrentlyActive, + options: { + suppressScrollX: true + } + }, JSX_("div", { + className: "chat-occurrences-list-inner" + }, this.renderOccurrences()))); + } +} +// EXTERNAL MODULE: ./js/chat/ui/fallback.jsx +const fallback = REQ_(3439); +// EXTERNAL MODULE: ./js/chat/ui/meetings/utils.jsx +const meetings_utils = REQ_(3901); +;// ./js/chat/ui/conversationpanel.jsx + + +let conversationpanel_dec, _dec2, conversationpanel_class; + + + + + + + + + + + + + + + + + + + + + + + + + + + +const Call = (0,external_React_.lazy)(() => REQ_.e( 987).then(REQ_.bind(REQ_, 8402))); +const Loading = (0,external_React_.lazy)(() => REQ_.e( 987).then(REQ_.bind(REQ_, 2914))); +const Join = (0,external_React_.lazy)(() => REQ_.e( 987).then(REQ_.bind(REQ_, 7128))); +const CloudBrowserDialog = (0,external_React_.lazy)(() => REQ_.e( 313).then(REQ_.bind(REQ_, 6961))); +const WaitingRoom = (0,external_React_.lazy)(() => REQ_.e( 752).then(REQ_.bind(REQ_, 2659))); +const ENABLE_GROUP_CALLING_FLAG = true; +const MAX_USERS_CHAT_PRIVATE = 100; +const ALERTS_BASE_OFFSET = 4; +const DISMISS_TRANSITIONS = { + NOT_SHOWN: 0, + SHOWN: 1, + DISMISSED: 2 +}; +class EndCallButton extends REaCt().Component { + constructor(...args) { + super(...args); + this.IS_MODERATOR = (0,meetings_utils.Cy)(this.props.chatRoom, u_handle); + this.LeaveButton = (0,hostsObserver.C)(({ + hasHost, + chatRoom, + confirmLeave, + onLeave + }) => { + return JSX_(ui_dropdowns.tJ, { + className: "link-button", + icon: "sprite-fm-mono icon-leave-call", + label: l.leave, + persistent: true, + onClick: () => { + const doLeave = () => hasHost(chatRoom.call ? chatRoom.call.peers.map(a => a.userHandle) : []) ? onLeave() : confirmLeave({ + title: l.assign_host_leave_call, + body: l.assign_host_leave_call_details, + cta: l.assign_host_button, + altCta: l.leave_anyway + }); + const { + recorderCid, + sfuClient + } = chatRoom.call; + return recorderCid && recorderCid === sfuClient.cid ? (0,meetings_utils.sX)(doLeave, () => sfuClient.recordingStop()) : doLeave(); + } + }); + }); + } + renderButton({ + label, + onClick, + children = null, + disabled + }) { + return JSX_(buttons.$, { + className: ` + link-button + light + red + dropdown-element + ${disabled ? 'disabled' : ''} + `, + icon: "small-icon colorized horizontal-red-handset", + label, + onClick: disabled ? null : onClick + }, children); + } + render() { + const { + chatRoom + } = this.props; + const { + type, + call + } = chatRoom; + if (call) { + const peers = call.peers && call.peers.length; + if (type === 'private') { + return this.renderButton({ + label: l[5884], + onClick: () => call.hangUp() + }); + } + if (this.IS_MODERATOR) { + const doEnd = () => chatRoom.endCallForAll(); + return this.renderButton({ + label: l[5884], + onClick: peers ? null : () => call.hangUp(), + children: peers && JSX_(ui_dropdowns.ms, { + className: "wide-dropdown light end-call-selector", + noArrow: "true", + vertOffset: 4, + horizOffset: 0 + }, JSX_(this.LeaveButton, { + chatRoom, + participants: chatRoom.getCallParticipants(), + onLeave: () => call.hangUp(), + onConfirmDenied: () => call.hangUp() + }), JSX_(ui_dropdowns.tJ, { + className: "link-button", + icon: "sprite-fm-mono icon-contacts", + label: l.end_for_all, + onClick: () => { + const { + recorderCid, + sfuClient + } = call; + return recorderCid && recorderCid === u_handle ? (0,meetings_utils._F)(doEnd, () => sfuClient.recordingStop()) : doEnd(); + } + })) + }); + } + return this.renderButton({ + label: peers ? l[5883] : l[5884], + onClick: () => call.hangUp() + }) + ; + } + if (chatRoom.havePendingGroupCall()) { + return this.IS_MODERATOR ? this.renderButton({ + label: l.end_call_for_all, + onClick: () => msgDialog('confirmation', null, l.end_call_for_all_title, l.end_call_for_all_text, cb => cb ? chatRoom.endCallForAll() : 0xDEAD), + disabled: !chatRoom.iAmInRoom() + }) : null; + } + return null; + } +} +const StartMeetingNotification = ({ + chatRoom, + offset, + onWaitingRoomJoin, + onStartCall +}) => { + if (chatRoom.call || !megaChat.hasSupportForCalls) { + return null; + } + return JSX_("div", { + className: "in-call-notif neutral start", + style: { + marginTop: offset + }, + onClick: () => { + eventlog(500288); + if (chatRoom.options.w && !chatRoom.iAmOperator()) { + return onWaitingRoomJoin(); + } + return onStartCall(meetings_utils.ZE.AUDIO); + } + }, JSX_("button", { + className: "mega-button positive small" + }, l.schedule_start_aot)); +}; +const JoinCallNotification = ({ + chatRoom, + offset, + rhpCollapsed +}) => { + if (chatRoom.call) { + return null; + } + if (!megaChat.hasSupportForCalls) { + return JSX_(Alert, { + className: ` + ${rhpCollapsed ? 'full-span' : ''} + ${offset === ALERTS_BASE_OFFSET ? 'single-alert' : ''} + unsupported-call-alert-progress + `, + offset: offset === ALERTS_BASE_OFFSET ? 0 : offset, + type: Alert.TYPE.MEDIUM, + content: l.active_call_not_supported + }); + } + if (chatRoom.callUserLimited && !chatRoom.canJoinLimitedCall()) { + return JSX_("div", { + className: "call-user-limit-banner", + style: { + marginTop: offset + } + }, l.call_join_user_limit_banner); + } + return JSX_("div", { + className: "in-call-notif neutral join", + style: { + marginTop: offset + } + }, JSX_("i", { + className: "sprite-fm-mono icon-phone" + }), JSX_(utils.P9, { + onClick: () => { + return (0,meetings_utils.dQ)(true, chatRoom).then(() => chatRoom.joinCall()).catch(ex => d && console.warn('Already in a call.', ex)); + } + }, (l[20460] || 'There is an active group call. [A]Join[/A]').replace('[A]', ''))); +}; +const allContactsInChat = participants => { + const currentContacts = M.u.keys(); + for (let i = 0; i < currentContacts.length; i++) { + const k = currentContacts[i]; + if (M.u[k].c === 1 && !participants.includes(k)) { + return false; + } + } + return true; +}; +const excludedParticipants = room => { + const excParticipants = room.type === "group" || room.type === "public" ? room.members && Object.keys(room.members).length > 0 ? Object.keys(room.members) : room.getParticipants() : room.getParticipants(); + if (excParticipants.includes(u_handle)) { + array.remove(excParticipants, u_handle, false); + } + return excParticipants; +}; +class ConversationRightArea extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + this.state = { + contactPickerDialog: false, + inviteDialog: false + }; + this.LeaveButton = (0,hostsObserver.C)(({ + chatRoom, + hasHost, + confirmLeave, + onLeave + }) => { + const isDisabled = chatRoom.call || is_chatlink || !chatRoom.iAmInRoom(); + const participants = chatRoom.getParticipantsExceptMe(); + return JSX_("div", { + className: ` + link-button + light + ${isDisabled ? 'disabled' : ''} + `, + onClick: isDisabled ? null : () => hasHost(participants) || !participants.length ? onLeave() : confirmLeave({ + title: chatRoom.isMeeting ? l.assign_host_to_leave : l.assign_host_to_leave_group, + body: chatRoom.isMeeting ? l.assign_host_to_details : l.assign_host_to_details_group, + cta: l.assign_host_button + }) + }, JSX_("i", { + className: "sprite-fm-mono icon-disabled-filled" + }), JSX_("span", null, chatRoom.isMeeting ? l.meeting_leave : l[8633])); + }); + this.OptionsButton = ({ + icon, + label, + secondLabel, + toggled, + disabled, + onClick + }) => { + const { + chatRoom + } = this.props; + const isDisabled = !chatRoom.iAmOperator() || disabled; + return JSX_(buttons.$, { + className: ` + link-button + light + room-settings-button + `, + disabled: isDisabled, + icon: ` + sprite-fm-mono + ${icon} + `, + label, + secondLabel, + secondLabelClass: "label--green", + toggle: { + enabled: toggled, + onClick: isDisabled ? null : onClick + }, + onClick: isDisabled ? null : onClick + }); + }; + this.handleCancelMeeting = () => { + const { + chatRoom + } = this.props; + const { + scheduledMeeting, + chatId + } = chatRoom || {}; + if (scheduledMeeting) { + const { + isRecurring, + title + } = scheduledMeeting; + const doConfirm = res => { + if (res) { + megaChat.plugins.meetingsManager.cancelMeeting(scheduledMeeting, chatId); + delay('chat-event-sm-cancel', () => eventlog(99925)); + } + }; + if (isRecurring) { + return chatRoom.hasMessages(true) ? msgDialog(`confirmation:!^${l.cancel_meeting_button}!${l.schedule_cancel_abort}`, null, l.schedule_cancel_dialog_title.replace('%s', megaChat.html(title)), l.schedule_cancel_dialog_move_recurring, doConfirm, 1) : msgDialog(`confirmation:!^${l.schedule_cancel_dialog_confirm}!${l.schedule_cancel_abort}`, null, l.schedule_cancel_dialog_title.replace('%s', megaChat.html(title)), l.schedule_cancel_dialog_archive_recurring, doConfirm, 1); + } + return chatRoom.hasMessages(true) ? msgDialog(`confirmation:!^${l.cancel_meeting_button}!${l.schedule_cancel_abort}`, null, l.schedule_cancel_dialog_title.replace('%s', megaChat.html(title)), l.schedule_cancel_dialog_move_single, doConfirm, 1) : msgDialog(`confirmation:!^${l.schedule_cancel_dialog_confirm}!${l.schedule_cancel_abort}`, null, l.schedule_cancel_dialog_title.replace('%s', megaChat.html(title)), l.schedule_cancel_dialog_archive_single, doConfirm, 1); + } + }; + } + customIsEventuallyVisible() { + return this.props.chatRoom.isCurrentlyActive; + } + setRetention(chatRoom, retentionTime) { + chatRoom.setRetention(retentionTime); + $(document).trigger('closeDropdowns'); + } + renderOptionsBanner() { + const { + chatRoom + } = this.props; + return !!chatRoom.options[MCO_FLAGS.WAITING_ROOM] && !!chatRoom.options[MCO_FLAGS.OPEN_INVITE] ? JSX_("div", { + className: "room-settings-banner" + }, JSX_("i", { + className: "sprite-fm-mono icon-info" + }), JSX_(utils.P9, null, l.waiting_room_invite.replace('[A]', ``).replace('[/A]', ''))) : null; + } + handleAddParticipants() { + if (Object.values(M.u.toJS()).some(u => u.c === 1)) { + if (allContactsInChat(excludedParticipants(this.props.chatRoom))) { + return msgDialog(`confirmationa:!^${l[8726]}!${l.msg_dlg_cancel}`, null, `${l.all_contacts_added}`, `${l.all_contacts_added_to_chat}`, res => { + if (res) { + contactAddDialog(null, false); + } + }, 1); + } + return this.setState({ + contactPickerDialog: true + }); + } + msgDialog(`confirmationa:!^${l[8726]}!${l.msg_dlg_cancel}`, null, `${l.no_contacts}`, `${l.no_contacts_text}`, resp => { + if (resp) { + contactAddDialog(null, false); + } + }, 1); + } + renderPushSettingsButton() { + const { + pushSettingsValue, + chatRoom, + onPushSettingsToggled, + onPushSettingsClicked + } = this.props; + const icon = pushSettingsValue || pushSettingsValue === 0 ? 'icon-notification-off-filled' : 'icon-notification-filled'; + return JSX_("div", { + className: "push-settings" + }, JSX_("div", { + className: "chat-button-separator" + }), JSX_(buttons.$, { + className: ` + link-button + light + push-settings-button + ${chatRoom.isReadOnly() ? 'disabled' : ''} + `, + icon: ` + sprite-fm-mono + ${icon} + `, + label: chatRoom.isMeeting ? l.meeting_notifications : l[16709], + secondLabel: (() => { + if (pushSettingsValue !== null && pushSettingsValue !== undefined) { + return pushSettingsValue === 0 ? PushSettingsDialog.options[Infinity] : l[23539].replace('%s', toLocaleTime(pushSettingsValue)); + } + })(), + secondLabelClass: "label--green", + toggle: chatRoom.isReadOnly() ? null : { + enabled: !pushSettingsValue && pushSettingsValue !== 0, + onClick: () => !pushSettingsValue && pushSettingsValue !== 0 ? onPushSettingsClicked() : onPushSettingsToggled() + }, + onClick: () => chatRoom.isReadOnly() ? null : onPushSettingsClicked() + }), JSX_("div", { + className: "chat-button-separator" + })); + } + componentDidMount() { + super.componentDidMount(); + megaChat.rebind(`${megaChat.plugins.meetingsManager.EVENTS.OCCURRENCES_UPDATE}.${this.getUniqueId()}`, () => { + if (this.isMounted()) { + this.safeForceUpdate(); + } + }); + megaChat.rebind(`onPrepareIncomingCallDialog.${this.getUniqueId()}`, () => { + if (this.isMounted() && this.state.inviteDialog) { + this.setState({ + inviteDialog: false + }); + } + }); + } + render() { + let _room$messagesBuff, _room$messagesBuff2, _room$messagesBuff3, _room$messagesBuff4, _room$messagesBuff5; + const self = this; + const { + chatRoom: room, + onStartCall, + occurrencesLoading, + onShowScheduledDescription + } = self.props; + if (!room || !room.roomId) { + return null; + } + if (!room.isCurrentlyActive && !self._wasAppendedEvenOnce) { + return null; + } + self._wasAppendedEvenOnce = true; + const startCallDisabled = isStartCallDisabled(room) || room.iAmWaitingRoomPeer(); + let startAudioCallButton; + let startVideoCallButton; + const isInCall = !!room.call; + if (isInCall) { + startAudioCallButton = startVideoCallButton = null; + } + if (room.type === "group" || room.type === "public") { + if (room.getCallParticipants().length > 0 && !isInCall) { + startAudioCallButton = startVideoCallButton = null; + } + } + if (startAudioCallButton !== null) { + startAudioCallButton = JSX_("div", { + "data-simpletip": l.unsupported_browser_audio, + "data-simpletipposition": "top", + "data-simpletipoffset": "7", + className: ` + link-button light + ${megaChat.hasSupportForCalls ? '' : 'simpletip'} + ${startCallDisabled ? 'disabled' : ''} + `, + onClick: () => onStartCall(meetings_utils.ZE.AUDIO) + }, JSX_("i", { + className: "sprite-fm-mono icon-phone" + }), JSX_("span", null, l[5896])); + } + if (startVideoCallButton !== null) { + startVideoCallButton = JSX_("div", { + "data-simpletip": l.unsupported_browser_video, + "data-simpletipposition": "top", + "data-simpletipoffset": "7", + className: ` + link-button light + ${megaChat.hasSupportForCalls ? '' : 'simpletip'} + ${startCallDisabled ? 'disabled' : ''} + `, + onClick: () => onStartCall(meetings_utils.ZE.VIDEO) + }, JSX_("i", { + className: "sprite-fm-mono icon-video-call-filled" + }), JSX_("span", null, l[5897])); + } + const AVseperator = JSX_("div", { + className: "chat-button-separator" + }); + let isReadOnlyElement = null; + if (room.isReadOnly()) { + isReadOnlyElement = JSX_("center", { + className: "center", + style: { + margin: "6px" + } + }, l.read_only_chat); + } + const exParticipants = excludedParticipants(room); + let dontShowTruncateButton = false; + if (!room.iAmOperator() || room.isReadOnly() || ((_room$messagesBuff = room.messagesBuff) == null ? void 0 : _room$messagesBuff.messages.length) === 0 || ((_room$messagesBuff2 = room.messagesBuff) == null ? void 0 : _room$messagesBuff2.messages.length) === 1 && ((_room$messagesBuff3 = room.messagesBuff) == null ? void 0 : _room$messagesBuff3.messages.getItem(0).dialogType) === "truncated") { + dontShowTruncateButton = true; + } + const renameButtonClass = ` + link-button + light + ${(0,meetings_utils.P)() || room.isReadOnly() || !room.iAmOperator() ? 'disabled' : ''} + `; + const getChatLinkClass = ` + link-button + light + ${(0,meetings_utils.P)() || room.isReadOnly() ? 'disabled' : ''} + `; + let participantsList = null; + if (room.type === "group" || room.type === "public") { + participantsList = JSX_("div", null, isReadOnlyElement, JSX_(buttons.$, { + className: "mega-button action invite-dialog-btn", + icon: "sprite-fm-mono icon-user-plus-thin-outline", + label: l[8726], + disabled: (0,meetings_utils.P)() || room.isReadOnly() || !room.iAmOperator() && !room.publicLink && !room.options[MCO_FLAGS.OPEN_INVITE], + onClick: () => { + delay('chat-event-inv-rhp', () => eventlog(99963)); + if (room.type === 'group') { + return this.handleAddParticipants(); + } + loadingDialog.show('fetchchatlink'); + room.updatePublicHandle(false, false, true).catch(dump).always(() => { + loadingDialog.hide('fetchchatlink'); + if (!this.isMounted()) { + return; + } + if (!room.iAmOperator() && room.options[MCO_FLAGS.OPEN_INVITE] && !room.publicLink) { + this.handleAddParticipants(); + } else if (room.type === 'public' && !room.topic) { + this.handleAddParticipants(); + } else { + this.setState({ + inviteDialog: true + }); + } + }); + } + }), JSX_(ParticipantsList, { + ref (r) { + self.participantsListRef = r; + }, + chatRoom: room, + members: room.members, + isCurrentlyActive: room.isCurrentlyActive + })); + } + const addParticipantBtn = room.type === 'private' && JSX_(buttons.$, { + className: "link-button light", + icon: "sprite-fm-mono icon-add-small", + label: l[8007], + disabled: (0,meetings_utils.P)() || room.isReadOnly() || !(room.iAmOperator() || room.type !== 'private' && room.options[MCO_FLAGS.OPEN_INVITE]), + onClick: () => Object.values(M.u.toJS()).some(u => u.c === 1) ? !allContactsInChat(exParticipants) ? this.setState({ + contactPickerDialog: true + }) : msgDialog(`confirmationa:!^${l[8726]}!${l.msg_dlg_cancel}`, null, `${l.all_contacts_added}`, `${l.all_contacts_added_to_chat}`, res => { + if (res) { + contactAddDialog(null, false); + } + }, 1) : msgDialog(`confirmationa:!^${l[8726]}!${l.msg_dlg_cancel}`, null, `${l.no_contacts}`, `${l.no_contacts_text}`, resp => { + if (resp) { + contactAddDialog(null, false); + } + }, 1) + }); + const waitingRoomButton = { + icon: 'icon-clock-user-thin-solid', + label: l.waiting_room, + secondLabel: l.waiting_room_info, + toggled: room.options[MCO_FLAGS.WAITING_ROOM], + disabled: room.havePendingCall(), + onClick: () => { + room.toggleWaitingRoom(); + delay('chat-event-wr-create-button', () => eventlog(99937)); + } + }; + const openInviteButton = { + icon: 'icon-user-filled', + label: room.isMeeting ? l.meeting_open_invite_label : l.chat_open_invite_label, + secondLabel: l.open_invite_desc, + toggled: room.options[MCO_FLAGS.OPEN_INVITE], + onClick: () => { + room.toggleOpenInvite(); + if (room.scheduledMeeting) { + delay('chat-event-sm-allow-non-hosts', () => eventlog(99928)); + } + } + }; + const retentionTime = room.retentionTime ? secondsToDays(room.retentionTime) : 0; + const ICON_ACTIVE = JSX_("i", { + className: "sprite-fm-mono icon-check" + }); + const retentionHistoryBtn = JSX_(buttons.$, { + className: "link-button light history-retention-btn", + icon: "sprite-fm-mono icon-recents-filled", + label: l[23436], + disabled: !room.iAmOperator() || room.isReadOnly() || (0,meetings_utils.P)(), + secondLabel: room.getRetentionLabel(), + secondLabelClass: "label--red", + chatRoom: room + }, room.iAmOperator() ? JSX_(ui_dropdowns.ms, { + className: "retention-history-menu light", + noArrow: "false", + vertOffset: -53, + horizOffset: -205 + }, JSX_("div", { + className: "retention-history-menu__list" + }, JSX_("div", { + className: "dropdown-item link-button retention-history-menu__list__elem", + onClick: () => this.setRetention(room, 0) + }, JSX_("span", null, l.disabled_chat_history_cleaning_status), retentionTime === 0 && ICON_ACTIVE), JSX_("div", { + className: "dropdown-item link-button retention-history-menu__list__elem", + onClick: () => this.setRetention(room, daysToSeconds(1)) + }, JSX_("span", null, l[23437]), retentionTime === 1 && ICON_ACTIVE), JSX_("div", { + className: "dropdown-item link-button retention-history-menu__list__elem", + onClick: () => this.setRetention(room, daysToSeconds(7)) + }, JSX_("span", null, l[23438]), retentionTime === 7 && ICON_ACTIVE), JSX_("div", { + className: "dropdown-item link-button retention-history-menu__list__elem", + onClick: () => this.setRetention(room, daysToSeconds(30)) + }, JSX_("span", null, l[23439]), retentionTime === 30 && ICON_ACTIVE), JSX_("div", { + className: "dropdown-item link-button retention-history-menu__list__elem", + onClick: () => { + $(document).trigger('closeDropdowns'); + self.props.onHistoryRetentionConfig(); + } + }, JSX_("span", null, l[23440]), [0, 1, 7, 30].indexOf(retentionTime) === -1 && ICON_ACTIVE))) : null); + const MEMBERS_LIMITED = Object.keys(room.members).length > MAX_USERS_CHAT_PRIVATE; + const { + scheduledMeeting, + isMeeting + } = room; + const { + isRecurring, + isUpcoming, + occurrences + } = scheduledMeeting || {}; + let archiveText = room.isMeeting ? l.archive_meeting_btn : l.archive_chat_btn; + if (room.isArchived()) { + archiveText = room.isMeeting ? l.unarchive_meeting_btn : l[19065]; + } + return JSX_("div", { + ref: this.domRef, + className: "chat-right-area" + }, JSX_(perfectScrollbar.O, { + className: "chat-right-area conversation-details-scroll", + options: { + 'suppressScrollX': true + }, + ref: ref => { + this.rightScroll = ref; + }, + triggerGlobalResize: true, + isVisible: room.isCurrentlyActive, + chatRoom: room + }, JSX_("div", { + className: "chat-right-pad" + }, JSX_(Accordion, (0,esm_extends.A)({}, this.state, { + chatRoom: room, + onToggle: SoonFc(20, () => { + if (this.rightScroll) { + this.rightScroll.reinitialise(); + } + if (this.participantsListRef) { + let _this$participantsLis, _this$participantsLis2; + (_this$participantsLis = (_this$participantsLis2 = this.participantsListRef).safeForceUpdate) == null || _this$participantsLis.call(_this$participantsLis2); + } + }), + expandedPanel: { + participants: false, + options: false, + occurrences: isMeeting && scheduledMeeting && isRecurring + } + }), participantsList ? JSX_(AccordionPanel, { + className: "small-pad", + title: room.isMeeting ? l.meeting_participants : l.chat_participants, + chatRoom: room, + key: "participants" + }, participantsList) : null, room.type === 'public' && room.observers > 0 && !room.options.w ? JSX_("div", { + className: "accordion-text observers" + }, l[20466], JSX_("span", { + className: "observers-count" + }, JSX_("i", { + className: "sprite-fm-mono icon-eye-reveal" + }), room.observers)) : JSX_("div", null), isRecurring && isUpcoming && scheduledMeeting.occurrences.some(o => o.isUpcoming) && JSX_(AccordionPanel, { + key: "occurrences", + className: "chat-occurrences-panel", + accordionClass: "chatroom-occurrences-panel", + title: l.occurrences_heading, + chatRoom: room, + scheduledMeeting, + occurrences + }, JSX_(Occurrences, { + chatRoom: room, + scheduledMeeting, + occurrences, + occurrencesLoading + })), JSX_(AccordionPanel, { + key: "options", + className: "have-animation buttons", + accordionClass: "chatroom-options-panel", + title: l[7537], + chatRoom: room, + sfuClient: window.sfuClient + }, JSX_(REaCt().Fragment, null, room.isNote ? null : JSX_(REaCt().Fragment, null, addParticipantBtn, startAudioCallButton, startVideoCallButton, JSX_(EndCallButton, { + call: room.havePendingGroupCall() || room.haveActiveCall(), + chatRoom: room + }), scheduledMeeting && JSX_("div", { + className: ` + link-button light + schedule-view-desc + ${room.isReadOnly() || !scheduledMeeting.description ? 'disabled' : ''} + `, + onClick: () => { + if (!room.isReadOnly() && scheduledMeeting.description) { + onShowScheduledDescription(); + } + } + }, JSX_("i", { + className: "sprite-fm-mono icon-description" + }), JSX_("span", null, l.schedule_view_desc)), (room.type === 'group' || room.type === 'public') && !scheduledMeeting ? JSX_("div", { + className: renameButtonClass, + onClick: e => { + if ($(e.target).closest('.disabled').length > 0) { + return false; + } + if (this.props.onRenameClicked) { + this.props.onRenameClicked(); + } + } + }, JSX_("i", { + className: "sprite-fm-mono icon-rename" + }), JSX_("span", null, room.isMeeting ? l.rename_meeting : l[9080])) : null, scheduledMeeting ? JSX_("div", { + className: ` + link-button + light + ${room.iAmOperator() ? '' : 'disabled'} + `, + onClick: () => room.iAmOperator() ? megaChat.trigger(megaChat.plugins.meetingsManager.EVENTS.EDIT, room) : null + }, JSX_("i", { + className: "sprite-fm-mono icon-rename" + }), scheduledMeeting.isRecurring ? JSX_("span", null, l.edit_meeting_series_button) : JSX_("span", null, l.edit_meeting_button)) : null, room.type === 'public' && !room.isMeeting ? JSX_("div", { + className: getChatLinkClass, + onClick: e => { + if ($(e.target).closest('.disabled').length > 0) { + return false; + } + this.props.onGetManageChatLinkClicked(); + } + }, JSX_("i", { + className: "sprite-fm-mono icon-link-filled" + }), JSX_("span", null, l[20481])) : null, scheduledMeeting ? JSX_("div", { + className: ` + link-button + light + ${room.iAmOperator() && !scheduledMeeting.canceled ? '' : 'disabled'} + `, + onClick: () => { + if (room.iAmOperator() && !scheduledMeeting.canceled) { + this.handleCancelMeeting(); + } + } + }, JSX_("i", { + className: "sprite-fm-mono icon-bin-filled" + }), scheduledMeeting.isRecurring ? JSX_("span", null, l.cancel_meeting_series_button) : JSX_("span", null, l.cancel_meeting_button)) : null, !room.membersSetFromApi.members.hasOwnProperty(u_handle) && room.type === 'public' && !is_chatlink && room.publicChatHandle && room.publicChatKey ? JSX_("div", { + className: "link-button light", + onClick: e => { + if ($(e.target).closest('.disabled').length > 0) { + return false; + } + this.props.onJoinViaPublicLinkClicked(); + } + }, JSX_("i", { + className: "sprite-fm-mono icon-rename" + }), JSX_("span", null, l[20597])) : null, scheduledMeeting ? null : JSX_(REaCt().Fragment, null, AVseperator, JSX_(buttons.$, { + className: "link-button light dropdown-element", + icon: "sprite-fm-mono icon-upload-filled", + label: l[23753], + disabled: room.isReadOnly() + }, JSX_(ui_dropdowns.ms, { + className: "wide-dropdown send-files-selector light", + noArrow: "true", + vertOffset: 4, + onClick: () => false + }, JSX_("div", { + className: "dropdown info-txt" + }, l[23753] || 'Send...'), JSX_(ui_dropdowns.tJ, { + className: "link-button", + icon: "sprite-fm-mono icon-cloud-drive", + label: l[19794] || 'My Cloud Drive', + disabled: mega.paywall, + onClick: () => { + this.props.onAttachFromCloudClicked(); + } + }), JSX_(ui_dropdowns.tJ, { + className: "link-button", + icon: "sprite-fm-mono icon-session-history", + label: l[19795] || 'My computer', + disabled: mega.paywall, + onClick: () => { + this.props.onAttachFromComputerClicked(); + } + })))), this.renderPushSettingsButton()), room.type === 'private' ? null : JSX_(REaCt().Fragment, null, room.scheduledMeeting && this.OptionsButton(waitingRoomButton), this.OptionsButton(openInviteButton), this.renderOptionsBanner(), AVseperator), JSX_(buttons.$, { + className: "link-button light export-chat-button", + disabled: ((_room$messagesBuff4 = room.messagesBuff) == null ? void 0 : _room$messagesBuff4.messages.length) === 0 || room.exportIo, + onClick: () => { + room.exportToFile(); + } + }, JSX_("i", { + className: "sprite-fm-mono icon-export-chat-filled" + }), JSX_("span", null, room.isMeeting ? l.export_meeting_rhp : l.export_chat_rhp)), JSX_(buttons.$, { + className: "link-button light clear-history-button", + disabled: dontShowTruncateButton || !room.members.hasOwnProperty(u_handle), + onClick: () => { + if (this.props.onTruncateClicked) { + this.props.onTruncateClicked(); + } + } + }, JSX_("i", { + className: "sprite-fm-mono icon-remove" + }), JSX_("span", { + className: "accordion-clear-history-text" + }, room.isMeeting ? l.meeting_clear_hist : l[8871])), retentionHistoryBtn, room.iAmOperator() && room.type === 'public' && !scheduledMeeting ? JSX_("div", { + className: "chat-enable-key-rotation-paragraph" + }, AVseperator, JSX_("div", { + className: ` + link-button + light + ${MEMBERS_LIMITED ? 'disabled' : ''} + `, + onClick: e => { + if (MEMBERS_LIMITED || $(e.target).closest('.disabled').length > 0) { + return false; + } + this.props.onMakePrivateClicked(); + } + }, JSX_("i", { + className: "sprite-fm-mono icon-key" + }), JSX_("span", null, l[20623])), JSX_("p", null, JSX_("span", null, l[20454]))) : null, AVseperator, JSX_("div", { + className: ` + link-button + light + ${(room.members.hasOwnProperty(u_handle) || room.state === ChatRoom.STATE.LEFT) && !is_chatlink ? '' : 'disabled'} + `, + onClick: e => { + if ($(e.target).closest('.disabled').length > 0) { + return false; + } + if (room.isArchived()) { + if (this.props.onUnarchiveClicked) { + this.props.onUnarchiveClicked(); + } + } else if (this.props.onArchiveClicked) { + this.props.onArchiveClicked(); + } + } + }, JSX_("i", { + className: ` + sprite-fm-mono + ${room.isArchived() ? 'icon-unarchive' : 'icon-archive'} + ` + }), JSX_("span", null, archiveText)), room.type === 'private' ? null : JSX_(this.LeaveButton, { + chatRoom: room, + participants: room.getParticipantsExceptMe(), + onLeave: () => room.leave(true) + }))), JSX_(SharedFilesAccordionPanel, { + key: "sharedFiles", + title: l[19796] || 'Shared Files', + chatRoom: room, + sharedFiles: (_room$messagesBuff5 = room.messagesBuff) == null ? void 0 : _room$messagesBuff5.sharedFiles + }), room.type === 'private' && !room.isNote ? JSX_(IncSharesAccordionPanel, { + key: "incomingShares", + title: l[5542], + chatRoom: room + }) : null))), this.state.contactPickerDialog && JSX_(ui_contacts.hm, { + exclude: exParticipants, + megaChat: room.megaChat, + multiple: true, + className: "popup add-participant-selector", + singleSelectedButtonLabel: room.isMeeting ? l.meeting_add_participant : l[8869], + multipleSelectedButtonLabel: room.isMeeting ? l.meeting_add_participant : l[8869], + nothingSelectedButtonLabel: l[8870], + inviteWarningLabel: room.haveActiveCall(), + chatRoom: room, + onSelectDone: selected => { + this.props.onAddParticipantSelected(selected); + this.setState({ + contactPickerDialog: false + }); + }, + onClose: () => this.setState({ + contactPickerDialog: false + }), + selectFooter: true + }), this.state.inviteDialog && JSX_(modalDialogs.A.ModalDialog, { + onClose: () => { + this.setState({ + inviteDialog: false + }); + }, + dialogName: "chat-link-dialog", + chatRoom: room + }, JSX_(inviteParticipantsPanel.Q, { + chatRoom: room, + onAddParticipants: () => { + this.setState({ + inviteDialog: false + }, () => this.handleAddParticipants()); + } + }))); + } +} +ConversationRightArea.defaultProps = { + 'requiresUpdateOnResize': true +}; +const ConversationPanel = (conversationpanel_dec = utils.Ay.SoonFcWrap(360), _dec2 = (0,mixins.N9)(0.7, 9), conversationpanel_class = class ConversationPanel extends mixins.w9 { + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.messagesBlockRef = REaCt().createRef(); + this.$container = undefined; + this.$messages = undefined; + this.selectedNodes = []; + this.state = { + startCallPopupIsActive: false, + localVideoIsMinimized: false, + isFullscreenModeEnabled: false, + mouseOverDuringCall: false, + attachCloudDialog: false, + sendContactDialog: false, + confirmDeleteDialog: false, + pasteImageConfirmDialog: false, + nonLoggedInJoinChatDialog: false, + pushSettingsDialog: false, + pushSettingsValue: null, + messageToBeDeleted: null, + callMinimized: false, + editing: false, + showHistoryRetentionDialog: false, + setNonLoggedInJoinChatDlgTrue: null, + hasInvalidKeys: null, + invalidKeysBanner: null, + descriptionDialog: false, + occurrencesLoading: false, + waitingRoom: false, + callUserLimit: false, + historyTimeOutBanner: DISMISS_TRANSITIONS.NOT_SHOWN, + renameDialog: false, + renameDialogValue: undefined, + typingAreaText: '' + }; + this.RenameDialog = () => { + const { + chatRoom + } = this.props; + const { + renameDialogValue + } = this.state; + const isDisabled = renameDialogValue === chatRoom.getRoomTitle() || !$.trim(renameDialogValue).length; + const onSubmit = () => chatRoom.setRoomTopic(renameDialogValue).then(() => this.setState({ + renameDialog: false, + renameDialogValue: undefined + })).catch(dump); + return JSX_(modalDialogs.A.ModalDialog, { + chatRoom, + title: chatRoom.isMeeting ? l.rename_meeting : l[9080], + name: "rename-group", + className: "chat-rename-dialog dialog-template-main", + onClose: () => this.setState({ + renameDialog: false, + renameDialogValue: undefined + }), + buttons: [{ + label: l.msg_dlg_cancel, + onClick: () => this.setState({ + renameDialog: false, + renameDialogValue: undefined + }) + }, { + label: l[61], + className: ` + positive + ${isDisabled ? 'disabled' : ''} + `, + onClick: isDisabled ? null : onSubmit + }] + }, JSX_("section", { + className: "content" + }, JSX_("div", { + className: "content-block" + }, JSX_("div", { + className: "dialog secondary-header" + }, JSX_("div", { + className: "rename-input-bl" + }, JSX_("input", { + type: "text", + name: "newTopic", + className: "chat-rename-group-dialog", + value: renameDialogValue === undefined ? chatRoom.getRoomTitle() : renameDialogValue, + maxLength: ChatRoom.TOPIC_MAX_LENGTH, + onChange: ev => this.setState({ + renameDialogValue: ev.target.value.substr(0, 30) + }), + onKeyUp: ev => isDisabled ? null : ev.which === 13 && onSubmit() + })))))); + }; + this.SelectContactDialog = () => { + const { + chatRoom + } = this.props; + const excludedContacts = chatRoom.getParticipantsExceptMe().filter(userHandle => userHandle in M.u); + return JSX_(modalDialogs.A.SelectContactDialog, { + chatRoom, + exclude: excludedContacts, + onSelectClicked: selected => this.setState({ + sendContactDialog: false + }, () => chatRoom.attachContacts(selected)), + onClose: () => this.setState({ + sendContactDialog: false + }) + }); + }; + this.DescriptionDialog = () => { + const { + chatRoom + } = this.props; + const dialogName = 'scheduled-description-dialog'; + return JSX_(modalDialogs.A.ModalDialog, { + className: "scheduled-description-dialog", + meeting: chatRoom.scheduledMeeting, + popupDidMount: () => M.safeShowDialog(dialogName, () => $(`.${dialogName}`)), + popupWillUnmount: () => $.dialog === dialogName && closeDialog(), + onClose: () => this.setState({ + descriptionDialog: false + }) + }, JSX_("header", null, JSX_("h3", null, l.schedule_desc_dlg_title)), JSX_("section", { + className: "content" + }, JSX_(perfectScrollbar.O, { + className: "description-scroller" + }, JSX_(utils.P9, { + content: megaChat.html(chatRoom.scheduledMeeting.description).replace(/\n/g, '
') || l.schedule_no_desc + })))); + }; + this.PushSettingsDialog = () => { + const { + chatRoom + } = this.props; + const { + pushSettingsValue + } = this.state; + const state = { + pushSettingsDialog: false, + pushSettingsValue: null + }; + return JSX_(PushSettingsDialog, { + room: chatRoom, + pushSettingsValue, + onClose: () => this.setState({ + ...state, + pushSettingsValue + }, () => $.dialog === 'push-settings-dialog' && closeDialog()), + onConfirm: pushSettingsValue => this.setState({ + ...state, + pushSettingsValue + }, () => pushNotificationSettings.setDnd(chatRoom.chatId, pushSettingsValue === Infinity ? 0 : unixtime() + pushSettingsValue * 60)) + }); + }; + this.updateTypingAreaText = value => { + this.setState({ + typingAreaText: value + }); + }; + const { + chatRoom: _chatRoom + } = this.props; + const uniqueId = this.getUniqueId(); + _chatRoom.rebind(`openAttachCloudDialog.${uniqueId}`, () => this.setState({ + attachCloudDialog: true + })); + _chatRoom.rebind(`openSendContactDialog.${uniqueId}`, () => this.setState({ + sendContactDialog: true + })); + _chatRoom.rebind(`openDescriptionDialog.${uniqueId}`, () => this.setState({ + descriptionDialog: true + })); + this.handleKeyDown = SoonFc(120, ev => this._handleKeyDown(ev)); + this.state.waitingRoom = _chatRoom.options.w && (_chatRoom.isAnonymous() || megaChat.initialChatId || is_eplusplus); + } + customIsEventuallyVisible() { + return this.props.chatRoom.isCurrentlyActive; + } + onMouseMove() { + if (this.isComponentEventuallyVisible()) { + this.props.chatRoom.trigger("onChatIsFocused"); + } + } + _handleKeyDown() { + if (this.__isMounted) { + const {chatRoom} = this.props; + if (chatRoom.isActive() && !chatRoom.isReadOnly()) { + chatRoom.trigger("onChatIsFocused"); + } + } + } + handleDeleteDialog(msg) { + if (msg) { + this.setState({ + editing: false, + confirmDeleteDialog: true, + messageToBeDeleted: msg + }); + } + } + toggleExpandedFlag() { + if (this.props.onToggleExpandedFlag) { + this.props.onToggleExpandedFlag(); + } + return document.body.classList[(0,meetings_utils.Av)() ? 'remove' : 'add'](meetings_utils.Fj); + } + startCall(type, scheduled) { + const { + chatRoom + } = this.props; + if (isStartCallDisabled(chatRoom) || chatRoom.iAmWaitingRoomPeer()) { + return false; + } + return type === meetings_utils.ZE.AUDIO ? chatRoom.startAudioCall(scheduled) : chatRoom.startVideoCall(scheduled); + } + renderUpcomingInfo() { + const { + scheduledMeeting + } = this.props.chatRoom; + if (scheduledMeeting) { + const { + recurring, + nextOccurrenceStart, + nextOccurrenceEnd, + isUpcoming + } = scheduledMeeting; + const until = `${(0,helpers.ro)(nextOccurrenceStart, nextOccurrenceEnd) ? '' : time2date(nextOccurrenceEnd / 1000, 4)} ${toLocaleTime(nextOccurrenceEnd)}`; + return JSX_(REaCt().Fragment, null, isUpcoming && recurring && JSX_("span", null, l.next_meeting), JSX_("span", null, (l.schedule_formatted_date || '%1 from %2 to %3').replace('%1', time2date(nextOccurrenceStart / 1000, 4)).replace('%2', toLocaleTime(nextOccurrenceStart)).replace('%3', until))); + } + return null; + } + componentDidMount() { + super.componentDidMount(); + const { + chatRoom + } = this.props; + this.$container = $('.conversation-panel', '#fmholder'); + this.$messages = $('.messages.scroll-area > .perfectScrollbarContainer', this.$container); + window.addEventListener('keydown', this.handleKeyDown); + chatRoom.rebind('onSendMessage.scrollToBottom', () => { + chatRoom.scrolledToBottom = true; + if (this.messagesListScrollable) { + this.messagesListScrollable.scrollToBottom(); + } + }); + chatRoom.rebind('openSendFilesDialog.cpanel', () => this.setState({ + attachCloudDialog: true + })); + chatRoom.rebind('showGetChatLinkDialog.ui', () => { + createTimeoutPromise(() => chatRoom.topic && chatRoom.state === ChatRoom.STATE.READY, 350, 15000).always(() => { + return chatRoom.isCurrentlyActive ? this.setState({ + chatLinkDialog: true + }) : chatRoom.updatePublicHandle(false, true); + }); + }); + if (chatRoom.type === 'private') { + const otherContactHash = chatRoom.getParticipantsExceptMe()[0]; + if (otherContactHash in M.u) { + this._privateChangeListener = M.u[otherContactHash].addChangeListener(() => { + if (!this.isMounted()) { + return 0xDEAD; + } + this.safeForceUpdate(); + }); + } + } + if (is_chatlink && !chatRoom.isMeeting) { + this.state.setNonLoggedInJoinChatDlgTrue = setTimeout(() => { + M.safeShowDialog('chat-links-preview-desktop', () => { + if (this.isMounted()) { + this.setState({ + nonLoggedInJoinChatDialog: true + }); + } + }); + }, rand_range(5, 10) * 1000); + } + if (is_chatlink && chatRoom.isMeeting && u_type !== false && u_type < 3) { + eventlog(99747, JSON.stringify([1, u_type | 0]), true); + } + chatRoom._uiIsMounted = true; + chatRoom.$rConversationPanel = this; + onIdle(() => this.isMounted() && chatRoom.trigger('onComponentDidMount')); + ChatdIntegration._waitForProtocolHandler(chatRoom, () => { + if (this.isMounted()) { + const hasInvalidKeys = chatRoom.hasInvalidKeys(); + this.setState({ + hasInvalidKeys, + invalidKeysBanner: hasInvalidKeys + }, () => this.safeForceUpdate()); + } + }); + megaChat.rebind(`${megaChat.plugins.meetingsManager.EVENTS.OCCURRENCES_UPDATE}.${this.getUniqueId()}`, () => { + return this.isMounted() && this.setState({ + occurrencesLoading: false + }); + }); + chatRoom.rebind(`wrOnJoinNotAllowed.${this.getUniqueId()}`, () => { + return this.isMounted() && this.setState({ + waitingRoom: true + }); + }); + chatRoom.rebind(`wrOnJoinAllowed.${this.getUniqueId()}`, () => { + return this.isMounted() && this.setState({ + waitingRoom: false + }); + }); + chatRoom.rebind(`onCallUserLimitExceeded.${this.getUniqueId()}`, () => { + if (!this.isMounted()) { + return; + } + if (megaChat.initialChatId || is_eplusplus) { + this.setState({ + callUserLimit: true + }); + } + }); + chatRoom.rebind(`onHistTimeoutChange.${this.getUniqueId()}`, () => { + if (this.state.historyTimeOutBanner === DISMISS_TRANSITIONS.NOT_SHOWN && chatRoom.historyTimedOut) { + this.setState({ + historyTimeOutBanner: DISMISS_TRANSITIONS.SHOWN + }); + } else if (this.state.historyTimeOutBanner && !chatRoom.historyTimedOut) { + this.setState({ + historyTimeOutBanner: DISMISS_TRANSITIONS.NOT_SHOWN + }); + } + }); + if (chatRoom.options.w) { + chatRoom.rebind(`onMembersUpdated.${this.getUniqueId()}`, (ev, { + userId, + priv + }) => { + if (userId === u_handle && priv !== ChatRoom.MembersSet.PRIVILEGE_STATE.LEFT) { + chatRoom.unbind(`onMembersUpdated.${this.getUniqueId()}`); + if (is_chatlink) { + return megaChat.routing.reinitAndOpenExistingChat(chatRoom.chatId, chatRoom.publicChatHandle).then(chatRoom => chatRoom.havePendingCall() && priv === ChatRoom.MembersSet.PRIVILEGE_STATE.OPERATOR && chatRoom.joinCall()).catch(dump); + } + return this.state.waitingRoom && this.setState({ + waitingRoom: priv !== ChatRoom.MembersSet.PRIVILEGE_STATE.OPERATOR + }); + } + }); + } + this.pageChangeListener = mBroadcaster.addListener('beforepagechange', () => M.chat && this.state.waitingRoom && this.setState({ + waitingRoom: false + }, () => this.safeForceUpdate())); + } + componentWillUnmount() { + super.componentWillUnmount(); + const self = this; + const {chatRoom} = self.props; + chatRoom._uiIsMounted = true; + if (this._privateChangeListener) { + const otherContactHash = self.props.chatRoom.getParticipantsExceptMe()[0]; + if (otherContactHash in M.u) { + M.u[otherContactHash].removeChangeListener(this._privateChangeListener); + delete this._privateChangeListener; + } + } + mBroadcaster.removeListener(this.pageChangeListener); + this.props.chatRoom.unbind(`openAttachCloudDialog.${this.getUniqueId()}`); + this.props.chatRoom.unbind(`openSendContactDialog.${this.getUniqueId()}`); + this.props.chatRoom.unbind(`openDescriptionDialog.${this.getUniqueId()}`); + window.removeEventListener('keydown', self.handleKeyDown); + $(document).off(`fullscreenchange.megaChat_${chatRoom.roomId}`); + $(document).off(`keydown.keyboardScroll_${chatRoom.roomId}`); + this.props.chatRoom.unbind(`wrOnJoinNotAllowed.${this.getUniqueId()}`); + this.props.chatRoom.unbind(`wrOnJoinAllowed.${this.getUniqueId()}`); + megaChat.unbind(`onIncomingCall.${this.getUniqueId()}`); + this.props.chatRoom.unbind(`onHistTimeoutChange.${this.getUniqueId()}`); + } + componentDidUpdate(prevProps, prevState) { + const self = this; + const room = this.props.chatRoom; + room.megaChat.updateSectionUnreadCount(); + if (prevProps.isActive === false && self.props.isActive === true) { + const $typeArea = $('.messages-textarea:visible:first', this.$container); + if ($typeArea.length === 1) { + $typeArea.trigger("focus"); + moveCursortoToEnd($typeArea[0]); + } + } + if (!prevState.renameDialog && self.state.renameDialog === true) { + Soon(() => { + const $input = $('.chat-rename-dialog input'); + if ($input && $input[0] && !$($input[0]).is(":focus")) { + $input.trigger("focus"); + $input[0].selectionStart = 0; + $input[0].selectionEnd = $input.val().length; + } + }); + } + if (self.$messages && self.isComponentEventuallyVisible()) { + $(window).rebind('pastedimage.chatRoom', (e, blob, fileName) => { + if (self.$messages && self.isComponentEventuallyVisible()) { + self.setState({ + 'pasteImageConfirmDialog': [blob, fileName, URL.createObjectURL(blob)] + }); + e.preventDefault(); + } + }); + self.props.chatRoom.trigger("onComponentDidUpdate"); + } + } + isActive() { + return document.hasFocus() && this.$messages && this.$messages.is(":visible"); + } + render() { + const self = this; + const room = this.props.chatRoom; + if (!room || !room.roomId) { + return null; + } + const contacts = room.getParticipantsExceptMe(); + let contactHandle; + let contact; + let nonLoggedInJoinChatDialog = null; + if (self.state.nonLoggedInJoinChatDialog === true) { + const usersCount = Object.keys(room.members).length; + const closeJoinDialog = () => { + onIdle(() => { + if ($.dialog === 'chat-links-preview-desktop') { + closeDialog(); + } + }); + self.setState({ + 'nonLoggedInJoinChatDialog': false + }); + }; + nonLoggedInJoinChatDialog = JSX_(modalDialogs.A.ModalDialog, { + title: l[20596], + className: "mega-dialog chat-links-preview-desktop dialog-template-graphic", + chatRoom: room, + onClose: closeJoinDialog + }, JSX_("section", { + className: "content" + }, JSX_("div", { + className: "chatlink-contents" + }, JSX_("div", { + className: "huge-icon group-chat" + }), JSX_("h3", null, JSX_(utils.zT, null, room.getRoomTitle())), JSX_("h5", null, usersCount ? mega.icu.format(l[20233], usersCount) : ''), JSX_("p", null, l[20595]))), JSX_("footer", null, JSX_("div", { + className: "bottom-buttons" + }, JSX_("button", { + className: "mega-button positive", + onClick: () => { + closeJoinDialog(); + megaChat.loginOrRegisterBeforeJoining(room.publicChatHandle, false, false, false, () => { + megaChat.routing.reinitAndJoinPublicChat(room.chatId, room.publicChatHandle, room.publicChatKey).then(() => { + delete megaChat.initialPubChatHandle; + }, ex => { + console.error("Failed to join room:", ex); + }); + }); + } + }, l[20597]), JSX_("button", { + className: "mega-button", + onClick: closeJoinDialog + }, l[18682])))); + } + let privateChatDialog; + if (self.state.privateChatDialog === true) { + const onClose = () => this.setState({ + privateChatDialog: false + }); + privateChatDialog = JSX_(modalDialogs.A.ModalDialog, { + title: l[20594], + className: "mega-dialog create-private-chat", + chatRoom: room, + onClose, + dialogType: "action", + dialogName: "create-private-chat-dialog" + }, JSX_("section", { + className: "content" + }, JSX_("div", { + className: "content-block" + }, JSX_("i", { + className: "huge-icon lock" + }), JSX_("div", { + className: "dialog-body-text" + }, JSX_("strong", null, l[20590]), JSX_("br", null), JSX_("span", null, l[20591])))), JSX_("footer", null, JSX_("div", { + className: "footer-container" + }, JSX_("button", { + className: "mega-button positive large", + onClick: () => { + this.props.chatRoom.switchOffPublicMode(); + onClose(); + } + }, JSX_("span", null, l[20593]))))); + } + let confirmDeleteDialog = null; + if (self.state.confirmDeleteDialog === true) { + confirmDeleteDialog = JSX_(modalDialogs.A.ConfirmDialog, { + chatRoom: room, + dialogType: "main", + title: l[8004], + subtitle: l[8879], + name: "delete-message", + pref: "1", + onClose: () => { + self.setState({ + 'confirmDeleteDialog': false + }); + }, + onConfirmClicked: () => { + const msg = self.state.messageToBeDeleted; + if (!msg) { + return; + } + const chatdint = room.megaChat.plugins.chatdIntegration; + if (msg.getState() === Message.STATE.SENT || msg.getState() === Message.STATE.DELIVERED || msg.getState() === Message.STATE.NOT_SENT) { + const textContents = msg.textContents || ''; + if (textContents[1] === Message.MANAGEMENT_MESSAGE_TYPES.VOICE_CLIP) { + const attachmentMetadata = msg.getAttachmentMeta() || []; + Promise.all(attachmentMetadata.map(v => M.moveToRubbish(v.h))).catch(dump); + } + chatdint.deleteMessage(room, msg.internalId ? msg.internalId : msg.orderValue); + msg.deleted = true; + msg.textContents = ""; + } else if (msg.getState() === Message.STATE.NOT_SENT_EXPIRED) { + chatdint.discardMessage(room, msg.internalId ? msg.internalId : msg.orderValue); + } + self.setState({ + 'confirmDeleteDialog': false, + 'messageToBeDeleted': false + }); + if (msg.getState && msg.getState() === Message.STATE.NOT_SENT && !msg.requiresManualRetry) { + msg.message = ""; + msg.textContents = ""; + msg.messageHtml = ""; + msg.deleted = true; + msg.trigger('onChange', [msg, "deleted", false, true]); + } + } + }, JSX_("section", { + className: "content" + }, JSX_("div", { + className: "content-block" + }, JSX_(generic.A, { + className: " dialog-wrapper", + message: self.state.messageToBeDeleted, + hideActionButtons: true, + initTextScrolling: true, + dialog: true, + chatRoom: self.props.chatRoom + })))); + } + if (self.state.pasteImageConfirmDialog) { + confirmDeleteDialog = JSX_(modalDialogs.A.ConfirmDialog, { + chatRoom: room, + title: l[20905], + subtitle: l[20906], + icon: "sprite-fm-uni icon-question", + name: "paste-image-chat", + pref: "2", + onClose: () => { + self.setState({ + 'pasteImageConfirmDialog': false + }); + }, + onConfirmClicked: () => { + const meta = self.state.pasteImageConfirmDialog; + if (!meta) { + return; + } + try { + Object.defineProperty(meta[0], 'name', { + configurable: true, + writeable: true, + value: `${Date.now() }.${ M.getSafeName(meta[1] || meta[0].name)}` + }); + } catch (e) {} + self.props.chatRoom.scrolledToBottom = true; + M.addUpload([meta[0]]); + self.setState({ + 'pasteImageConfirmDialog': false + }); + URL.revokeObjectURL(meta[2]); + } + }, JSX_("img", { + src: self.state.pasteImageConfirmDialog[2], + style: { + maxWidth: "90%", + height: "auto", + maxHeight: $(document).outerHeight() * 0.3, + margin: '10px auto', + display: 'block', + border: '1px solid #ccc', + borderRadius: '4px' + }, + onLoad (e) { + $(e.target).parents('.paste-image-chat').position({ + of: $(document.body) + }); + } + })); + } + if (self.state.truncateDialog === true) { + confirmDeleteDialog = JSX_(modalDialogs.A.ConfirmDialog, { + chatRoom: room, + title: room.isMeeting ? l.meeting_clear_hist : l[8871], + subtitle: room.isMeeting ? l.meeting_trunc_txt : l[8881], + icon: "sprite-fm-uni icon-question", + name: "truncate-conversation", + pref: "3", + dontShowAgainCheckbox: false, + onClose: () => { + self.setState({ + 'truncateDialog': false + }); + }, + onConfirmClicked: () => { + self.props.chatRoom.scrolledToBottom = true; + room.truncate(); + self.setState({ + 'truncateDialog': false + }); + } + }); + } + if (self.state.archiveDialog === true) { + confirmDeleteDialog = JSX_(modalDialogs.A.ConfirmDialog, { + chatRoom: room, + title: room.isMeeting ? l.meeting_archive_dlg : l[19068], + subtitle: room.isMeeting ? l.meeting_archive_dlg_text : l[19069], + icon: "sprite-fm-uni icon-question", + name: "archive-conversation-dialog", + pref: "4", + onClose: () => { + self.setState({ + 'archiveDialog': false + }); + }, + onConfirmClicked: () => { + self.props.chatRoom.scrolledToBottom = true; + room.archive(); + self.setState({ + 'archiveDialog': false + }); + } + }); + } + if (self.state.unarchiveDialog === true) { + confirmDeleteDialog = JSX_(modalDialogs.A.ConfirmDialog, { + chatRoom: room, + title: room.isMeeting ? l.meeting_unarchive_dlg : l[19063], + subtitle: room.isMeeting ? l.meeting_unarchive_dlg_text : l[19064], + icon: "sprite-fm-uni icon-question", + name: "unarchive-conversation-dialog", + pref: "5", + onClose: () => { + self.setState({ + 'unarchiveDialog': false + }); + }, + onConfirmClicked: () => { + self.props.chatRoom.scrolledToBottom = true; + room.unarchive(); + self.setState({ + 'unarchiveDialog': false + }); + } + }); + } + let topicInfo = null; + const isUpcoming = room.scheduledMeeting && room.scheduledMeeting.isUpcoming; + const isRecurring = room.scheduledMeeting && room.scheduledMeeting.isRecurring; + if (room.type === 'group' || room.type === 'public') { + topicInfo = JSX_("div", { + className: "chat-topic-info" + }, JSX_("div", { + className: ` + chat-topic-icon + ${room.isMeeting ? 'meeting-icon' : ''} + ` + }, JSX_("i", { + className: room.isMeeting ? 'sprite-fm-mono icon-video-call-filled' : 'sprite-fm-uni icon-chat-group' + })), JSX_("div", { + className: "chat-topic-text" + }, JSX_("span", { + className: "txt" + }, JSX_(utils.zT, null, room.getRoomTitle()), isUpcoming && isRecurring && JSX_("i", { + className: "sprite-fm-mono recurring-meeting icon-repeat-thin-solid" + })), JSX_("span", { + className: "txt small" + }, is_chatlink && isUpcoming && !isRecurring ? this.renderUpcomingInfo() : JSX_(ui_contacts.U5, { + chatRoom: room + })))); + } else { + contactHandle = contacts[0]; + contact = M.u[contactHandle || u_handle]; + topicInfo = megaChat.WITH_SELF_NOTE && room.isNote ? JSX_("div", { + className: "note-chat-topic" + }, JSX_("div", { + className: "note-chat-signifier" + }, JSX_("i", { + className: "sprite-fm-mono icon-file-text-thin-outline note-chat-icon" + })), JSX_("span", { + className: "note-chat-label" + }, l.note_label)) : JSX_(ui_contacts.nB, { + key: contact.u, + className: "short", + chatRoom: room, + contact, + noContextButton: true, + showLastGreen: true + }); + } + let historyRetentionDialog = null; + if (self.state.showHistoryRetentionDialog === true) { + historyRetentionDialog = JSX_(HistoryRetentionDialog, { + chatRoom: room, + title: '', + name: "rename-group", + className: "", + onClose: () => { + self.setState({ + showHistoryRetentionDialog: false + }); + } + }); + } + if (this.state.waitingRoom) { + return JSX_(WaitingRoom, { + chatRoom: room, + havePendingCall: room.havePendingCall(), + onWaitingRoomLeave: () => { + let _room$call; + (_room$call = room.call) == null || _room$call.destroy(); + if (is_eplusplus) { + room.leave(true); + return onIdle(M.logout); + } + return this.setState({ + waitingRoom: false + }, () => { + onIdle(() => { + if (megaChat.initialChatId) { + megaChat.initialChatId = undefined; + loadSubPage(getLandingPage()); + } + }); + }); + } + }); + } + if (this.state.callUserLimit) { + return JSX_(ChatOverlay, { + overlayType: ChatOverlays.PARTICIPANT_LIMIT, + onClose: () => { + if (is_eplusplus) { + location.replace('https://mega.io'); + } else { + this.setState({ + callUserLimit: false + }); + } + } + }); + } + const startCallDisabled = isStartCallDisabled(room) || room.iAmWaitingRoomPeer(); + return JSX_("div", { + ref: this.domRef, + className: ` + conversation-panel + ${room.type === 'public' ? 'group-chat ' : ''} + ${room.type}-chat + ${!room.isCurrentlyActive || megaChat._joinDialogIsShown ? 'hidden' : ''} + `, + onMouseMove: () => self.onMouseMove(), + "data-room-id": self.props.chatRoom.chatId + }, JSX_(external_React_.Suspense, { + fallback: JSX_(fallback.A, null) + }, room.meetingsLoading && JSX_(Loading, { + chatRoom: room, + title: room.meetingsLoading.title + }), room.call && JSX_(Call, { + chatRoom: room, + peers: room.call.peers, + call: room.call, + minimized: this.state.callMinimized, + typingAreaText: this.state.typingAreaText, + onCallMinimize: () => { + return this.state.callMinimized ? null : this.setState({ + callMinimized: true + }, () => { + this.toggleExpandedFlag(); + this.safeForceUpdate(); + }); + }, + onCallExpand: () => { + return this.state.callMinimized && this.setState({ + callMinimized: false + }, () => { + $.hideTopMenu(); + if ($.dialog) { + closeDialog(); + } + loadSubPage('fm/chat'); + room.show(); + this.toggleExpandedFlag(); + }); + }, + didMount: () => { + this.toggleExpandedFlag(); + if (room.isMeeting) { + room.updatePublicHandle().catch(dump); + } + }, + willUnmount: minimised => this.setState({ + callMinimized: false + }, () => minimised ? null : this.toggleExpandedFlag()), + onCallEnd: () => this.safeForceUpdate(), + onDeleteMessage: msg => this.handleDeleteDialog(msg), + onTypingAreaChanged: this.updateTypingAreaText, + parent: this + }), megaChat.initialPubChatHandle && room.publicChatHandle === megaChat.initialPubChatHandle && !room.call && room.isMeeting && !room.call && room.activeCallIds.length > 0 && JSX_(Join, { + initialView: u_type || is_eplusplus ? workflow_utils.j.ACCOUNT : workflow_utils.j.INITIAL, + chatRoom: room, + onJoinGuestClick: (firstName, lastName, audioFlag, videoFlag) => { + room.meetingsLoading = l.joining; + u_eplusplus(firstName, lastName).then(() => { + return megaChat.routing.reinitAndJoinPublicChat(room.chatId, room.publicChatHandle, room.publicChatKey); + }).then(() => { + delete megaChat.initialPubChatHandle; + return megaChat.getChatById(room.chatId).joinCall(audioFlag, videoFlag); + }).catch(ex => { + if (d) { + console.error('E++ account failure!', ex); + } + setTimeout(() => { + msgDialog('warninga', l[135], l.eplusplus_create_failed, escapeHTML(api_strerror(ex) || ex)); + }, 1234); + eventlog(99745, JSON.stringify([1, String(ex).split('\n')[0]])); + }); + }, + onJoinClick: (audioFlag, videoFlag) => { + const {chatId} = room; + if (room.members[u_handle]) { + delete megaChat.initialPubChatHandle; + megaChat.routing.reinitAndOpenExistingChat(chatId, room.publicChatHandle).then(() => { + return megaChat.getChatById(chatId).joinCall(audioFlag, videoFlag); + }).catch(ex => { + console.error("Failed to open existing room and join call:", ex); + }); + } else { + megaChat.routing.reinitAndJoinPublicChat(chatId, room.publicChatHandle, room.publicChatKey).then(() => { + delete megaChat.initialPubChatHandle; + return megaChat.getChatById(chatId).joinCall(audioFlag, videoFlag); + }).catch(ex => { + console.error("Failed to join room:", ex); + }); + } + } + })), JSX_("div", { + className: ` + chat-content-block + ${room.megaChat.chatUIFlags.convPanelCollapse ? 'no-pane' : 'with-pane'} + ` + }, room.megaChat.chatUIFlags.convPanelCollapse ? null : JSX_(ConversationRightArea, { + isVisible: this.props.chatRoom.isCurrentlyActive, + chatRoom: this.props.chatRoom, + roomFlags: this.props.chatRoom.flags, + members: this.props.chatRoom.membersSetFromApi, + messagesBuff: room.messagesBuff, + pushSettingsValue: pushNotificationSettings.getDnd(this.props.chatRoom.chatId), + occurrencesLoading: this.state.occurrencesLoading, + onStartCall: mode => (0,meetings_utils.dQ)(room.haveActiveCall(), room).then(() => this.startCall(mode)).catch(() => d && console.warn('Already in a call.')), + onAttachFromComputerClicked: () => this.props.chatRoom.uploadFromComputer(), + onTruncateClicked: () => this.setState({ + truncateDialog: true + }), + onArchiveClicked: () => this.setState({ + archiveDialog: true + }), + onUnarchiveClicked: () => this.setState({ + unarchiveDialog: true + }), + onRenameClicked: () => { + this.setState({ + renameDialog: true, + renameDialogValue: this.props.chatRoom.getRoomTitle() + }); + }, + onGetManageChatLinkClicked: () => this.setState({ + chatLinkDialog: true + }), + onMakePrivateClicked: () => this.setState({ + privateChatDialog: true + }), + onCloseClicked: () => room.destroy(), + onJoinViaPublicLinkClicked: () => room.joinViaPublicHandle(), + onSwitchOffPublicMode: topic => room.switchOffPublicMode(topic), + onAttachFromCloudClicked: () => this.setState({ + attachCloudDialog: true + }), + onPushSettingsClicked: () => M.safeShowDialog('push-settings-dialog', () => this.setState({ + pushSettingsDialog: true + })), + onPushSettingsToggled: () => { + return room.dnd || room.dnd === 0 ? this.setState({ + pushSettingsValue: null + }, () => pushNotificationSettings.disableDnd(room.chatId)) : pushNotificationSettings.setDnd(room.chatId, 0); + }, + onHistoryRetentionConfig: () => this.setState({ + showHistoryRetentionDialog: true + }), + onAddParticipantSelected: contactHashes => { + room.scrolledToBottom = true; + if (room.type === 'group' || room.type === 'public') { + if (room.options.w && room.call) { + let _room$call$sfuClient; + (_room$call$sfuClient = room.call.sfuClient) == null || _room$call$sfuClient.wrAllowJoin(contactHashes); + } + return room.trigger('onAddUserRequest', [contactHashes]); + } + loadingDialog.show(); + megaChat.trigger('onNewGroupChatRequest', [[...room.getParticipantsExceptMe(), ...contactHashes], { + keyRotation: false, + topic: '' + }]); + }, + onShowScheduledDescription: room.scheduledMeeting ? () => this.setState({ + descriptionDialog: true + }) : null + }), this.state.sendContactDialog && JSX_(this.SelectContactDialog, null), this.state.descriptionDialog && JSX_(this.DescriptionDialog, null), this.state.pushSettingsDialog && JSX_(this.PushSettingsDialog, null), JSX_(external_React_.Suspense, { + fallback: JSX_(fallback.A, null) + }, this.state.attachCloudDialog && JSX_(CloudBrowserDialog, { + room, + allowAttachFolders: true, + onSelected: nodes => { + this.selectedNodes = nodes; + }, + onAttachClicked: () => { + this.setState({ + attachCloudDialog: false + }, () => { + chatRoom.scrolledToBottom = true; + chatRoom.attachNodes(this.selectedNodes).catch(dump); + }); + }, + onClose: () => { + this.setState({ + attachCloudDialog: false + }, () => { + this.selectedNodes = []; + }); + } + })), privateChatDialog, nonLoggedInJoinChatDialog, confirmDeleteDialog, historyRetentionDialog, null, this.state.renameDialog && JSX_(this.RenameDialog, null), this.state.chatLinkDialog && JSX_(ChatlinkDialog, { + chatRoom: this.props.chatRoom, + onClose: () => this.setState({ + chatLinkDialog: false + }) + }), JSX_("div", { + className: ` + chat-topic-block + ${room.isNote ? 'is-note' : ''} + ` + }, JSX_("div", { + className: "chat-topic-buttons" + }, room.type === 'public' && room.isMeeting && JSX_(buttons.$, { + className: "mega-button small share-meeting-button", + label: l.share_meeting_button, + onClick: () => this.setState({ + chatLinkDialog: true + }, () => eventlog(500230)) + }), JSX_(buttons.$, { + className: "right", + disableCheckingVisibility: true, + icon: "sprite-fm-mono icon-info-filled", + onClick: () => room.megaChat.toggleUIFlag('convPanelCollapse') + }), room.isNote ? null : JSX_(REaCt().Fragment, null, JSX_("div", { + "data-simpletip": l.unsupported_browser_video, + "data-simpletipposition": "top", + "data-simpletipoffset": "5", + className: ` + ${!megaChat.hasSupportForCalls ? 'simpletip' : ''} + right + ${startCallDisabled ? 'disabled' : ''} + ` + }, JSX_(buttons.$, { + icon: "sprite-fm-mono icon-video-call-filled", + onClick: () => startCallDisabled ? false : (0,meetings_utils.dQ)(room.haveActiveCall(), room).then(() => this.startCall(meetings_utils.ZE.VIDEO)).catch(() => d && console.warn('Already in a call.')).then(() => room.isMeeting ? eventlog(500289) : eventlog(500290)) + })), JSX_("div", { + "data-simpletip": l.unsupported_browser_audio, + "data-simpletipposition": "top", + "data-simpletipoffset": "5", + className: ` + ${!megaChat.hasSupportForCalls ? 'simpletip' : ''} + right + ${startCallDisabled ? 'disabled' : ''} + ` + }, JSX_(buttons.$, { + icon: "sprite-fm-mono icon-phone", + onClick: () => startCallDisabled ? false : (0,meetings_utils.dQ)(room.haveActiveCall(), room).then(() => this.startCall(meetings_utils.ZE.AUDIO)).catch(() => d && console.warn('Already in a call.')).then(() => room.isMeeting ? eventlog(500291) : eventlog(500292)) + })))), topicInfo), JSX_("div", { + ref: this.messagesBlockRef, + className: ` + messages-block + ${""} + ` + }, this.state.hasInvalidKeys && this.state.invalidKeysBanner && JSX_(Alert, { + type: Alert.TYPE.HIGH, + className: ` + ${megaChat.chatUIFlags.convPanelCollapse ? 'full-span' : ''} + ${this.props.offset === ALERTS_BASE_OFFSET ? 'single-alert' : ''} + `, + offset: this.props.offset === ALERTS_BASE_OFFSET ? 0 : this.props.offset, + content: JSX_(REaCt().Fragment, null, l.chat_key_failed_banner.split('[A]')[0], JSX_("a", { + onClick: () => M.reload() + }, l.chat_key_failed_banner.substring(l.chat_key_failed_banner.indexOf('[A]') + 3, l.chat_key_failed_banner.indexOf('[/A]'))), l.chat_key_failed_banner.split('[/A]')[1]), + onClose: () => this.setState({ + invalidKeysBanner: false + }) + }), this.state.historyTimeOutBanner === DISMISS_TRANSITIONS.SHOWN && JSX_(Alert, { + type: Alert.TYPE.ERROR, + className: ` + ${megaChat.chatUIFlags.convPanelCollapse ? 'full-span' : ''} + ${this.props.offset === ALERTS_BASE_OFFSET ? 'single_alert' : ''} + history-timeout-banner + `, + offset: this.props.offset === ALERTS_BASE_OFFSET ? 0 : this.props.offset, + content: JSX_(REaCt().Fragment, null, l.chat_timeout_banner, JSX_("a", { + onClick: () => location.reload() + }, l[85])), + onClose: () => this.setState({ + historyTimeOutBanner: DISMISS_TRANSITIONS.DISMISSED + }) + }), JSX_(historyPanel.A, (0,esm_extends.A)({}, this.props, { + onMessagesListScrollableMount: mls => { + this.messagesListScrollable = mls; + }, + ref: historyPanel => { + this.historyPanel = historyPanel; + }, + onDeleteClicked: msg => this.handleDeleteDialog(msg) + })), !is_chatlink && room.state !== ChatRoom.STATE.LEFT && navigator.onLine && room.scheduledMeeting && !room.isArchived() && !this.state.hasInvalidKeys && !isStartCallDisabled(room) ? JSX_(StartMeetingNotification, { + chatRoom: room, + offset: this.props.offset, + onWaitingRoomJoin: () => this.setState({ + waitingRoom: true + }), + onStartCall: mode => { + return isStartCallDisabled(room) ? null : (0,meetings_utils.dQ)(true, room).then(() => this.startCall(mode, true)).catch(ex => d && console.warn(`Already in a call. ${ex}`)); + } + }) : null, !is_chatlink && room.state !== ChatRoom.STATE.LEFT && (room.havePendingGroupCall() || room.havePendingCall()) && !this.state.hasInvalidKeys && navigator.onLine ? JSX_(JoinCallNotification, { + rhpCollapsed: megaChat.chatUIFlags.convPanelCollapse, + chatRoom: room, + offset: this.props.offset + }) : null, room.isAnonymous() ? JSX_("div", { + className: "join-chat-block" + }, JSX_("div", { + className: "mega-button large positive", + onClick: () => { + const join = () => { + megaChat.routing.reinitAndJoinPublicChat(room.chatId, room.publicChatHandle, room.publicChatKey).then(() => delete megaChat.initialPubChatHandle, ex => console.error("Failed to join room:", ex)); + }; + if (u_type === 0) { + return loadSubPage('register'); + } + if (u_type === false) { + clearTimeout(self.state.setNonLoggedInJoinChatDlgTrue); + megaChat.loginOrRegisterBeforeJoining(room.publicChatHandle, false, false, false, join); + return; + } + clearTimeout(self.state.setNonLoggedInJoinChatDlgTrue); + join(); + } + }, l[20597])) : JSX_(composedTextArea.A, { + chatRoom: room, + parent: this, + containerRef: this.messagesBlockRef, + typingAreaText: this.state.typingAreaText, + onTypingAreaChanged: this.updateTypingAreaText + })))); + } +}, (0,applyDecoratedDescriptor.A)(conversationpanel_class.prototype, "onMouseMove", [conversationpanel_dec], Object.getOwnPropertyDescriptor(conversationpanel_class.prototype, "onMouseMove"), conversationpanel_class.prototype), (0,applyDecoratedDescriptor.A)(conversationpanel_class.prototype, "render", [_dec2], Object.getOwnPropertyDescriptor(conversationpanel_class.prototype, "render"), conversationpanel_class.prototype), conversationpanel_class); +class ConversationPanels extends mixins.w9 { + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.notificationListener = 'meetings:notificationPermissions'; + this.notificationGranted = undefined; + this.notificationHelpURL = `${l.mega_help_host}/chats-meetings/meetings/enable-notification-browser-system-permission`; + this.state = { + supportAlert: undefined, + notificationsPermissions: undefined, + alertsOffset: ALERTS_BASE_OFFSET + }; + this.closeSupportAlert = () => this.setState({ + supportAlert: false + }, () => mega.config.set('nocallsup', 1)); + this.onNotificationsGranted = () => { + msgDialog('info', '', l.notifications_permissions_granted_title, l.notifications_permissions_granted_info.replace('[A]', ``).replace('[/A]', '')); + this.notificationGranted = new Notification(l.notification_granted_title, { + body: l.notification_granted_body + }); + }; + this.state.supportAlert = !megaChat.hasSupportForCalls; + this.state.notificationsPermissions = window.Notification ? Notification.permission : 'granted'; + } + renderNotificationsPending() { + return JSX_(Alert, { + type: Alert.TYPE.LIGHT, + className: ` + ${megaChat.chatUIFlags.convPanelCollapse ? 'full-span' : ''} + ${this.props.isEmpty ? 'empty-state' : ''} + `, + ref: ref => { + this.notifPendingRef = ref; + }, + onTransition: ref => this.setState({ + alertsOffset: ref ? ref.current.offsetHeight : ALERTS_BASE_OFFSET + }), + onClose: () => { + this.setState({ + notificationsPermissions: undefined + }, () => { + showToast('success', l.notifications_permissions_toast_title, l.notifications_permissions_toast_control, '', () => loadSubPage('fm/account/notifications')); + }); + } + }, l.notifications_permissions_pending, JSX_("div", { + className: "meetings-alert-control" + }, JSX_("a", { + href: "#", + onClick: ev => { + ev.preventDefault(); + Notification.requestPermission().then(status => { + this.setState({ + notificationsPermissions: status + }, () => onIdle(() => this.state.notificationsPermissions === 'granted' && this.onNotificationsGranted())); + }).catch(ex => d && console.warn(`Failed to retrieve permissions: ${ex}`)); + } + }, l.notifications_permissions_enable))); + } + renderNotificationsBlocked() { + return JSX_(Alert, { + type: Alert.TYPE.MEDIUM, + className: ` + ${megaChat.chatUIFlags.convPanelCollapse ? 'full-span' : ''} + ${this.props.isEmpty ? 'empty-state' : ''} + `, + ref: ref => { + this.notifBlockedRef = ref; + }, + onTransition: ref => this.setState({ + alertsOffset: ref ? ref.current.offsetHeight : ALERTS_BASE_OFFSET + }), + onClose: () => this.setState({ + notificationsPermissions: undefined + }) + }, JSX_(utils.P9, { + content: l.notifications_permissions_denied_info.replace('[A]', ``).replace('[/A]', '') + })); + } + componentWillUnmount() { + super.componentWillUnmount(); + mBroadcaster.removeListener(this.notificationListener); + } + componentDidMount() { + let _this$props$onMount, _this$props; + super.componentDidMount(); + (_this$props$onMount = (_this$props = this.props).onMount) == null || _this$props$onMount.call(_this$props); + megaChat.chats.forEach(chatRoom => { + const { + scheduledMeeting + } = chatRoom; + if (scheduledMeeting && !scheduledMeeting.isPast && scheduledMeeting.isRecurring) { + scheduledMeeting.getOccurrences().catch(nop); + } + }); + mBroadcaster.addListener(this.notificationListener, notificationsPermissions => this.isMounted() && this.setState({ + notificationsPermissions + })); + window.addEventListener('resize', () => { + delay('conv-panels-resize', () => { + if (!M.chat || !this.isMounted()) { + return; + } + const { + alertsOffset + } = this.state; + if (alertsOffset !== ALERTS_BASE_OFFSET) { + let _this$notifBlockedRef, _this$notifPendingRef, _this$noSupportRef; + const state = {}; + if ((_this$notifBlockedRef = this.notifBlockedRef) != null && _this$notifBlockedRef.current) { + state.alertsOffset = this.notifBlockedRef.current.offsetHeight; + } else if ((_this$notifPendingRef = this.notifPendingRef) != null && _this$notifPendingRef.current) { + state.alertsOffset = this.notifPendingRef.current.offsetHeight; + } else if ((_this$noSupportRef = this.noSupportRef) != null && _this$noSupportRef.current) { + state.alertsOffset = this.noSupportRef.current.offsetHeight; + } + if (state.alertsOffset !== alertsOffset) { + this.setState(state); + } + } + }); + }); + } + render() { + const { + routingSection, + chatUIFlags, + isEmpty, + onToggleExpandedFlag + } = this.props; + const { + notificationsPermissions, + supportAlert, + alertsOffset + } = this.state; + const now = Date.now(); + return JSX_("div", { + ref: this.domRef, + className: "conversation-panels" + }, routingSection === 'contacts' || is_chatlink ? null : window.Notification && notificationsPermissions !== 'granted' && JSX_(REaCt().Fragment, null, notificationsPermissions === 'default' && this.renderNotificationsPending(), notificationsPermissions === 'denied' && this.renderNotificationsBlocked()), routingSection === 'contacts' ? null : supportAlert && !mega.config.get('nocallsup') && JSX_(Alert, { + type: Alert.TYPE.MEDIUM, + className: ` + ${megaChat.chatUIFlags.convPanelCollapse ? 'full-span' : ''} + ${isEmpty ? 'empty-state' : ''} + unsupported-call-alert + `, + content: (0,meetings_utils.HV)(), + ref: ref => { + this.noSupportRef = ref; + }, + onTransition: ref => this.setState({ + alertsOffset: ref ? ref.current.offsetHeight : ALERTS_BASE_OFFSET + }), + onClose: this.closeSupportAlert + }), megaChat.chats.map(chatRoom => { + if (chatRoom.isCurrentlyActive || now - chatRoom.lastShownInUI < 900000) { + return JSX_(ConversationPanel, { + key: `${chatRoom.roomId}_${chatRoom.instanceIndex}`, + chatRoom, + roomType: chatRoom.type, + isExpanded: chatRoom.megaChat.chatUIFlags.convPanelCollapse, + isActive: chatRoom.isCurrentlyActive, + messagesBuff: chatRoom.messagesBuff, + chatUIFlags, + offset: alertsOffset, + onToggleExpandedFlag + }); + } + return null; + })); + } +} +function isStartCallDisabled(room) { + if ((0,meetings_utils.P)()) { + return true; + } + if (!megaChat.hasSupportForCalls) { + return true; + } + return !room.isOnlineForCalls() || room.isReadOnly() || !room.chatId || room.call || (room.type === "group" || room.type === "public") && false || room.getCallParticipants().length > 0; +} + + }, + + 8596 +(_, EXP_, REQ_) { + +REQ_.r(EXP_); + REQ_.d(EXP_, { + "default": () => EmptyConversationsPanel + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _ui_buttons_jsx1__ = REQ_(5155); + const _link_jsx2__ = REQ_(4649); + const _ui_utils_jsx3__ = REQ_(6411); + + + + +const Tile = ({ + title, + desc, + imgClass, + buttonPrimary, + buttonSecondary, + onClickPrimary, + onClickSecondary +}) => JSX_("div", { + className: "conversations-empty-tile" +}, JSX_("span", { + className: `chat-tile-img ${imgClass}` +}), JSX_("div", { + className: "tile-content" +}, JSX_("h2", null, title), JSX_("div", null, desc), JSX_(_ui_buttons_jsx1__ .$, { + className: "mega-button positive", + label: buttonPrimary, + onClick: onClickPrimary +}), buttonSecondary && JSX_(_ui_buttons_jsx1__ .$, { + className: "mega-button action positive", + icon: "sprite-fm-mono icon-link", + label: buttonSecondary, + onClick: onClickSecondary +}))); +class EmptyConversationsPanel extends react0___default().Component { + constructor(...args) { + super(...args); + this.domRef = react0___default().createRef(); + this.state = { + linkData: '' + }; + } + componentDidMount() { + (M.account && M.account.contactLink ? Promise.resolve(M.account.contactLink) : api.send('clc')).then(res => { + let _this$domRef; + if ((_this$domRef = this.domRef) != null && _this$domRef.current && typeof res === 'string') { + const prefix = res.startsWith('C!') ? '' : 'C!'; + this.setState({ + linkData: `${getBaseUrl()}/${prefix}${res}` + }); + } + }).catch(dump); + } + render() { + const { + isMeeting, + onNewChat, + onStartMeeting, + onScheduleMeeting + } = this.props; + const { + linkData + } = this.state; + return JSX_("div", { + ref: this.domRef, + className: "conversations-empty" + }, JSX_("div", { + className: "conversations-empty-header" + }, JSX_("h1", null, isMeeting ? l.meetings_empty_header : l.chat_empty_header), JSX_("h3", null, (0,_ui_utils_jsx3__ .lI)(isMeeting ? l.meetings_empty_subheader : l.chat_empty_subheader, '[A]', _link_jsx2__ .A, { + onClick: () => { + window.open('https://mega.io/chatandmeetings', '_blank', 'noopener,noreferrer'); + eventlog(this.props.isMeeting ? 500281 : 500280); + } + }))), JSX_("div", { + className: "conversations-empty-content" + }, JSX_(Tile, { + title: isMeeting ? l.meetings_empty_calls_head : l.invite_friend_btn, + desc: isMeeting ? l.meetings_empty_calls_desc : l.chat_empty_contact_desc, + imgClass: isMeeting ? 'empty-meetings-call' : 'empty-chat-contacts', + buttonPrimary: isMeeting ? l.new_meeting_start : l[71], + buttonSecondary: !isMeeting && linkData && l.copy_contact_link_btn, + onClickPrimary: () => { + if (isMeeting) { + onStartMeeting(); + eventlog(500275); + } else { + contactAddDialog(); + eventlog(500276); + } + }, + onClickSecondary: () => { + copyToClipboard(linkData, `${l[371]}${linkData}`); + delay('chat-event-copy-contact-link', () => eventlog(500277)); + } + }), JSX_(Tile, { + title: isMeeting ? l.meetings_empty_schedule_head : l.chat_empty_add_chat_header, + desc: isMeeting ? l.meetings_empty_schedule_desc : l.chat_empty_add_chat_desc, + imgClass: isMeeting ? 'empty-meetings-schedule' : 'empty-chat-new', + buttonPrimary: isMeeting ? l.schedule_meeting_start : l.add_chat, + onClickPrimary: () => { + if (isMeeting) { + onScheduleMeeting(); + eventlog(500278); + } else { + onNewChat(); + eventlog(500279); + } + } + }))); + } +} + + }, + + 1635 +(_, EXP_, REQ_) { + + REQ_.d(EXP_, { + Hc: () => GIF_PANEL_CLASS, + L9: () => MAX_HEIGHT, + kg: () => LABELS, + nC: () => API + }); +const GIF_PANEL_CLASS = 'gif-panel-wrapper'; +const MAX_HEIGHT = 550; +const API = { + HOSTNAME: 'https://giphy.mega.nz/', + ENDPOINT: 'v1/gifs', + SCHEME: 'giphy://', + convert: path => { + if (path && typeof path === 'string') { + const FORMAT = [API.SCHEME, API.HOSTNAME]; + if (path.indexOf(API.SCHEME) === 0 || path.indexOf(API.HOSTNAME) === 0) { + return String.prototype.replace.apply(path, path.indexOf(API.SCHEME) === 0 ? FORMAT : FORMAT.reverse()); + } + } + }, + LIMIT: 50, + OFFSET: 50 +}; +const LABELS = freeze({ + get SEARCH() { + return l[24025]; + }, + get NO_RESULTS() { + return l[24050]; + }, + get NOT_AVAILABLE() { + return l[24512]; + }, + get END_OF_RESULTS() { + return l[24156]; + } +}); + + }, + + 5522 +(_, EXP_, REQ_) { + + +// EXPORTS +REQ_.d(EXP_, { + A: () => HistoryPanel +}); + +// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/applyDecoratedDescriptor.js +const applyDecoratedDescriptor = REQ_(793); +// EXTERNAL MODULE: external "React" +const external_React_ = REQ_(1594); +const REaCt = REQ_.n(external_React_); +// EXTERNAL MODULE: ./js/chat/mixins.js +const mixins = REQ_(8264); +// EXTERNAL MODULE: ./js/ui/utils.jsx +const utils = REQ_(6411); +// EXTERNAL MODULE: ./js/chat/ui/contacts.jsx +const contacts = REQ_(8022); +// EXTERNAL MODULE: ./js/chat/ui/messages/mixin.jsx +const mixin = REQ_(855); +;// ./js/chat/ui/messages/alterParticipants.jsx + + + + +class AltPartsConvMessage extends mixin.M { + haveMoreContactListeners() { + if (!this.props.message || !this.props.message.meta) { + return false; + } + const { + included, + excluded + } = this.props.message.meta; + return array.unique([...included || [], ...excluded || []]); + } + render() { + const self = this; + const {message} = this.props; + const contact = self.getContact(); + const timestampInt = self.getTimestamp(); + const timestamp = self.getTimestampAsString(); + const datetime = JSX_("div", { + className: "message date-time simpletip", + "data-simpletip": time2date(timestampInt, 17) + }, timestamp); + let displayName; + if (contact) { + displayName = M.getNameByHandle(contact.u); + } else { + displayName = contact; + } + const messages = []; + message.meta.included.forEach((h) => { + const otherContact = M.u[h] ? M.u[h] : { + 'u': h, + h, + 'c': 0 + }; + const avatar = JSX_(contacts.eu, { + contact: otherContact, + chatRoom: self.props.chatRoom, + className: "message avatar-wrapper small-rounded-avatar" + }); + const otherDisplayName = M.getNameByHandle(otherContact.u); + const isSelfJoin = h === contact.u; + let text = isSelfJoin ? l[23756] : l[8907]; + if (self.props.chatRoom.isMeeting) { + text = isSelfJoin ? l.meeting_mgmt_user_joined : l.meeting_mgmt_user_added; + } + text = text.replace('%1', megaChat.html(otherDisplayName)); + if (!isSelfJoin) { + text = text.replace('%2', `${megaChat.html(displayName)}`); + } + messages.push(JSX_("div", { + className: "message body", + "data-id": `id${ message.messageId}`, + key: `${message.messageId }_${ h}` + }, avatar, JSX_("div", { + className: "message content-area small-info-txt selectable-txt" + }, JSX_(contacts.bq, { + className: "message", + contact: otherContact, + chatRoom: self.props.chatRoom, + label: JSX_(utils.zT, null, otherDisplayName) + }), datetime, JSX_("div", { + className: "message text-block" + }, JSX_(utils.P9, null, text))))); + }); + message.meta.excluded.forEach((h) => { + const otherContact = M.u[h] ? M.u[h] : { + 'u': h, + h, + 'c': 0 + }; + const avatar = JSX_(contacts.eu, { + contact: otherContact, + chatRoom: self.props.chatRoom, + className: "message avatar-wrapper small-rounded-avatar" + }); + const otherDisplayName = M.getNameByHandle(otherContact.u); + let text; + if (otherContact.u === contact.u) { + text = self.props.chatRoom.isMeeting ? l.meeting_mgmt_left : l[8908]; + } else { + text = (self.props.chatRoom.isMeeting ? l.meeting_mgmt_kicked : l[8906]).replace("%s", `${megaChat.html(displayName)}`); + } + messages.push(JSX_("div", { + className: "message body", + "data-id": `id${ message.messageId}`, + key: `${message.messageId }_${ h}` + }, avatar, JSX_("div", { + className: "message content-area small-info-txt selectable-txt" + }, JSX_(contacts.bq, { + className: "message", + chatRoom: self.props.chatRoom, + contact: otherContact, + label: JSX_(utils.zT, null, otherDisplayName) + }), datetime, JSX_("div", { + className: "message text-block" + }, JSX_(utils.P9, null, text))))); + }); + return JSX_("div", null, messages); + } +} + +;// ./js/chat/ui/messages/truncated.jsx + + + + +class TruncatedMessage extends mixin.M { + render() { + const self = this; + let cssClasses = "message body"; + const {message} = this.props; + const {chatRoom} = this.props.message; + const contact = self.getContact(); + const timestampInt = self.getTimestamp(); + const timestamp = self.getTimestampAsString(); + let datetime = JSX_("div", { + className: "message date-time simpletip", + "data-simpletip": time2date(timestampInt, 17) + }, timestamp); + let displayName; + if (contact) { + displayName = M.getNameByHandle(contact.u); + } else { + displayName = contact; + } + let avatar = null; + if (this.props.grouped) { + cssClasses += " grouped"; + } else { + avatar = JSX_(contacts.eu, { + contact, + className: "message avatar-wrapper small-rounded-avatar", + chatRoom + }); + datetime = JSX_("div", { + className: "message date-time simpletip", + "data-simpletip": time2date(timestampInt, 17) + }, timestamp); + } + return JSX_("div", { + className: cssClasses, + "data-id": `id${ message.messageId}`, + key: message.messageId + }, avatar, JSX_("div", { + className: "message content-area small-info-txt selectable-txt" + }, JSX_(contacts.bq, { + contact, + className: "message", + label: JSX_(utils.zT, null, displayName), + chatRoom + }), datetime, JSX_("div", { + className: "message text-block" + }, l[8905]))); + } +} + +;// ./js/chat/ui/messages/privilegeChange.jsx + + + + +class PrivilegeChange extends mixin.M { + haveMoreContactListeners() { + if (!this.props.message.meta || !this.props.message.meta.targetUserId) { + return false; + } + const uid = this.props.message.meta.targetUserId; + if (uid && M.u[uid]) { + return uid; + } + return false; + } + render() { + const self = this; + const {message} = this.props; + const {chatRoom} = this.props.message; + const contact = self.getContact(); + const timestampInt = self.getTimestamp(); + const timestamp = self.getTimestampAsString(); + const datetime = JSX_("div", { + className: "message date-time simpletip", + "data-simpletip": time2date(timestampInt, 17) + }, timestamp); + let displayName; + if (contact) { + displayName = M.getNameByHandle(contact.u); + } else { + displayName = contact; + } + const messages = []; + const otherContact = M.u[message.meta.targetUserId] ? M.u[message.meta.targetUserId] : { + 'u': message.meta.targetUserId, + 'h': message.meta.targetUserId, + 'c': 0 + }; + const avatar = JSX_(contacts.eu, { + contact: otherContact, + className: "message avatar-wrapper small-rounded-avatar", + chatRoom + }); + const otherDisplayName = M.getNameByHandle(otherContact.u); + let newPrivilegeText = ""; + if (message.meta.privilege === 3) { + newPrivilegeText = l.priv_change_to_op; + } else if (message.meta.privilege === 2) { + newPrivilegeText = l.priv_change_to_std; + } else if (message.meta.privilege === 0) { + newPrivilegeText = l.priv_change_to_ro; + } + const text = newPrivilegeText.replace('[S]', '').replace('[/S]', '').replace('%s', `${megaChat.html(displayName)}`); + messages.push(JSX_("div", { + className: "message body", + "data-id": `id${ message.messageId}`, + key: message.messageId + }, avatar, JSX_("div", { + className: "message content-area small-info-txt selectable-txt" + }, JSX_(contacts.bq, { + className: "message", + chatRoom: self.props.chatRoom, + contact: otherContact, + label: JSX_(utils.zT, null, otherDisplayName) + }), datetime, JSX_("div", { + className: "message text-block" + }, JSX_(utils.P9, null, text))))); + return JSX_("div", null, messages); + } +} + +;// ./js/chat/ui/messages/topicChange.jsx + + + + +class TopicChange extends mixin.M { + render() { + const self = this; + const {message} = this.props; + const {megaChat} = this.props.message.chatRoom; + const {chatRoom} = this.props.message; + if (message.meta.isScheduled) { + return null; + } + const contact = self.getContact(); + const timestampInt = self.getTimestamp(); + const timestamp = self.getTimestampAsString(); + const datetime = JSX_("div", { + className: "message date-time simpletip", + "data-simpletip": time2date(timestampInt, 17) + }, timestamp); + let displayName; + if (contact) { + displayName = M.getNameByHandle(contact.u); + } else { + displayName = contact; + } + const messages = []; + const avatar = JSX_(contacts.eu, { + contact, + chatRoom, + className: "message avatar-wrapper small-rounded-avatar" + }); + const topic = megaChat.html(message.meta.topic); + const oldTopic = megaChat.html(message.meta.oldTopic) || ''; + messages.push(JSX_("div", { + className: "message body", + "data-id": `id${ message.messageId}`, + key: message.messageId + }, avatar, JSX_("div", { + className: "message content-area small-info-txt selectable-txt" + }, JSX_(contacts.bq, { + className: "message", + chatRoom, + contact, + label: JSX_(utils.zT, null, displayName) + }), datetime, JSX_("div", { + className: "message text-block" + }, JSX_(utils.P9, null, (chatRoom.scheduledMeeting ? l.schedule_mgmt_title.replace('%1', `${oldTopic}`) : l[9081]).replace('%s', `${topic}`)))))); + return JSX_("div", null, messages); + } +} + +;// ./js/chat/ui/messages/closeOpenMode.jsx + + + + +class CloseOpenModeMessage extends mixin.M { + render() { + const self = this; + let cssClasses = "message body"; + const {message} = this.props; + const contact = self.getContact(); + const timestampInt = self.getTimestamp(); + const timestamp = self.getTimestampAsString(); + let datetime = JSX_("div", { + className: "message date-time", + title: time2date(timestampInt) + }, timestamp); + let displayName; + if (contact) { + displayName = M.getNameByHandle(contact.u); + } else { + displayName = contact; + } + let avatar = null; + if (this.props.grouped) { + cssClasses += " grouped"; + } else { + avatar = JSX_(contacts.eu, { + contact, + className: "message avatar-wrapper small-rounded-avatar", + chatRoom: this.props.chatRoom + }); + datetime = JSX_("div", { + className: "message date-time", + title: time2date(timestampInt) + }, timestamp); + } + return JSX_("div", { + className: cssClasses, + "data-id": `id${ message.messageId}`, + key: message.messageId + }, avatar, JSX_("div", { + className: "message content-area small-info-txt selectable-txt" + }, JSX_("div", { + className: "message user-card-name" + }, JSX_(utils.zT, null, displayName)), datetime, JSX_("div", { + className: "message text-block" + }, l[20569]))); + } +} + +;// ./js/chat/ui/messages/chatHandle.jsx + + + + +class ChatHandleMessage extends mixin.M { + render() { + const self = this; + let cssClasses = "message body"; + const {message} = this.props; + const contact = self.getContact(); + const timestampInt = self.getTimestamp(); + const timestamp = self.getTimestampAsString(); + let datetime = JSX_("div", { + className: "message date-time", + title: time2date(timestampInt) + }, timestamp); + let displayName; + if (contact) { + displayName = M.getNameByHandle(contact.u); + } else { + displayName = contact; + } + let avatar = null; + if (this.props.grouped) { + cssClasses += " grouped"; + } else { + avatar = JSX_(contacts.eu, { + contact, + className: "message avatar-wrapper small-rounded-avatar", + chatRoom: this.props.chatRoom + }); + datetime = JSX_("div", { + className: "message date-time", + title: time2date(timestampInt) + }, timestamp); + } + return JSX_("div", { + className: cssClasses, + "data-id": `id${ message.messageId}`, + key: message.messageId + }, avatar, JSX_("div", { + className: "message content-area small-info-txt selectable-txt" + }, JSX_("div", { + className: "message user-card-name" + }, JSX_(utils.zT, null, displayName)), datetime, JSX_("div", { + className: "message text-block" + }, message.meta.handleUpdate === 1 ? l[20570] : l[20571]))); + } +} + +// EXTERNAL MODULE: ./js/chat/ui/messages/generic.jsx + 14 modules +const generic = REQ_(8025); +// EXTERNAL MODULE: ./js/ui/perfectScrollbar.jsx +const perfectScrollbar = REQ_(1301); +;// ./js/chat/ui/messages/retentionChange.jsx + + + + +class RetentionChange extends mixin.M { + render() { + const { + message + } = this.props; + const contact = this.getContact(); + return JSX_("div", { + className: "message body", + "data-id": `id${ message.messageId}`, + key: message.messageId + }, JSX_(contacts.eu, { + contact, + className: "message avatar-wrapper small-rounded-avatar" + }), JSX_("div", { + className: "message content-area small-info-txt selectable-txt" + }, JSX_(contacts.bq, { + contact, + className: "message", + label: JSX_(utils.zT, null, M.getNameByHandle(contact.u)) + }), JSX_("div", { + className: "message date-time simpletip", + "data-simpletip": time2date(this.getTimestamp(), 17) + }, this.getTimestampAsString()), JSX_("div", { + className: "message text-block" + }, message.getMessageRetentionSummary()))); + } +} +// EXTERNAL MODULE: ./js/chat/ui/meetings/utils.jsx +const meetings_utils = REQ_(3901); +// EXTERNAL MODULE: ./js/chat/ui/messages/scheduleMetaChange.jsx +const scheduleMetaChange = REQ_(5470); +;// ./js/chat/ui/historyPanel.jsx + +let _dec, _class; + + + + + + + + + + + + + + +const HistoryPanel = (_dec = (0,mixins.hG)(450, true), _class = class HistoryPanel extends mixins.w9 { + constructor(props) { + super(props); + this.$container = null; + this.$messages = null; + this.domRef = REaCt().createRef(); + this.state = { + editing: false, + toast: false + }; + this.renderNotice = label => JSX_("div", { + className: "dropdown body dropdown-arrow down-arrow tooltip not-sent-notification-cancel hidden" + }, JSX_("i", { + className: "dropdown-white-arrow" + }), JSX_("div", { + className: "dropdown notification-text" + }, JSX_("i", { + className: "small-icon conversations" + }), label)); + this.renderLoadingSpinner = () => JSX_("div", { + style: { + top: '50%' + }, + className: ` + loading-spinner + js-messages-loading + light + manual-management + ${this.loadingShown ? '' : 'hidden'} + ` + }, JSX_("div", { + className: "main-loader", + style: { + position: 'fixed', + top: '50%', + left: '50%' + } + })); + this.renderNavigationToast = () => { + const { + chatRoom + } = this.props; + const unreadCount = chatRoom.messagesBuff.getUnreadCount(); + return JSX_("div", { + className: ` + theme-dark-forced + messages-toast + ${this.state.toast ? 'active' : ''} + `, + onClick: () => { + this.setState({ + toast: false + }, () => { + this.messagesListScrollable.scrollToBottom(); + chatRoom.scrolledToBottom = true; + }); + } + }, JSX_("i", { + className: "sprite-fm-mono icon-down" + }), unreadCount > 0 && JSX_("span", null, unreadCount > 9 ? '9+' : unreadCount)); + }; + this.onKeyboardScroll = ({ + keyCode + }) => { + let _scrollbar$domRef; + const scrollbar = this.messagesListScrollable; + const domNode = scrollbar == null || (_scrollbar$domRef = scrollbar.domRef) == null ? void 0 : _scrollbar$domRef.current; + if (domNode && this.isComponentEventuallyVisible() && !this.state.attachCloudDialog) { + const scrollPositionY = scrollbar.getScrollPositionY(); + const offset = parseInt(domNode.style.height); + const PAGE = { + UP: 33, + DOWN: 34 + }; + switch (keyCode) { + case PAGE.UP: + scrollbar.scrollToY(scrollPositionY - offset, true); + this.onMessagesScrollUserScroll(scrollbar, 100); + break; + case PAGE.DOWN: + if (!scrollbar.isAtBottom()) { + scrollbar.scrollToY(scrollPositionY + offset, true); + } + break; + } + } + }; + this.onMessagesScrollUserScroll = (ps, offset = 5) => { + const { + chatRoom + } = this.props; + const { + messagesBuff + } = chatRoom; + const scrollPositionY = ps.getScrollPositionY(); + if (messagesBuff.messages.length === 0) { + chatRoom.scrolledToBottom = true; + return; + } + if (ps.isCloseToBottom(30) === true) { + if (!chatRoom.scrolledToBottom) { + messagesBuff.detachMessages(); + } + chatRoom.scrolledToBottom = true; + } else { + chatRoom.scrolledToBottom = false; + } + if (!this.scrollPullHistoryRetrieval && !messagesBuff.isRetrievingHistory && (ps.isAtTop() || scrollPositionY < offset && ps.getScrollHeight() > 500) && messagesBuff.haveMoreHistory()) { + ps.disable(); + this.scrollPullHistoryRetrieval = true; + this.lastScrollPosition = scrollPositionY; + let msgAppended = 0; + const scrYOffset = ps.getScrollHeight(); + chatRoom.one('onMessagesBuffAppend.pull', () => { + msgAppended++; + }); + chatRoom.off('onHistoryDecrypted.pull'); + chatRoom.one('onHistoryDecrypted.pull', () => { + chatRoom.off('onMessagesBuffAppend.pull'); + if (msgAppended > 0) { + this._reposOnUpdate = scrYOffset; + } + this.scrollPullHistoryRetrieval = -1; + }); + messagesBuff.retrieveChatHistory(); + } + if (this.lastScrollPosition !== scrollPositionY) { + this.lastScrollPosition = scrollPositionY; + } + delay('chat-toast', this.initToast, 200); + }; + this.initToast = () => { + let _this$messagesListScr; + const { + chatRoom + } = this.props; + return this.isMounted() && this.setState({ + toast: !chatRoom.scrolledToBottom && !((_this$messagesListScr = this.messagesListScrollable) != null && _this$messagesListScr.isCloseToBottom != null && _this$messagesListScr.isCloseToBottom(30)) + }, () => this.state.toast ? null : chatRoom.trigger('onChatIsFocused')); + }; + this.handleWindowResize = this._handleWindowResize.bind(this); + } + customIsEventuallyVisible() { + return this.props.chatRoom.isCurrentlyActive; + } + UNSAFE_componentWillMount() { + let _chatRoom$messagesBuf; + const { + chatRoom + } = this.props; + chatRoom.rebind('onHistoryDecrypted.cp', () => this.eventuallyUpdate()); + this._messagesBuffChangeHandler = (_chatRoom$messagesBuf = chatRoom.messagesBuff) == null ? void 0 : _chatRoom$messagesBuf.addChangeListener(SoonFc(() => { + if (this.isComponentEventuallyVisible()) { + let _this$domRef; + $('.js-messages-scroll-area', (_this$domRef = this.domRef) == null ? void 0 : _this$domRef.current).trigger('forceResize', [true]); + } + this.refreshUI(); + })); + } + componentDidMount() { + super.componentDidMount(); + const { + chatRoom, + onMount + } = this.props; + window.addEventListener('resize', this.handleWindowResize); + window.addEventListener('keydown', this.handleKeyDown); + this.$container = $(`.conversation-panel[data-room-id="${chatRoom.chatId}"]`); + this.eventuallyInit(); + chatRoom.trigger('onHistoryPanelComponentDidMount'); + if (onMount) { + onMount(this); + } + } + componentWillUnmount() { + super.componentWillUnmount(); + const { + chatRoom + } = this.props; + if (this._messagesBuffChangeHandler) { + let _chatRoom$messagesBuf2; + (_chatRoom$messagesBuf2 = chatRoom.messagesBuff) == null || _chatRoom$messagesBuf2.removeChangeListener(this._messagesBuffChangeHandler); + delete this._messagesBuffChangeHandler; + } + window.removeEventListener('resize', this.handleWindowResize); + window.removeEventListener('keydown', this.handleKeyDown); + $(document).off(`fullscreenchange.megaChat_${chatRoom.roomId}`); + $(document).off(`keydown.keyboardScroll_${chatRoom.roomId}`); + } + componentDidUpdate(prevProps, prevState) { + let _self$domRef; + const self = this; + self.eventuallyInit(false); + const domNode = (_self$domRef = self.domRef) == null ? void 0 : _self$domRef.current; + const jml = domNode && domNode.querySelector('.js-messages-loading'); + if (jml) { + if (self.loadingShown) { + jml.classList.remove('hidden'); + } else { + jml.classList.add('hidden'); + } + } + self.handleWindowResize(); + if (prevState.editing === false && self.state.editing !== false && self.messagesListScrollable) { + self.messagesListScrollable.reinitialise(false); + Soon(() => { + if (self.editDomElement && self.editDomElement.length === 1) { + self.messagesListScrollable.scrollToElement(self.editDomElement[0], false); + } + }); + } + if (self._reposOnUpdate !== undefined) { + const ps = self.messagesListScrollable; + ps.__prevPosY = ps.getScrollHeight() - self._reposOnUpdate + self.lastScrollPosition; + ps.scrollToY(ps.__prevPosY, true); + } + } + eventuallyInit(doResize) { + let _this$domRef2; + if (this.initialised) { + return; + } + const domNode = (_this$domRef2 = this.domRef) == null ? void 0 : _this$domRef2.current; + if (domNode) { + this.initialised = true; + } else { + return; + } + this.$messages = $('.messages.scroll-area > .perfectScrollbarContainer', this.$container); + this.$messages.droppable({ + tolerance: 'pointer', + drop(e, ui) { + $.doDD(e, ui, 'drop', 1); + }, + over(e, ui) { + $.doDD(e, ui, 'over', 1); + }, + out(e, ui) { + $.doDD(e, ui, 'out', 1); + } + }); + this.lastScrollPosition = null; + this.props.chatRoom.scrolledToBottom = true; + if (doResize !== false) { + this.handleWindowResize(); + } + } + _handleWindowResize(e, scrollToBottom) { + if (!M.chat) { + return; + } + if (!this.isMounted()) { + this.componentWillUnmount(); + return; + } + if (!this.isComponentEventuallyVisible()) { + return; + } + const self = this; + self.eventuallyInit(false); + if (!self.$messages) { + return; + } + if ((0,meetings_utils.Av)()) { + const $container = $('.meetings-call'); + const $messages = $('.js-messages-scroll-area', $container); + const $textarea = $('.chat-textarea-block', $container); + const $sidebar = $('.sidebar', $container); + const scrollBlockHeight = parseInt($sidebar.outerHeight(), 10) - parseInt($textarea.outerHeight(), 10) - 72; + if ($sidebar.hasClass('chat-opened') && scrollBlockHeight !== $messages.outerHeight()) { + $messages.css('height', scrollBlockHeight); + self.refreshUI(true); + } + return; + } + const scrollBlockHeight = $('.chat-content-block', self.$container).outerHeight() - ($('.chat-topic-block', self.$container).outerHeight() || 0) - (is_chatlink ? $('.join-chat-block', self.$container).outerHeight() : $('.messages-block .chat-textarea-block', self.$container).outerHeight()); + if (scrollBlockHeight !== self.$messages.outerHeight()) { + self.$messages.css('height', scrollBlockHeight); + $('.messages.main-pad', self.$messages).css('min-height', scrollBlockHeight); + self.refreshUI(true); + } else { + self.refreshUI(scrollToBottom); + } + } + refreshUI() { + if (this.isComponentEventuallyVisible()) { + const room = this.props.chatRoom; + room.renderContactTree(); + room.megaChat.refreshConversations(); + room.trigger('RefreshUI'); + if (room.scrolledToBottom) { + delay(`hp:reinit-scroll:${this.getUniqueId()}`, () => { + if (this.messagesListScrollable) { + this.messagesListScrollable.reinitialise(true, true); + } + }, 30); + } + } + } + isLoading() { + const {chatRoom} = this.props; + if (chatRoom.historyTimedOut) { + return false; + } + const mb = chatRoom.messagesBuff; + return this.scrollPullHistoryRetrieval === true || chatRoom.activeSearches || mb.messagesHistoryIsLoading() || mb.joined === false || mb.isDecrypting; + } + specShouldComponentUpdate() { + return !this.loadingShown && this.isComponentEventuallyVisible(); + } + enableScrollbar() { + const ps = this.messagesListScrollable; + ps.enable(); + this._reposOnUpdate = undefined; + this.lastScrollPosition = ps.__prevPosY | 0; + } + editMessage(messageId) { + const self = this; + self.setState({ + 'editing': messageId + }); + self.props.chatRoom.scrolledToBottom = false; + } + onMessageEditDone(v, messageContents) { + const self = this; + const room = this.props.chatRoom; + room.scrolledToBottom = true; + self.editDomElement = null; + const currentContents = v.textContents; + v.edited = false; + if (messageContents === false || messageContents === currentContents) { + let _self$messagesListScr; + (_self$messagesListScr = self.messagesListScrollable) == null || _self$messagesListScr.scrollToBottom(true); + } else if (messageContents) { + let _self$messagesListScr2; + room.trigger('onMessageUpdating', v); + room.megaChat.plugins.chatdIntegration.updateMessage(room, v.internalId ? v.internalId : v.orderValue, messageContents); + if (v.getState && (v.getState() === Message.STATE.NOT_SENT || v.getState() === Message.STATE.SENT) && !v.requiresManualRetry) { + if (v.textContents) { + v.textContents = messageContents; + } + if (v.emoticonShortcutsProcessed) { + v.emoticonShortcutsProcessed = false; + } + if (v.emoticonsProcessed) { + v.emoticonsProcessed = false; + } + if (v.messageHtml) { + delete v.messageHtml; + } + v.trigger('onChange', [v, "textContents", "", messageContents]); + megaChat.plugins.richpreviewsFilter.processMessage({}, v, false, true); + } + (_self$messagesListScr2 = self.messagesListScrollable) == null || _self$messagesListScr2.scrollToBottom(true); + } else if (messageContents.length === 0) { + this.props.onDeleteClicked(v); + } + self.setState({ + 'editing': false + }); + self.refreshUI(); + Soon(() => { + $('.chat-textarea-block:visible textarea').focus(); + }, 300); + } + render() { + const self = this; + const room = this.props.chatRoom; + if (!room || !room.roomId) { + return null; + } + const contacts = room.getParticipantsExceptMe(); + let contactHandle; + let contact; + let avatarMeta; + let contactName = ""; + if (contacts && contacts.length === 1) { + contactHandle = contacts[0]; + contact = M.u[contactHandle]; + avatarMeta = contact ? generateAvatarMeta(contact.u) : {}; + contactName = avatarMeta.fullName; + } else if (contacts && contacts.length > 1) { + contactName = room.getRoomTitle(); + } + let messagesList = []; + if (this.isLoading()) { + self.loadingShown = true; + } else { + const mb = room.messagesBuff; + if (this.scrollPullHistoryRetrieval < 0) { + this.scrollPullHistoryRetrieval = false; + self.enableScrollbar(); + } + delete self.loadingShown; + if (room.historyTimedOut || mb.joined === true && !self.scrollPullHistoryRetrieval && mb.haveMoreHistory() === false) { + const $$WELCOME_MESSAGE = ({ + heading, + title, + info, + className + }) => JSX_("div", { + className: ` + messages + welcome-message + ${className || ''} + ` + }, JSX_(utils.P9, { + tag: "h1", + content: heading + }), title && JSX_("span", null, title), info); + messagesList = [...messagesList, room.isNote ? $$WELCOME_MESSAGE({ + heading: l.note_heading, + info: JSX_("p", null, JSX_("i", { + className: "sprite-fm-mono icon-file-text-thin-outline note-chat-icon" + }), l.note_description), + className: 'note-chat-info' + }) : $$WELCOME_MESSAGE({ + heading: room.scheduledMeeting || !contactName ? megaChat.html(room.getRoomTitle()) : l[8002].replace('%s', `${megaChat.html(contactName)}`), + title: l[8080], + info: JSX_(REaCt().Fragment, null, JSX_("p", null, JSX_("i", { + className: "sprite-fm-mono icon-lock" + }), JSX_(utils.P9, { + content: l[8540].replace("[S]", "").replace("[/S]", "") + })), JSX_("p", null, JSX_("i", { + className: "sprite-fm-mono icon-accept" + }), JSX_(utils.P9, { + content: l[8539].replace("[S]", "").replace("[/S]", "") + }))) + })]; + } + } + let lastTimeMarker; + let lastMessageFrom = null; + let lastGroupedMessageTimeStamp = null; + let grouped = false; + for (let i = 0; i < room.messagesBuff.messages.length; i++) { + let v = room.messagesBuff.messages.getItem(i); + if (!v.protocol && v.revoked !== true) { + let shouldRender = true; + if (v.isManagement && v.isManagement() === true && v.isRenderableManagement() === false || v.deleted === true) { + shouldRender = false; + } + const timestamp = v.delay; + const curTimeMarker = getTimeMarker(timestamp); + if (shouldRender === true && curTimeMarker && lastTimeMarker !== curTimeMarker) { + lastTimeMarker = curTimeMarker; + messagesList.push(JSX_("div", { + className: "message date-divider selectable-txt", + key: `${v.messageId }_marker`, + title: time2date(timestamp) + }, curTimeMarker)); + grouped = false; + lastMessageFrom = null; + lastGroupedMessageTimeStamp = null; + } + if (shouldRender === true) { + let {userId} = v; + if (!userId && contact && contact.u) { + userId = contact.u; + } + if (v instanceof Message && v.dialogType !== "truncated") { + if (!lastMessageFrom || userId && lastMessageFrom === userId) { + if (timestamp - lastGroupedMessageTimeStamp < 300) { + grouped = true; + } else { + grouped = false; + lastMessageFrom = userId; + lastGroupedMessageTimeStamp = timestamp; + } + } else { + grouped = false; + lastMessageFrom = userId; + if (lastMessageFrom === userId) { + lastGroupedMessageTimeStamp = timestamp; + } else { + lastGroupedMessageTimeStamp = null; + } + } + } else { + grouped = false; + lastMessageFrom = null; + lastGroupedMessageTimeStamp = null; + } + } + if ((v.dialogType === "remoteCallEnded" || v.dialogType === "remoteCallStarted") && v && v.wrappedChatDialogMessage) { + v = v.wrappedChatDialogMessage; + } + if (v.dialogType) { + let messageInstance = null; + if (v.dialogType === 'alterParticipants') { + messageInstance = JSX_(AltPartsConvMessage, { + message: v, + key: v.messageId, + contact: Message.getContactForMessage(v), + grouped, + chatRoom: room + }); + } else if (v.dialogType === 'truncated') { + messageInstance = JSX_(TruncatedMessage, { + message: v, + key: v.messageId, + contact: Message.getContactForMessage(v), + grouped, + chatRoom: room + }); + } else if (v.dialogType === 'privilegeChange') { + messageInstance = JSX_(PrivilegeChange, { + message: v, + key: v.messageId, + contact: Message.getContactForMessage(v), + grouped, + chatRoom: room + }); + } else if (v.dialogType === 'topicChange') { + messageInstance = JSX_(TopicChange, { + message: v, + key: v.messageId, + contact: Message.getContactForMessage(v), + grouped, + chatRoom: room + }); + } else if (v.dialogType === 'openModeClosed') { + messageInstance = JSX_(CloseOpenModeMessage, { + message: v, + key: v.messageId, + contact: Message.getContactForMessage(v), + grouped, + chatRoom: room + }); + } else if (v.dialogType === 'chatHandleUpdate') { + messageInstance = JSX_(ChatHandleMessage, { + message: v, + key: v.messageId, + contact: Message.getContactForMessage(v), + grouped, + chatRoom: room + }); + } else if (v.dialogType === 'messageRetention') { + messageInstance = JSX_(RetentionChange, { + message: v, + key: v.messageId, + contact: Message.getContactForMessage(v) + }); + } else if (v.dialogType === 'scheduleMeta') { + if (v.meta.onlyTitle) { + messageInstance = JSX_(TopicChange, { + message: v, + key: v.messageId, + contact: Message.getContactForMessage(v), + grouped, + chatRoom: v.chatRoom + }); + } else { + if (v.meta.topicChange) { + messagesList.push(JSX_(TopicChange, { + message: v, + key: `${v.messageId}-topic`, + contact: Message.getContactForMessage(v), + grouped, + chatRoom: v.chatRoom + })); + } + messageInstance = JSX_(scheduleMetaChange.A, { + message: v, + key: v.messageId, + mode: v.meta.mode, + chatRoom: room, + grouped, + link: v.chatRoom.publicLink, + contact: Message.getContactForMessage(v) + }); + } + } + messagesList.push(messageInstance); + } else { + if (!v.chatRoom) { + v.chatRoom = room; + } + messagesList.push(JSX_(generic.A, { + message: v, + state: v.state, + key: v.messageId, + contact: Message.getContactForMessage(v), + grouped, + onUpdate: () => { + self.onResizeDoUpdate(); + }, + editing: self.state.editing === v.messageId || self.state.editing === v.pendingMessageId, + onEditStarted: ((v, $domElement) => { + self.editDomElement = $domElement; + self.setState({ + 'editing': v.messageId + }); + self.forceUpdate(); + }).bind(this, v), + chatRoom: room, + onEditDone: this.onMessageEditDone.bind(this, v), + onDeleteClicked: msg => { + if (this.props.onDeleteClicked) { + this.props.onDeleteClicked(msg); + } + }, + onResized: () => { + this.handleWindowResize(); + }, + onEmojiBarChange: () => { + this.handleWindowResize(); + } + })); + } + } + } + return JSX_("div", { + ref: this.domRef, + className: ` + messages + scroll-area + ${this.props.className || ''} + ` + }, JSX_(perfectScrollbar.O, { + className: "js-messages-scroll-area perfectScrollbarContainer", + ref: ref => { + let _this$props$onMessage, _this$props; + this.messagesListScrollable = ref; + $(document).rebind(`keydown.keyboardScroll_${room.roomId}`, this.onKeyboardScroll); + (_this$props$onMessage = (_this$props = this.props).onMessagesListScrollableMount) == null || _this$props$onMessage.call(_this$props, ref); + }, + chatRoom: room, + messagesBuff: room.messagesBuff, + editDomElement: this.state.editDomElement, + editingMessageId: this.state.editing, + confirmDeleteDialog: this.state.confirmDeleteDialog, + renderedMessagesCount: messagesList.length, + options: { + suppressScrollX: true + }, + isLoading: room.messagesBuff.messagesHistoryIsLoading() || room.activeSearches > 0 || this.loadingShown, + onFirstInit: ps => { + ps.scrollToBottom(true); + room.scrolledToBottom = 1; + }, + onUserScroll: this.onMessagesScrollUserScroll + }, JSX_("div", { + className: "messages main-pad" + }, JSX_("div", { + className: "messages content-area" + }, this.renderLoadingSpinner(), messagesList))), this.renderNavigationToast()); + } +}, (0,applyDecoratedDescriptor.A)(_class.prototype, "enableScrollbar", [_dec], Object.getOwnPropertyDescriptor(_class.prototype, "enableScrollbar"), _class.prototype), _class); + + + }, + + 8956 +(_, EXP_, REQ_) { + + REQ_.d(EXP_, { + Q: () => InviteParticipantsPanel + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _meetings_utils_jsx1__ = REQ_(3901); + const _ui_miniui_jsx2__ = REQ_(5009); + const _ui_buttons_jsx3__ = REQ_(5155); + const _ui_dropdowns_jsx4__ = REQ_(1510); + + + + + +const NAMESPACE = 'invite-panel'; +class InviteParticipantsPanel extends react0___default().Component { + constructor(props) { + super(props); + this.domRef = react0___default().createRef(); + this.state = { + link: '', + copied: false + }; + this.retrieveChatLink(); + } + retrieveChatLink(cim) { + const { + chatRoom + } = this.props; + if (!chatRoom.topic) { + return; + } + this.loading = chatRoom.updatePublicHandle(false, cim).always(() => { + delete this.loading; + if (this.domRef.current) { + if (chatRoom.publicLink) { + this.setState({ + link: `${getBaseUrl()}/${chatRoom.publicLink}` + }); + } else { + this.setState({ + link: false + }); + } + } + }); + } + getInviteBody(encode) { + const { + chatRoom + } = this.props; + const { + link + } = this.state; + const { + scheduledMeeting + } = chatRoom; + let body = l.invite_body_text; + if (scheduledMeeting) { + const { + nextOccurrenceStart + } = chatRoom.scheduledMeeting; + body = l.invite_body_text_scheduled.replace('%4', time2date(nextOccurrenceStart / 1000, 20)).replace('%5', toLocaleTime(nextOccurrenceStart)); + } + body = body.replace(/\[BR]/g, '\n').replace('%1', u_attr.name).replace('%2', chatRoom.getRoomTitle()).replace('%3', link); + if (encode) { + return typeof body.toWellFormatted === 'function' ? body.toWellFormatted() : body.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, '').replace(/[\uD800-\uDBFF]/g, '\uFFFD').replace(/[\uDC00-\uDFFF]/g, '\uFFFD'); + } + return body; + } + render() { + const { + chatRoom, + disableLinkToggle, + onAddParticipants + } = this.props; + const { + link, + copied + } = this.state; + const inCall = (0,_meetings_utils_jsx1__ .Av)(); + if (this.loading) { + return JSX_("div", { + ref: this.domRef, + className: ` + ${NAMESPACE} + ${inCall ? 'theme-dark-forced' : ''} + ` + }, JSX_("header", null), JSX_("section", { + className: "content" + }, JSX_("div", { + className: "content-block" + }))); + } + const canInvite = !!(chatRoom.iAmOperator() || chatRoom.options[MCO_FLAGS.OPEN_INVITE]) && onAddParticipants; + const canToggleLink = !disableLinkToggle && chatRoom.iAmOperator() && (chatRoom.isMeeting || chatRoom.topic); + const mailto = `mailto:?to=&subject=${l.invite_subject_text}&body=${this.getInviteBody(true)}`; + const copyText = chatRoom.isMeeting ? l.copy_meeting_link : l[1394]; + return JSX_("div", { + ref: this.domRef, + className: ` + ${NAMESPACE} + ${inCall ? 'theme-dark-forced' : ''} + ` + }, JSX_("header", null, JSX_("h3", null, l.invite_participants)), JSX_("section", { + className: "content" + }, canToggleLink && chatRoom.type !== 'group' && JSX_("div", { + className: "content-block link-block" + }, JSX_("div", { + className: "text-wrapper" + }, JSX_("span", { + className: "link-label" + }, l.invite_toggle_link_label), JSX_("div", { + className: `link-description ${inCall ? '' : 'hidden'}` + }, l.invite_toggle_link_desc)), JSX_(_ui_miniui_jsx2__ .A.ToggleCheckbox, { + className: "meeting-link-toggle", + checked: !!link, + value: !!link, + onToggle: () => { + if (this.loading) { + return; + } + if (link) { + this.loading = chatRoom.updatePublicHandle(true).always(() => { + delete this.loading; + if (this.domRef.current) { + this.setState({ + link: false + }); + } + }); + } else { + this.retrieveChatLink(true); + } + } + })), link && JSX_("div", { + className: "content-block" + }, JSX_(_ui_buttons_jsx3__ .$, { + className: "flat-button", + icon: `sprite-fm-mono icon-${copied ? 'check' : 'link-thin-outline'}`, + label: copied ? l.copied : copyText, + onClick: () => { + if (copied) { + return; + } + delay('chat-event-inv-copylink', () => eventlog(99964)); + copyToClipboard(link); + this.setState({ + copied: true + }, () => { + tSleep(3).then(() => { + if (this.domRef.current) { + this.setState({ + copied: false + }); + } + }); + }); + } + })), link && JSX_("div", { + className: "content-block" + }, JSX_(_ui_buttons_jsx3__ .$, { + className: "flat-button", + label: l.share_chat_link, + icon: "sprite-fm-mono icon-share-02-thin-outline" + }, JSX_(_ui_dropdowns_jsx4__ .ms, { + className: ` + button-group-menu + invite-dropdown + ${inCall ? 'theme-dark-forced' : ''} + `, + noArrow: true, + positionAt: "left bottom", + collision: "none", + horizOffset: 79, + vertOffset: 6, + ref: r => { + this.dropdownRef = r; + }, + onBeforeActiveChange: e => { + if (e) { + delay('chat-event-inv-dropdown', () => eventlog(99965)); + $(document.body).trigger('closeAllDropdownsExcept', this.dropdownRef); + } + } + }, JSX_(_ui_dropdowns_jsx4__ .tJ, { + key: "send-invite", + className: ` + ${inCall ? 'theme-dark-forced' : ''} + `, + icon: "sprite-fm-mono icon-mail-thin-outline", + label: l.share_chat_link_invite, + onClick: () => { + delay('chat-event-inv-email', () => eventlog(99966)); + window.open(mailto, '_self', 'noopener,noreferrer'); + } + }), JSX_(_ui_dropdowns_jsx4__ .tJ, { + key: "copy-invite", + className: ` + ${inCall ? 'theme-dark-forced' : ''} + `, + label: l.copy_chat_link_invite, + icon: "sprite-fm-mono icon-square-copy", + onClick: () => { + delay('chat-event-inv-copy', () => eventlog(99967)); + copyToClipboard(this.getInviteBody(), l.invite_copied); + } + })))), canInvite && (link || canToggleLink) && chatRoom.type !== 'group' && JSX_("div", { + className: "content-block invite-panel-divider" + }, l.invite_dlg_divider), canInvite && JSX_("div", { + className: "content-block add-participant-block" + }, JSX_(_ui_buttons_jsx3__ .$, { + className: "flat-button", + icon: "sprite-fm-mono icon-user-square-thin-outline", + label: l.add_participants, + onClick: () => { + delay('chat-event-inv-add-participant', () => eventlog(99968)); + onAddParticipants(); + } + })))); + } +} + + }, + + 4907 +(_, EXP_, REQ_) { + +// ESM COMPAT FLAG +REQ_.r(EXP_); + +// EXPORTS +REQ_.d(EXP_, { + "default": () => leftPanel +}); + +// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/extends.js +const esm_extends = REQ_(8168); +// EXTERNAL MODULE: external "React" +const external_React_ = REQ_(1594); +const REaCt = REQ_.n(external_React_); +// EXTERNAL MODULE: ./js/chat/mixins.js +const mixins = REQ_(8264); +;// ./js/chat/ui/searchPanel/utils.jsx +const STATUS = { + IN_PROGRESS: 1, + PAUSED: 2, + COMPLETED: 3 +}; +const EVENTS = { + RESULT_OPEN: 'chatSearchResultOpen', + KEYDOWN: 'keydown' +}; +const ACTIONS = { + PAUSE: 'pause', + RESUME: 'resume' +}; +const TYPE = { + MESSAGE: 1, + CHAT: 2, + MEMBER: 3, + NIL: 4 +}; +const LABEL = { + MESSAGES: l[6868], + CONTACTS_AND_CHATS: l[20174], + NO_RESULTS: l[8674], + SEARCH_MESSAGES_CTA: l[23547], + SEARCH_MESSAGES_INLINE: l[23548], + DECRYPTING_RESULTS: l[23543], + PAUSE_SEARCH: l[23544], + SEARCH_PAUSED: l[23549], + SEARCH_COMPLETE: l[23546] +}; +;// ./js/chat/ui/searchPanel/searchField.jsx +let _SearchField; + + + +const SEARCH_STATUS_CLASS = 'search-field-status'; +const BASE_ICON_CLASS = 'sprite-fm-mono'; +class SearchField extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + this.state = { + hovered: false + }; + this.renderStatusBanner = () => { + switch (this.props.status) { + case STATUS.IN_PROGRESS: + return JSX_("div", { + className: `${SEARCH_STATUS_CLASS} searching info` + }, LABEL.DECRYPTING_RESULTS); + case STATUS.PAUSED: + return JSX_("div", { + className: `${SEARCH_STATUS_CLASS} paused info` + }, LABEL.SEARCH_PAUSED); + case STATUS.COMPLETED: + return JSX_("div", { + className: `${SEARCH_STATUS_CLASS} complete success` + }, LABEL.SEARCH_COMPLETE); + default: + return null; + } + }; + this.renderStatusControls = () => { + const { + status, + onToggle + } = this.props; + const handleHover = () => this.setState(state => ({ + hovered: !state.hovered + })); + switch (status) { + case STATUS.IN_PROGRESS: + return JSX_("div", { + className: "progress-controls", + onClick: onToggle + }, JSX_("i", { + className: `${BASE_ICON_CLASS} icon-pause` + })); + case STATUS.PAUSED: + return JSX_("i", { + className: `${BASE_ICON_CLASS} icon-resume`, + onClick: onToggle, + onMouseOver: handleHover, + onMouseOut: handleHover + }); + case STATUS.COMPLETED: + return null; + default: + return null; + } + }; + } + componentDidMount() { + super.componentDidMount(); + SearchField.focus(); + } + render() { + const { + value, + searching, + status, + onChange, + onReset + } = this.props; + return JSX_("div", { + ref: this.domRef, + className: "search-field" + }, JSX_("i", { + className: `${BASE_ICON_CLASS} icon-preview-reveal search-icon-find` + }), JSX_("input", { + type: "search", + autoComplete: "off", + placeholder: l[102], + ref: SearchField.inputRef, + value, + onChange: ev => { + if (this.state.hovered) { + this.setState({ + hovered: false + }); + } + onChange(ev); + } + }), searching && JSX_("i", { + className: ` + ${BASE_ICON_CLASS} + icon-close-component + search-icon-reset + `, + onClick: onReset + }), searching && status && JSX_(REaCt().Fragment, null, this.renderStatusControls(), this.renderStatusBanner())); + } +} +_SearchField = SearchField; +SearchField.inputRef = REaCt().createRef(); +SearchField.select = () => { + const inputElement = _SearchField.inputRef && _SearchField.inputRef.current; + const value = inputElement && inputElement.value; + if (inputElement && value) { + inputElement.selectionStart = 0; + inputElement.selectionEnd = value.length; + } +}; +SearchField.focus = () => _SearchField.inputRef && _SearchField.inputRef.current && _SearchField.inputRef.current.focus(); +SearchField.hasValue = () => _SearchField.inputRef && _SearchField.inputRef.current && !!_SearchField.inputRef.current.value.length; +SearchField.isVisible = () => _SearchField.inputRef && _SearchField.inputRef.current && elementIsVisible(_SearchField.inputRef.current); +;// ./js/chat/ui/searchPanel/resultTable.jsx + +const ResultTable = ({ + heading, + children +}) => { + return JSX_("div", { + className: `result-table ${heading ? '' : 'nil'}` + }, heading ? JSX_("div", { + className: "result-table-heading" + }, heading) : null, children); +}; + const resultTable = ResultTable; +// EXTERNAL MODULE: ./js/chat/ui/contacts.jsx +const contacts = REQ_(8022); +// EXTERNAL MODULE: ./js/ui/utils.jsx +const utils = REQ_(6411); +;// ./js/chat/ui/searchPanel/resultRow.jsx + + + + + + + +const RESULT_ROW_CLASS = 'result-table-row'; +const USER_CARD_CLASS = 'user-card'; +const roomIsGroup = room => room && room.type === 'group' || room.type === 'public'; +const openResult = ({ + room, + messageId, + index +}, callback) => { + document.dispatchEvent(new Event(EVENTS.RESULT_OPEN)); + if (isString(room)) { + loadSubPage(`fm/chat/p/${room}`); + } else if (room && room.chatId && !messageId) { + const chatRoom = megaChat.getChatById(room.chatId); + if (chatRoom) { + loadSubPage(chatRoom.getRoomUrl()); + } else { + loadSubPage(`/fm/chat/contacts/${room.chatId}`); + } + } else { + loadSubPage(room.getRoomUrl()); + if (messageId) { + room.scrollToMessageId(messageId, index); + } + } + return callback && typeof callback === 'function' && callback(); +}; +const lastActivity = room => { + if (!room.lastActivity || !room.ctime) { + room = megaChat.getChatById(room.chatId); + } + if (room && room.lastActivity || room.ctime) { + return room.lastActivity ? todayOrYesterday(room.lastActivity * 1000) ? getTimeMarker(room.lastActivity) : time2date(room.lastActivity, 17) : todayOrYesterday(room.ctime * 1000) ? getTimeMarker(room.ctime) : time2date(room.ctime, 17); + } + return l[8000]; +}; +class MessageRow extends mixins.w9 { + render() { + const { + data, + matches, + room, + index, + onResultOpen + } = this.props; + const isGroup = roomIsGroup(room); + const contact = room.getParticipantsExceptMe(); + const summary = room.messagesBuff.getRenderableSummary(data); + const date = todayOrYesterday(data.delay * 1000) ? getTimeMarker(data.delay) : time2date(data.delay, 17); + return JSX_("div", { + ref: node => { + this.domRef = node; + }, + className: ` + ${RESULT_ROW_CLASS} + message + `, + onClick: () => openResult({ + room, + messageId: data.messageId, + index + }, () => onResultOpen(this.domRef)) + }, JSX_("div", { + className: "message-result-avatar" + }, isGroup && JSX_("div", { + className: "chat-topic-icon" + }, JSX_("i", { + className: "sprite-fm-uni icon-chat-group" + })), room.isNote && JSX_("div", { + className: "note-chat-signifier" + }, JSX_("i", { + className: "sprite-fm-mono icon-file-text-thin-outline note-chat-icon" + })), room.type === 'private' && JSX_(contacts.eu, { + contact: M.u[contact] + })), JSX_("div", { + className: "user-card" + }, JSX_("span", { + className: "title" + }, isGroup && JSX_(utils.sp, null, room.getRoomTitle()), room.isNote && JSX_("span", null, l.note_label), room.type === 'private' && JSX_(contacts.uA, { + contact: M.u[contact], + overflow: true + })), isGroup ? null : JSX_(contacts.i1, { + contact: M.u[contact] + }), JSX_("div", { + className: "clear" + }), JSX_("div", { + className: "message-result-info" + }, JSX_("div", { + className: "summary" + }, JSX_(utils.oM, { + content: megaChat.highlight(summary, matches, true) + })), JSX_("div", { + className: "result-separator" + }, JSX_("i", { + className: "sprite-fm-mono icon-dot" + })), JSX_("span", { + className: "date" + }, date)))); + } +} +class ChatRow extends mixins.w9 { + render() { + const { + room, + matches, + onResultOpen + } = this.props; + const result = megaChat.highlight(megaChat.html(room.getRoomTitle()), matches, true); + return JSX_("div", { + ref: node => { + this.domRef = node; + }, + className: RESULT_ROW_CLASS, + onClick: () => openResult({ + room + }, () => onResultOpen(this.domRef)) + }, JSX_("div", { + className: "chat-topic-icon" + }, JSX_("i", { + className: "sprite-fm-uni icon-chat-group" + })), JSX_("div", { + className: USER_CARD_CLASS + }, JSX_("div", { + className: "graphic" + }, JSX_(utils.oM, null, result)), JSX_("div", { + className: "result-last-activity" + }, lastActivity(room))), JSX_("div", { + className: "clear" + })); + } +} +class MemberRow extends mixins.w9 { + render() { + const { + data, + matches, + room, + contact, + onResultOpen + } = this.props; + const isGroup = room && roomIsGroup(room); + return JSX_("div", { + ref: node => { + this.domRef = node; + }, + className: RESULT_ROW_CLASS, + onClick: () => openResult({ + room: room || contact.h + }, () => onResultOpen(this.domRef)) + }, isGroup ? JSX_("div", { + className: "chat-topic-icon" + }, JSX_("i", { + className: "sprite-fm-uni icon-chat-group" + })) : JSX_(contacts.eu, { + contact + }), JSX_("div", { + className: USER_CARD_CLASS + }, JSX_("div", { + className: "graphic" + }, isGroup ? JSX_(utils.oM, null, megaChat.highlight(megaChat.html(room.getRoomTitle()), matches, true)) : JSX_(REaCt().Fragment, null, JSX_(utils.oM, null, megaChat.highlight(megaChat.html(nicknames.getNickname(data)), matches, true)), JSX_(contacts.i1, { + contact + }))), lastActivity(room)), JSX_("div", { + className: "clear" + })); + } +} +const NilRow = ({ + onSearchMessages, + isFirstQuery +}) => { + const label = LABEL.SEARCH_MESSAGES_INLINE.replace('[A]', '').replace('[/A]', ''); + return JSX_("div", { + className: ` + ${RESULT_ROW_CLASS} + nil + ` + }, JSX_("div", { + className: "nil-container" + }, JSX_("i", { + className: "sprite-fm-mono icon-preview-reveal" + }), JSX_("span", null, LABEL.NO_RESULTS), isFirstQuery && JSX_("div", { + className: "search-messages", + onClick: onSearchMessages + }, JSX_(utils.oM, { + tag: "div", + content: label + })))); +}; +class ResultRow extends mixins.w9 { + constructor(...args) { + super(...args); + this.setActive = nodeRef => { + if (nodeRef) { + const elements = document.querySelectorAll(`.${RESULT_ROW_CLASS}.${"active"}`); + for (let i = elements.length; i--;) { + elements[i].classList.remove('active'); + } + nodeRef.classList.add("active"); + } + }; + } + render() { + const { + type, + result, + children, + onSearchMessages, + isFirstQuery + } = this.props; + if (result) { + const { + data, + index, + matches, + room + } = result; + const PROPS = { + data, + index, + matches, + room, + onResultOpen: this.setActive + }; + switch (type) { + case TYPE.MESSAGE: + return JSX_(MessageRow, PROPS); + case TYPE.CHAT: + return JSX_(ChatRow, PROPS); + case TYPE.MEMBER: + return JSX_(MemberRow, (0,esm_extends.A)({}, PROPS, { + contact: M.u[data] + })); + default: + return JSX_("div", { + className: RESULT_ROW_CLASS + }, children); + } + } + return JSX_(NilRow, { + onSearchMessages, + isFirstQuery + }); + } +} +;// ./js/chat/ui/searchPanel/resultContainer.jsx + + + + +class ResultContainer extends REaCt().Component { + constructor(...args) { + super(...args); + this.renderResults = (results, status, isFirstQuery, onSearchMessages) => { + if (status === STATUS.COMPLETED && results.length < 1) { + return JSX_(resultTable, null, JSX_(ResultRow, { + type: TYPE.NIL, + isFirstQuery, + onSearchMessages + })); + } + const RESULT_TABLE = { + CONTACTS_AND_CHATS: [], + MESSAGES: [] + }; + for (const resultTypeGroup in results) { + if (results.hasOwnProperty(resultTypeGroup)) { + const len = results[resultTypeGroup].length; + for (let i = 0; i < len; i++) { + const result = results[resultTypeGroup].getItem(i); + const { + MESSAGE, + MEMBER, + CHAT + } = TYPE; + const { + resultId, + type + } = result; + const table = type === MESSAGE ? 'MESSAGES' : 'CONTACTS_AND_CHATS'; + RESULT_TABLE[table] = [...RESULT_TABLE[table], JSX_(ResultRow, { + key: resultId, + type: type === MESSAGE ? MESSAGE : type === MEMBER ? MEMBER : CHAT, + result + })]; + } + } + } + return Object.keys(RESULT_TABLE).map((key, index) => { + const table = { + ref: RESULT_TABLE[key], + hasRows: RESULT_TABLE[key] && RESULT_TABLE[key].length, + isEmpty: RESULT_TABLE[key] && RESULT_TABLE[key].length < 1, + props: { + key: index, + heading: key === 'MESSAGES' ? LABEL.MESSAGES : LABEL.CONTACTS_AND_CHATS + } + }; + if (table.hasRows) { + return JSX_(resultTable, table.props, table.ref.map(row => row)); + } + if (status === STATUS.COMPLETED && key === 'MESSAGES') { + const SEARCH_MESSAGES = JSX_("button", { + className: "search-messages mega-button", + onClick: onSearchMessages + }, JSX_("span", null, LABEL.SEARCH_MESSAGES_CTA)); + const NO_RESULTS = JSX_(ResultRow, { + type: TYPE.NIL, + isFirstQuery, + onSearchMessages + }); + return JSX_(resultTable, table.props, isFirstQuery ? SEARCH_MESSAGES : NO_RESULTS); + } + return null; + }); + }; + } + render() { + const { + results, + status, + isFirstQuery, + onSearchMessages + } = this.props; + return this.renderResults(results, status, isFirstQuery, onSearchMessages); + } +} +// EXTERNAL MODULE: ./js/ui/perfectScrollbar.jsx +const perfectScrollbar = REQ_(1301); +;// ./js/chat/ui/searchPanel/searchPanel.jsx + + + + + + +const SEARCH_PANEL_CLASS = `search-panel`; +class SearchPanel extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = (0,external_React_.createRef)(); + this.wrapperRef = null; + this.state = { + value: '', + searching: false, + status: undefined, + isFirstQuery: true, + results: [] + }; + this.unbindEvents = () => { + if (this.pageChangeListener) { + mBroadcaster.removeListener(this.pageChangeListener); + } + document.removeEventListener(EVENTS.RESULT_OPEN, this.doPause); + document.removeEventListener(EVENTS.KEYDOWN, this.handleKeyDown); + megaChat.plugins.chatdIntegration.chatd.off('onClose.search'); + megaChat.plugins.chatdIntegration.chatd.off('onOpen.search'); + }; + this.bindEvents = () => { + this.pageChangeListener = mBroadcaster.addListener('pagechange', this.doPause); + document.addEventListener(EVENTS.RESULT_OPEN, this.doPause); + document.addEventListener(EVENTS.KEYDOWN, this.handleKeyDown); + megaChat.plugins.chatdIntegration.chatd.rebind('onClose.search', () => this.state.searching && this.doToggle(ACTIONS.PAUSE)); + megaChat.plugins.chatdIntegration.chatd.rebind('onOpen.search', () => this.state.searching && this.doToggle(ACTIONS.RESUME)); + }; + this.doPause = () => { + if (this.state.status === STATUS.IN_PROGRESS) { + this.doToggle(ACTIONS.PAUSE); + } + }; + this.doSearch = (s, searchMessages) => { + return ChatSearch.doSearch(s, (room, result, results) => this.setState({ + results + }), searchMessages).catch(ex => d && console.error('Search failed (or was reset)', ex)).always(() => this.setState({ + status: STATUS.COMPLETED + })); + }; + this.doToggle = (action) => { + const { + IN_PROGRESS, + PAUSED, + COMPLETED + } = STATUS; + const searching = this.state.status === IN_PROGRESS || this.state.status === PAUSED; + if (action && searching) { + const chatSearch = ChatSearch.doSearch.cs; + if (!chatSearch) { + return delay('chat-toggle', () => this.doToggle(action), 600); + } + this.setState({ + status: action === ACTIONS.PAUSE ? PAUSED : action === ACTIONS.RESUME ? IN_PROGRESS : COMPLETED + }, () => chatSearch[action]()); + } + }; + this.doDestroy = () => ChatSearch && ChatSearch.doSearch && ChatSearch.doSearch.cs && ChatSearch.doSearch.cs.destroy(); + this.handleKeyDown = ev => { + const { + keyCode + } = ev; + if (keyCode && keyCode === 27) { + return SearchField.hasValue() ? this.handleReset() : this.doPause(); + } + }; + this.handleChange = ev => { + if (SearchField.isVisible()) { + const { + value + } = ev.target; + const searching = value.length > 0; + this.doDestroy(); + this.setState({ + value, + searching, + status: undefined, + isFirstQuery: true, + results: [] + }, () => { + if (searching) { + delay('chat-search', () => this.doSearch(value, false), 1600); + if ($.dialog === 'onboardingDialog') { + closeDialog(); + } + } else { + megaChat.plugins.chatOnboarding.checkAndShowStep(); + } + }); + this.wrapperRef.scrollToY(0); + } + }; + this.handleToggle = () => { + const inProgress = this.state.status === STATUS.IN_PROGRESS; + this.setState({ + status: inProgress ? STATUS.PAUSED : STATUS.IN_PROGRESS + }, () => { + delay('chat-toggled', () => SearchField.focus()); + return this.doToggle(inProgress ? ACTIONS.PAUSE : ACTIONS.RESUME); + }); + }; + this.handleReset = () => this.setState({ + value: '', + searching: false, + status: undefined, + results: [] + }, () => { + this.wrapperRef.scrollToY(0); + onIdle(() => SearchField.focus()); + this.doDestroy(); + }); + this.handleSearchMessages = () => SearchField.hasValue() && this.setState({ + status: STATUS.IN_PROGRESS, + isFirstQuery: false + }, () => { + this.doSearch(this.state.value, true); + SearchField.focus(); + SearchField.select(); + }); + } + componentDidMount() { + super.componentDidMount(); + this.bindEvents(); + } + componentWillUnmount() { + super.componentWillUnmount(); + this.unbindEvents(); + } + render() { + const { + value, + searching, + status, + isFirstQuery, + results + } = this.state; + return JSX_("div", { + ref: this.domRef, + className: ` + ${SEARCH_PANEL_CLASS} + ${searching ? 'expanded' : ''} + ` + }, JSX_(SearchField, { + value, + searching, + status, + onChange: this.handleChange, + onToggle: this.handleToggle, + onReset: this.handleReset + }), JSX_(perfectScrollbar.O, { + className: "search-results-wrapper", + ref: wrapper => { + this.wrapperRef = wrapper; + }, + options: { + 'suppressScrollX': true + } + }, searching && JSX_(ResultContainer, { + status, + results, + isFirstQuery, + onSearchMessages: this.handleSearchMessages + }))); + } +} +// EXTERNAL MODULE: ./js/chat/ui/meetings/button.jsx +const meetings_button = REQ_(6740); +// EXTERNAL MODULE: ./js/chat/ui/leftPanel/utils.jsx +const leftPanel_utils = REQ_(4664); +;// ./js/chat/ui/leftPanel/navigation.jsx + + + +const Navigation = ({ + view, + views: { + CHATS, + MEETINGS + }, + routingSection, + unreadChats, + unreadMeetings, + contactRequests, + renderView +}) => JSX_("div", { + className: `${leftPanel_utils.C}-nav` +}, JSX_("div", { + className: ` + ${leftPanel_utils.C}-nav-container + ${leftPanel_utils.C}-chats-tab + ${view === CHATS && routingSection === 'chat' ? 'active' : ''} + `, + onClick: () => { + renderView(CHATS); + eventlog(500233); + } +}, JSX_(meetings_button.A, { + unreadChats, + className: `${leftPanel_utils.C}-nav-button`, + icon: "icon-chat-filled" +}, !!unreadChats && JSX_("div", { + className: "notifications-count" +})), JSX_("span", null, l.chats)), JSX_("div", { + className: ` + ${leftPanel_utils.C}-nav-container + ${leftPanel_utils.C}-meetings-tab + ${view === MEETINGS && routingSection === 'chat' ? 'active' : ''} + `, + onClick: () => { + renderView(MEETINGS); + eventlog(500234); + } +}, JSX_(meetings_button.A, { + unreadMeetings, + className: `${leftPanel_utils.C}-nav-button`, + icon: "icon-video-call-filled" +}, !!unreadMeetings && JSX_("div", { + className: "notifications-count" +})), JSX_("span", null, l.meetings)), is_eplusplus || is_chatlink ? null : JSX_("div", { + className: ` + ${leftPanel_utils.C}-nav-container + ${leftPanel_utils.C}-contacts-tab + ${routingSection === 'contacts' ? 'active' : ''} + `, + onClick: () => { + loadSubPage('fm/chat/contacts'); + eventlog(500296); + } +}, JSX_(meetings_button.A, { + className: `${leftPanel_utils.C}-nav-button`, + contactRequests, + icon: "icon-contacts" +}, !!contactRequests && JSX_("div", { + className: "notifications-count" +})), JSX_("span", null, l[165]))); +// EXTERNAL MODULE: ./js/ui/buttons.jsx +const buttons = REQ_(5155); +// EXTERNAL MODULE: ./js/ui/dropdowns.jsx +const dropdowns = REQ_(1510); +;// ./js/chat/ui/leftPanel/actions.jsx + + + + +const Actions = ({ + view, + views, + filter, + routingSection, + startMeeting, + scheduleMeeting, + createNewChat, + onFilter +}) => { + const { + CHATS, + MEETINGS, + LOADING + } = views; + if (is_eplusplus || is_chatlink) { + return null; + } + return JSX_("div", { + className: `${leftPanel_utils.C}-action-buttons` + }, view === LOADING && JSX_(buttons.$, { + className: "mega-button action loading-sketch" + }, JSX_("i", null), JSX_("span", null)), view === CHATS && routingSection !== 'contacts' && JSX_(REaCt().Fragment, null, JSX_(buttons.$, { + className: "mega-button small positive new-chat-action", + label: l.add_chat, + onClick: () => { + createNewChat(); + eventlog(500284); + } + }), JSX_("div", { + className: "lhp-filter" + }, JSX_("div", { + className: "lhp-filter-control" + }, JSX_(buttons.$, { + icon: "sprite-fm-mono icon-sort-thin-solid" + }, JSX_(dropdowns.ms, { + className: "light", + noArrow: "true" + }, JSX_(dropdowns.tJ, { + className: "link-button", + icon: "sprite-fm-mono icon-eye-reveal", + label: l.filter_unread, + onClick: () => onFilter(leftPanel_utils.x.UNREAD) + }), JSX_(dropdowns.tJ, { + className: "link-button", + icon: "sprite-fm-mono icon-notification-off", + label: view === MEETINGS ? l.filter_muted__meetings : l.filter_muted__chats, + onClick: () => onFilter(leftPanel_utils.x.MUTED) + })))), filter && JSX_(REaCt().Fragment, null, filter === leftPanel_utils.x.MUTED && JSX_("div", { + className: "lhp-filter-tag", + onClick: () => onFilter(leftPanel_utils.x.MUTED) + }, JSX_("span", null, view === MEETINGS ? l.filter_muted__meetings : l.filter_muted__chats), JSX_("i", { + className: "sprite-fm-mono icon-close-component" + })), filter === leftPanel_utils.x.UNREAD && JSX_("div", { + className: "lhp-filter-tag", + onClick: () => onFilter(leftPanel_utils.x.UNREAD) + }, JSX_("span", null, l.filter_unread), JSX_("i", { + className: "sprite-fm-mono icon-close-component" + }))))), view === MEETINGS && routingSection !== 'contacts' && JSX_(buttons.$, { + className: "mega-button small positive new-meeting-action", + label: l.new_meeting + }, JSX_("i", { + className: "dropdown-indicator sprite-fm-mono icon-arrow-down" + }), JSX_(dropdowns.ms, { + className: "light", + noArrow: "true", + vertOffset: 4, + positionMy: "left top", + positionAt: "left bottom" + }, JSX_(dropdowns.tJ, { + className: "link-button", + icon: "sprite-fm-mono icon-video-plus", + label: l.new_meeting_start, + onClick: startMeeting + }), JSX_("hr", null), JSX_(dropdowns.tJ, { + className: "link-button", + icon: "sprite-fm-mono icon-calendar2", + label: l.schedule_meeting_start, + onClick: scheduleMeeting + }))), routingSection === 'contacts' && JSX_(buttons.$, { + className: "mega-button small positive", + label: l[71], + onClick: () => { + contactAddDialog(); + eventlog(500285); + } + })); +}; + const actions = Actions; +// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/applyDecoratedDescriptor.js +const applyDecoratedDescriptor = REQ_(793); +;// ./js/chat/ui/leftPanel/conversationsListItem.jsx + +let _dec, _dec2, _class; + + + + +const ConversationsListItem = (_dec = utils.Ay.SoonFcWrap(40, true), _dec2 = (0,mixins.N9)(0.7, 8), _class = class ConversationsListItem extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + this.state = { + isLoading: true + }; + } + isLoading() { + const mb = this.props.chatRoom.messagesBuff; + if (mb.haveMessages) { + return false; + } + return mb.messagesHistoryIsLoading() || mb.joined === false && mb.isDecrypting; + } + specShouldComponentUpdate() { + return !this.state.isLoading; + } + componentWillUnmount() { + super.componentWillUnmount(); + this.props.chatRoom.unbind('onUnreadCountUpdate.conversationsListItem'); + } + componentDidMount() { + super.componentDidMount(); + this.eventuallyScrollTo(); + const promise = this.isLoading(); + if (promise && promise.always) { + promise.always(() => { + if (this.isMounted()) { + this.setState({ + isLoading: false + }); + } + }); + } else if (promise === false) { + this.setState({ + isLoading: false + }); + } + this.props.chatRoom.rebind('onUnreadCountUpdate.conversationsListItem', () => { + this.safeForceUpdate(); + }); + } + componentDidUpdate() { + super.componentDidUpdate(); + this.eventuallyScrollTo(); + } + eventuallyScrollTo() { + const chatRoom = this.props.chatRoom || false; + if (chatRoom._scrollToOnUpdate) { + if (chatRoom.isCurrentlyActive) { + chatRoom.scrollToChat(); + } else { + chatRoom._scrollToOnUpdate = false; + } + } + } + getConversationTimestamp() { + const { + chatRoom + } = this.props; + if (chatRoom) { + const lastMessage = chatRoom.messagesBuff.getLatestTextMessage(); + const timestamp = lastMessage && lastMessage.delay || chatRoom.ctime; + return todayOrYesterday(timestamp * 1000) ? getTimeMarker(timestamp) : time2date(timestamp, 17); + } + return null; + } + getScheduledDateTime() { + const { + scheduledMeeting + } = this.props.chatRoom; + if (scheduledMeeting) { + const { + nextOccurrenceStart, + nextOccurrenceEnd + } = scheduledMeeting; + return { + date: time2date(nextOccurrenceStart / 1000, 19), + startTime: toLocaleTime(nextOccurrenceStart), + endTime: toLocaleTime(nextOccurrenceEnd) + }; + } + } + render() { + let classString = ""; + const {chatRoom} = this.props; + if (!chatRoom || !chatRoom.chatId) { + return null; + } + const roomId = chatRoom.chatId; + if (chatRoom.isCurrentlyActive) { + classString += " active"; + } + let nameClassString = "user-card-name conversation-name selectable-txt"; + let contactId; + let id; + let contact; + if (chatRoom.type === 'private') { + const handle = chatRoom.getParticipantsExceptMe()[0]; + contact = handle ? M.u[handle] : M.u[u_handle]; + if (!contact) { + return `Unknown conversation id for ${chatRoom.roomId}`; + } + id = `conversation_${htmlentities(contact.u)}`; + } else if (chatRoom.type === 'group') { + contactId = roomId; + id = `conversation_${contactId}`; + classString += ' groupchat'; + } else if (chatRoom.type === 'public') { + contactId = roomId; + id = `conversation_${contactId}`; + classString += ' groupchat public'; + } else { + return `Unknown room type for ${chatRoom.roomId}`; + } + const unreadCount = chatRoom.messagesBuff.getUnreadCount(); + let isUnread = false; + const notificationItems = []; + if (chatRoom.havePendingCall() && chatRoom.state !== ChatRoom.STATE.LEFT) { + notificationItems.push(JSX_("i", { + className: "tiny-icon white-handset", + key: "callIcon" + })); + } + if (unreadCount > 0) { + notificationItems.push(JSX_("span", { + key: "unreadCounter" + }, unreadCount > 9 ? "9+" : unreadCount)); + isUnread = true; + } + let lastMessageDiv = null; + const showHideMsg = mega.config.get('showHideChat'); + const lastMessage = showHideMsg ? '' : chatRoom.messagesBuff.getLatestTextMessage(); + let lastMsgDivClasses; + if (lastMessage) { + lastMsgDivClasses = `conversation-message${ isUnread ? " unread" : ""}`; + const renderableSummary = chatRoom.messagesBuff.getRenderableSummary(lastMessage); + if (chatRoom.havePendingCall() || chatRoom.haveActiveCall()) { + lastMsgDivClasses += " call"; + classString += " call-exists"; + } + lastMessageDiv = JSX_("div", { + className: lastMsgDivClasses + }, JSX_(utils.P9, null, renderableSummary)); + if (lastMessage.textContents && lastMessage.textContents[1] === Message.MANAGEMENT_MESSAGE_TYPES.VOICE_CLIP && lastMessage.getAttachmentMeta()[0]) { + const playTime = secondsToTimeShort(lastMessage.getAttachmentMeta()[0].playtime); + lastMessageDiv = JSX_("div", { + className: lastMsgDivClasses + }, JSX_("i", { + className: "sprite-fm-mono icon-audio-filled voice-message-icon" + }), playTime); + } + if (lastMessage.metaType && lastMessage.metaType === Message.MESSAGE_META_TYPE.GEOLOCATION) { + lastMessageDiv = JSX_("div", { + className: lastMsgDivClasses + }, JSX_("i", { + className: "sprite-fm-mono icon-location geolocation-icon" + }), l[20789]); + } + } else { + lastMsgDivClasses = "conversation-message"; + lastMessageDiv = showHideMsg ? '' : JSX_("div", { + className: lastMsgDivClasses + }, this.state.isLoading ? l[7006] : l[8000]); + } + if (chatRoom.type !== 'public') { + nameClassString += ' privateChat'; + } + let roomTitle = JSX_(utils.oM, null, megaChat.html(chatRoom.getRoomTitle())); + if (chatRoom.type === 'private') { + roomTitle = megaChat.WITH_SELF_NOTE && chatRoom.isNote ? JSX_("span", { + className: "note-chat-label" + }, l.note_label) : JSX_("span", null, JSX_("div", { + className: "user-card-wrapper" + }, JSX_(utils.oM, null, megaChat.html(chatRoom.getRoomTitle())))); + } + nameClassString += chatRoom.type === "private" || chatRoom.type === "group" ? ' badge-pad' : ''; + const { + scheduledMeeting, + isMeeting + } = chatRoom; + const isUpcoming = scheduledMeeting && scheduledMeeting.isUpcoming; + const { + startTime, + endTime + } = this.getScheduledDateTime() || {}; + const isEmptyNote = chatRoom.isNote && !chatRoom.hasMessages(); + return JSX_("li", { + ref: this.domRef, + id, + className: ` + ${classString} + ${isUpcoming ? 'upcoming-conversation' : ''} + ${this.props.className || ''} + `, + "data-room-id": roomId, + "data-jid": contactId, + onClick: ev => { + let _this$props$onConvers, _this$props; + return ((_this$props$onConvers = (_this$props = this.props).onConversationClick) == null ? void 0 : _this$props$onConvers.call(_this$props, ev)) || loadSubPage(chatRoom.getRoomUrl(false)); + } + }, JSX_("div", { + className: "conversation-avatar" + }, (chatRoom.type === 'group' || chatRoom.type === 'public') && JSX_("div", { + className: ` + chat-topic-icon + ${isMeeting ? 'meeting-icon' : ''} + ` + }, JSX_("i", { + className: isMeeting ? 'sprite-fm-mono icon-video-call-filled' : 'sprite-fm-uni icon-chat-group' + })), chatRoom.type === 'private' && contact && chatRoom.isNote ? JSX_("div", { + className: ` + note-chat-signifier + ${isEmptyNote ? 'note-chat-empty' : ''} + ` + }, JSX_("i", { + className: "sprite-fm-mono icon-file-text-thin-outline note-chat-icon" + })) : JSX_(contacts.eu, { + contact + })), JSX_("div", { + className: "conversation-data" + }, JSX_("div", { + className: "conversation-data-top" + }, JSX_("div", { + className: `conversation-data-name ${nameClassString}` + }, roomTitle, chatRoom.isMuted() ? JSX_("i", { + className: "sprite-fm-mono icon-notification-off-filled muted-conversation-icon" + }) : null), chatRoom.isNote ? null : JSX_("div", { + className: "conversation-data-badges" + }, chatRoom.type === 'private' ? JSX_(contacts.i1, { + contact + }) : null, chatRoom.type === 'group' || chatRoom.type === 'private' ? JSX_("i", { + className: "sprite-fm-uni icon-ekr-key simpletip", + "data-simpletip": l[20935] + }) : null, scheduledMeeting && scheduledMeeting.isUpcoming && scheduledMeeting.isRecurring && JSX_("i", { + className: "sprite-fm-mono icon-repeat-thin-solid" + }))), JSX_("div", { + className: "clear" + }), isUpcoming ? JSX_("div", { + className: "conversation-message-info" + }, JSX_("div", { + className: "conversation-scheduled-data" + }, JSX_("span", null, startTime), JSX_("span", null, "\xA0 - \xA0"), JSX_("span", null, endTime)), JSX_("div", { + className: "conversation-scheduled-data" + }, notificationItems.length > 0 ? JSX_("div", { + className: ` + unread-messages + items-${notificationItems.length} + unread-upcoming + ${unreadCount > 9 && notificationItems.length > 1 ? 'unread-spaced' : ''} + ` + }, notificationItems) : null)) : JSX_("div", { + className: "conversation-message-info" + }, isEmptyNote ? null : lastMessageDiv)), isUpcoming || isEmptyNote ? null : JSX_("div", { + className: "date-time-wrapper" + }, JSX_("div", { + className: "date-time" + }, this.getConversationTimestamp()), notificationItems.length > 0 ? JSX_("div", { + className: ` + unread-messages-container + ${unreadCount > 9 && notificationItems.length > 1 ? 'unread-spaced' : ''} + ` + }, JSX_("div", { + className: `unread-messages items-${notificationItems.length}` + }, notificationItems)) : null)); + } +}, (0,applyDecoratedDescriptor.A)(_class.prototype, "eventuallyScrollTo", [_dec], Object.getOwnPropertyDescriptor(_class.prototype, "eventuallyScrollTo"), _class.prototype), (0,applyDecoratedDescriptor.A)(_class.prototype, "render", [_dec2], Object.getOwnPropertyDescriptor(_class.prototype, "render"), _class.prototype), _class); + +;// ./js/chat/ui/leftPanel/conversationsList.jsx + + + + + + + +const ConversationsList = ({ + conversations, + className, + children +}) => { + return JSX_(perfectScrollbar.O, { + className: "chat-lp-scroll-area", + didMount: (id, ref) => { + megaChat.$chatTreePanePs = [...megaChat.$chatTreePanePs, { + id, + ref + }]; + }, + willUnmount: id => { + megaChat.$chatTreePanePs = megaChat.$chatTreePanePs.filter(ref => ref.id !== id); + }, + conversations + }, JSX_("ul", { + className: ` + conversations-pane + ${className || ''} + ` + }, children || conversations.map(c => c.roomId && JSX_(ConversationsListItem, (0,esm_extends.A)({ + key: c.roomId, + chatRoom: c + }, c.type === 'private' && { + contact: M.u[c.getParticipantsExceptMe()[0]] + }))))); +}; +const Chats = ({ + conversations, + onArchivedClicked, + filter +}) => { + conversations = Object.values(conversations || {}).filter(c => !c.isMeeting && c.isDisplayable() && (!filter || filter === leftPanel_utils.x.UNREAD && c.messagesBuff.getUnreadCount() > 0 || filter === leftPanel_utils.x.MUTED && c.isMuted())).sort(M.sortObjFn(c => c.lastActivity || c.ctime, -1)); + const noteChat = megaChat.getNoteChat(); + return JSX_(REaCt().Fragment, null, JSX_("div", { + className: "conversations-holder" + }, filter ? null : JSX_("div", { + className: "conversations-category" + }, JSX_("span", null, l.filter_heading__recent)), conversations && conversations.length >= 1 ? JSX_(ConversationsList, { + conversations + }, megaChat.WITH_SELF_NOTE && noteChat && noteChat.isDisplayable() ? filter ? null : JSX_(ConversationsListItem, { + chatRoom: noteChat + }) : null, conversations.map(c => c.roomId && !c.isNote && JSX_(ConversationsListItem, (0,esm_extends.A)({ + key: c.roomId, + chatRoom: c + }, c.type === 'private' && { + contact: M.u[c.getParticipantsExceptMe()[0]] + })))) : JSX_("div", { + className: ` + ${leftPanel_utils.C}-nil + ${filter ? `${leftPanel_utils.C}-nil--chats` : ''} + ` + }, filter ? JSX_(REaCt().Fragment, null, filter === leftPanel_utils.x.MUTED && JSX_(REaCt().Fragment, null, JSX_("i", { + className: "sprite-fm-mono icon-notification-off-filled" + }), JSX_("h3", null, l.filter_nil__muted_chats)), filter === leftPanel_utils.x.UNREAD && JSX_(REaCt().Fragment, null, JSX_("i", { + className: "sprite-fm-mono icon-eye-thin-solid" + }), JSX_("h3", null, l.filter_nil__unread_messages))) : JSX_("span", null, l.no_chats_lhp)), megaChat.WITH_SELF_NOTE && conversations && conversations.length === 1 && noteChat && JSX_(ConversationsList, { + conversations + }, JSX_(ConversationsListItem, { + chatRoom: noteChat + }))), JSX_("div", { + className: `${leftPanel_utils.C}-bottom` + }, JSX_("div", { + className: `${leftPanel_utils.C}-bottom-control` + }, JSX_("div", { + className: "conversations-category", + onClick: onArchivedClicked + }, JSX_("span", null, l.filter_archived__chats), JSX_("i", { + className: "sprite-fm-mono icon-arrow-right" + }))))); +}; +const Archived = ({ + conversations, + archivedUnmounting, + onClose +}) => { + const archivedChats = Object.values(conversations || {}).filter(c => !c.isMeeting && c.isArchived()).sort(M.sortObjFn(c => c.lastActivity || c.ctime, -1)); + return JSX_("div", { + className: ` + ${leftPanel_utils.C}-archived + ${archivedUnmounting ? 'with-unmount-animation' : ''} + ` + }, JSX_("div", { + className: `${leftPanel_utils.C}-archived-head` + }, JSX_(meetings_button.A, { + className: "mega-button round", + icon: "sprite-fm-mono icon-arrow-left-regular-outline", + onClick: onClose + }), JSX_("h2", null, l.filter_archived__chats)), JSX_("div", { + className: `${leftPanel_utils.C}-archived-content` + }, archivedChats && archivedChats.length ? JSX_(ConversationsList, { + conversations: archivedChats + }) : JSX_("div", { + className: `${leftPanel_utils.C}-archived-empty` + }, JSX_("i", { + className: "sprite-fm-mono icon-archive" + }), JSX_("h3", null, l.filter_archived__nil_chats)))); +}; +class Meetings extends mixins.w9 { + constructor(props) { + let _megaChat$getCurrentM; + super(props); + this.TABS = { + UPCOMING: 0x00, + PAST: 0x01 + }; + this.domRef = REaCt().createRef(); + this.ongoingRef = REaCt().createRef(); + this.navigationRef = REaCt().createRef(); + this.state = { + tab: this.TABS.UPCOMING + }; + this.Navigation = ({ + conversations + }) => { + const { + UPCOMING, + PAST + } = this.TABS; + const { + tab + } = this.state; + const unreadMeetings = Object.values(conversations || {}).reduce((acc, curr) => { + if (curr.isDisplayable() && curr.isMeeting && curr.messagesBuff.getUnreadCount()) { + let _curr$scheduledMeetin; + acc[(_curr$scheduledMeetin = curr.scheduledMeeting) != null && _curr$scheduledMeetin.isUpcoming ? UPCOMING : PAST]++; + } + return acc; + }, { + [UPCOMING]: 0, + [PAST]: 0 + }); + return JSX_("div", { + ref: this.navigationRef, + className: ` + ${leftPanel_utils.C}-meetings--navigation + ${this.props.leftPaneWidth < 230 ? 'narrow-width' : ''} + ` + }, JSX_(meetings_button.A, { + converstaions: conversations, + className: ` + mega-button + action + ${tab === UPCOMING ? 'is-active' : ''} + `, + onClick: () => this.setState({ + tab: UPCOMING + }) + }, JSX_("span", null, l.meetings_tab_upcoming, !!unreadMeetings[UPCOMING] && JSX_("div", { + className: "notification-indication" + }))), JSX_(meetings_button.A, { + converstaions: conversations, + className: ` + mega-button + action + ${tab === PAST ? 'is-active' : ''} + `, + onClick: () => this.setState({ + tab: PAST + }, () => eventlog(500254)) + }, JSX_("span", null, l.meetings_tab_past, !!unreadMeetings[PAST] && JSX_("div", { + className: "notification-indication" + })))); + }; + this.Holder = ({ + heading, + className, + children + }) => JSX_("div", { + className: ` + conversations-holder + ${className || ''} + ` + }, JSX_("div", { + className: ` + conversations-category + ` + }, heading && JSX_("span", null, heading)), children); + this.Ongoing = ({ + ongoingMeetings + }) => ongoingMeetings != null && ongoingMeetings.length ? JSX_("div", { + ref: this.ongoingRef, + className: `${leftPanel_utils.C}-meetings--ongoing` + }, JSX_("strong", null, l.happening_now), JSX_(ConversationsList, { + conversations: ongoingMeetings + })) : null; + this.Upcoming = () => { + const { + upcomingMeetings, + nextOccurrences + } = megaChat.plugins.meetingsManager.filterUpcomingMeetings(this.props.conversations); + const upcomingItem = chatRoom => JSX_(ConversationsListItem, { + key: chatRoom.roomId, + chatRoom + }); + return JSX_(this.Holder, null, upcomingMeetings && upcomingMeetings.length ? JSX_(ConversationsList, { + conversations: upcomingMeetings + }, nextOccurrences.today && nextOccurrences.today.length ? JSX_("div", { + className: "conversations-group" + }, JSX_("div", { + className: "conversations-category category--label" + }, JSX_("span", null, l.upcoming__today)), nextOccurrences.today.map(upcomingItem)) : null, nextOccurrences.tomorrow && nextOccurrences.tomorrow.length ? JSX_("div", { + className: "conversations-group" + }, JSX_("div", { + className: "conversations-category category--label" + }, JSX_("span", null, l.upcoming__tomorrow)), nextOccurrences.tomorrow.map(upcomingItem)) : null, Object.keys(nextOccurrences.rest).length ? Object.keys(nextOccurrences.rest).map(date => JSX_("div", { + key: date, + className: "conversations-group" + }, JSX_("div", { + className: "conversations-category category--label" + }, JSX_("span", null, date)), nextOccurrences.rest[date].map(upcomingItem))) : null) : JSX_("div", { + className: `${leftPanel_utils.C}-nil` + }, JSX_("i", { + className: "sprite-fm-mono icon-calendar-plus-thin-solid" + }), JSX_("span", null, l.meetings_upcoming_nil))); + }; + this.Past = () => { + const conversations = Object.values(this.props.conversations || {}); + const pastMeetings = conversations.filter(c => { + const { + isCanceled, + isPast, + isCompleted + } = c.scheduledMeeting || {}; + return c.isMeeting && c.isDisplayable() && (!c.scheduledMeeting || isCanceled || isPast || isCompleted) && !c.havePendingCall(); + }).sort(M.sortObjFn(c => c.lastActivity || c.ctime, -1)); + const archivedMeetings = conversations.filter(c => c.isMeeting && c.isArchived()).sort(M.sortObjFn(c => c.lastActivity || c.ctime, -1)); + return JSX_(this.Holder, null, JSX_(ConversationsList, { + conversations: pastMeetings + }, pastMeetings.length ? pastMeetings.map(chatRoom => chatRoom.roomId && JSX_(ConversationsListItem, { + key: chatRoom.roomId, + chatRoom + })) : JSX_("div", { + className: ` + ${leftPanel_utils.C}-nil + ${archivedMeetings.length ? 'half-sized' : ''} + ` + }, archivedMeetings.length ? JSX_("strong", null, l.meetings_past_nil_heading) : null, JSX_("i", { + className: "sprite-fm-mono icon-video-thin-solid" + }), JSX_("span", null, l.meetings_past_nil)), archivedMeetings.length ? JSX_(REaCt().Fragment, null, JSX_("div", { + className: "archived-separator" + }), JSX_("div", { + className: "conversations-category category--label" + }, JSX_("span", null, l.meetings_label_archived)), archivedMeetings.map(chatRoom => chatRoom.roomId && JSX_(ConversationsListItem, { + key: chatRoom.roomId, + chatRoom + }))) : null)); + }; + this.getContainerStyles = ongoingMeetings => { + if (ongoingMeetings != null && ongoingMeetings.length) { + let _this$ongoingRef, _this$navigationRef; + const ongoingHeight = (_this$ongoingRef = this.ongoingRef) == null || (_this$ongoingRef = _this$ongoingRef.current) == null ? void 0 : _this$ongoingRef.clientHeight; + const navigationHeight = (_this$navigationRef = this.navigationRef) == null || (_this$navigationRef = _this$navigationRef.current) == null ? void 0 : _this$navigationRef.clientHeight; + return { + style: { + maxHeight: `calc(100% - ${ongoingHeight + navigationHeight + 30}px)` + } + }; + } + return null; + }; + this.state.tab = this.TABS[(_megaChat$getCurrentM = megaChat.getCurrentMeeting()) != null && _megaChat$getCurrentM.isPast ? 'PAST' : 'UPCOMING']; + } + componentWillUnmount() { + super.componentWillUnmount(); + megaChat.off(`${megaChat.plugins.meetingsManager.EVENTS.OCCURRENCES_UPDATE}.${this.getUniqueId()}`); + } + componentDidMount() { + super.componentDidMount(); + megaChat.rebind(`${megaChat.plugins.meetingsManager.EVENTS.OCCURRENCES_UPDATE}.${this.getUniqueId()}`, () => this.safeForceUpdate()); + megaChat.rebind(megaChat.plugins.meetingsManager.EVENTS.INITIALIZE, (ev, scheduledMeeting) => this.isMounted() && this.setState({ + tab: this.TABS[scheduledMeeting != null && scheduledMeeting.isPast ? 'PAST' : 'UPCOMING'] + })); + } + render() { + const { + UPCOMING, + PAST + } = this.TABS; + const { + tab + } = this.state; + const ongoingMeetings = Object.values(this.props.conversations || {}).filter(c => c.isDisplayable() && c.isMeeting && c.havePendingCall()); + return JSX_("div", { + ref: this.domRef, + className: `${leftPanel_utils.C}-meetings` + }, JSX_(this.Ongoing, { + ongoingMeetings + }), JSX_(this.Navigation, { + conversations: this.props.conversations + }), JSX_("div", (0,esm_extends.A)({ + className: ` + ${leftPanel_utils.C}-meetings--content + ${tab === UPCOMING ? 'is-upcoming' : ''} + ${tab === PAST ? 'is-past' : ''} + ` + }, this.getContainerStyles(ongoingMeetings)), tab === UPCOMING && JSX_(this.Upcoming, null), tab === PAST && JSX_(this.Past, null))); + } +} +// EXTERNAL MODULE: ./js/chat/ui/updateObserver.jsx +const updateObserver = REQ_(4372); +;// ./js/chat/ui/leftPanel/leftPanel.jsx + + + + + + + + + +class LeftPanel extends mixins.w9 { + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.contactRequestsListener = undefined; + this.fmConfigLeftPaneListener = undefined; + this.state = { + leftPaneWidth: Math.min(mega.config.get('leftPaneWidth') | 0, 400) || 384, + archived: false, + archivedUnmounting: false, + filter: '', + unreadChats: 0, + unreadMeetings: 0, + contactRequests: 0 + }; + this.toggleFilter = filter => { + this.setState(state => ({ + filter: state.filter === filter ? '' : filter + }), () => { + Object.values(megaChat.$chatTreePanePs).map(({ + ref + }) => ref.reinitialise == null ? void 0 : ref.reinitialise()); + }); + }; + this.state.contactRequests = Object.keys(M.ipc).length; + } + customIsEventuallyVisible() { + return M.chat; + } + renderLoading() { + return JSX_(REaCt().Fragment, null, JSX_("span", { + className: "heading loading-sketch" + }), JSX_("ul", { + className: "conversations-pane loading-sketch" + }, Array.from({ + length: this.props.conversations.length + }, (el, i) => { + return JSX_("li", { + key: i + }, JSX_("div", { + className: "conversation-avatar" + }, JSX_("div", { + className: "chat-topic-icon" + })), JSX_("div", { + className: "conversation-data" + }, JSX_("div", { + className: "conversation-data-top" + }), JSX_("div", { + className: "conversation-message-info" + }, JSX_("div", { + className: "conversation-message" + })))); + }))); + } + componentWillUnmount() { + super.componentWillUnmount(); + megaChat.unbind(`onUnreadCountUpdate.${leftPanel_utils.C}`); + mBroadcaster.removeListener(this.contactRequestsListener); + mBroadcaster.removeListener(this.fmConfigLeftPaneListener); + } + componentDidMount() { + let _$$leftPaneResizable; + super.componentDidMount(); + megaChat.rebind(`onUnreadCountUpdate.${leftPanel_utils.C}`, (ev, { + unreadChats, + unreadMeetings + }) => { + this.setState({ + unreadChats, + unreadMeetings + }, () => this.safeForceUpdate()); + }); + this.contactRequestsListener = mBroadcaster.addListener('fmViewUpdate:ipc', () => this.setState({ + contactRequests: Object.keys(M.ipc).length + })); + $.leftPaneResizableChat = new FMResizablePane(this.domRef.current, { + ...(_$$leftPaneResizable = $.leftPaneResizable) == null ? void 0 : _$$leftPaneResizable.options, + minWidth: mega.flags.ab_ads ? 260 : 200 + }); + this.fmConfigLeftPaneListener = mBroadcaster.addListener('fmconfig:leftPaneWidth', value => this.setState(state => ({ + leftPaneWidth: value || state.leftPaneWidth + }))); + } + render() { + const { + view, + views, + conversations, + routingSection, + renderView, + startMeeting, + scheduleMeeting, + createNewChat + } = this.props; + const { + CHATS, + MEETINGS, + LOADING + } = views; + return JSX_("div", (0,esm_extends.A)({ + ref: this.domRef, + className: ` + fm-left-panel + chat-lp-body + ${leftPanel_utils.C}-container + ${is_chatlink && 'hidden' || ''} + ${megaChat._joinDialogIsShown && 'hidden' || ''} + ` + }, this.state.leftPaneWidth && { + width: this.state.leftPaneWidth + }), JSX_("div", { + className: "left-pane-drag-handle" + }), JSX_(SearchPanel, null), JSX_(Navigation, { + view, + views, + routingSection, + unreadChats: this.state.unreadChats, + unreadMeetings: this.state.unreadMeetings, + contactRequests: this.state.contactRequests, + renderView: view => this.setState({ + filter: false + }, () => renderView(view)) + }), JSX_(actions, { + view, + views, + filter: this.state.filter, + routingSection, + startMeeting, + scheduleMeeting, + createNewChat, + onFilter: this.toggleFilter + }), this.state.archived && JSX_(Archived, { + conversations, + archivedUnmounting: this.state.archivedUnmounting, + onClose: () => this.setState({ + archivedUnmounting: true + }, () => tSleep(0.3).then(() => this.setState({ + archivedUnmounting: false, + archived: false + }))) + }), JSX_("div", { + className: ` + ${leftPanel_utils.C}-conversations + ${view === MEETINGS ? 'meetings-view' : ''} + ${view === CHATS ? 'chats-view' : ''} + conversations + content-panel + active + ` + }, view === LOADING ? this.renderLoading() : JSX_(REaCt().Fragment, null, view === MEETINGS && JSX_(Meetings, { + conversations, + leftPaneWidth: this.state.leftPaneWidth + }), view === CHATS && JSX_(Chats, { + conversations, + filter: this.state.filter, + onArchivedClicked: () => this.setState({ + archived: true, + filter: false + }) + })))); + } +} + const leftPanel = (0,mixins.Zz)(updateObserver.Y)(LeftPanel); + + }, + + 7677 +(_, EXP_, REQ_) { + + REQ_.d(EXP_, { + C: () => withHostsObserver + }); + const _babel_runtime_helpers_extends0__ = REQ_(8168); + const react1__ = REQ_(1594); + const react1___default = REQ_.n(react1__); + const _mixins_js2__ = REQ_(8264); + const _ui_modalDialogs_jsx3__ = REQ_(8120); + const _contacts_jsx4__ = REQ_(8022); + const _ui_buttons_jsx5__ = REQ_(5155); + + + + + + +const withHostsObserver = Component => { + return class extends _mixins_js2__ .w9 { + constructor(...args) { + super(...args); + this.state = { + dialog: false, + selected: [] + }; + this.hasHost = participants => participants.some(handle => this.props.chatRoom.members[handle] === ChatRoom.MembersSet.PRIVILEGE_STATE.OPERATOR); + this.toggleDialog = () => { + this.setState(state => ({ + dialog: !state.dialog, + selected: [] + }), () => this.safeForceUpdate()); + }; + this.renderDialog = () => { + let _this$props$participa; + const { + selected + } = this.state; + return JSX_(_ui_modalDialogs_jsx3__ .A.ModalDialog, (0,_babel_runtime_helpers_extends0__ .A)({}, this.state, { + className: "assign-host contact-picker-widget", + dialogName: "assign-host-dialog", + dialogType: "tool", + onClose: () => this.setState({ + dialog: false + }, () => this.safeForceUpdate()) + }), JSX_("header", null, JSX_("h2", null, l.assign_host_title)), JSX_("div", { + className: "content-block" + }, JSX_(_contacts_jsx4__ .hU, { + className: "popup contacts-search small-footer", + contacts: (_this$props$participa = this.props.participants) == null ? void 0 : _this$props$participa.filter(h => h !== u_handle), + multiple: true, + hideSearch: true, + disableFrequents: true, + participantsList: true, + disableDoubleClick: true, + emailTooltips: true, + nothingSelectedButtonLabel: l.add_hosts_placeholder, + onClose: () => this.setState({ + dialog: false + }), + onSelected: selected => this.setState({ + selected + }, () => this.safeForceUpdate()) + })), JSX_("footer", null, JSX_("div", { + className: "footer-container" + }, JSX_(_ui_buttons_jsx5__ .$, { + label: l.msg_dlg_cancel, + className: "mega-button", + onClick: this.toggleDialog + }), JSX_(_ui_buttons_jsx5__ .$, { + label: l.assign_and_leave, + className: ` + mega-button + positive + ${selected.length ? '' : 'disabled'} + `, + onClick: () => selected.length && this.assignAndLeave() + })))); + }; + this.assignAndLeave = () => { + const { + chatRoom, + onLeave + } = this.props; + const { + selected + } = this.state; + for (let i = selected.length; i--;) { + chatRoom.trigger('alterUserPrivilege', [selected[i], ChatRoom.MembersSet.PRIVILEGE_STATE.OPERATOR]); + } + this.toggleDialog(); + onLeave == null || onLeave(); + $(document).trigger('closeDropdowns'); + }; + this.confirmLeave = ({ + title, + body, + cta, + altCta + }) => { + msgDialog(`confirmationa:!^${cta}!${altCta || l.msg_dlg_cancel}`, null, title, body, cb => { + if (cb) { + this.toggleDialog(); + } else if (cb === false) { + let _this$props$onConfirm, _this$props; + (_this$props$onConfirm = (_this$props = this.props).onConfirmDenied) == null || _this$props$onConfirm.call(_this$props); + } + }, 1); + }; + } + render() { + return JSX_(react1___default().Fragment, null, JSX_(Component, (0,_babel_runtime_helpers_extends0__ .A)({}, this.props, { + confirmLeave: this.confirmLeave, + hasHost: this.hasHost + })), this.state.dialog && this.renderDialog()); + } + }; +}; + + }, + + 2153 +(_, EXP_, REQ_) { + + REQ_.d(EXP_, { + j: () => JOIN_VIEW + }); +const JOIN_VIEW = { + INITIAL: 0, + GUEST: 1, + ACCOUNT: 2, + UNSUPPORTED: 4 +}; + + }, + + 8025 +(_, EXP_, REQ_) { + + +// EXPORTS +REQ_.d(EXP_, { + A: () => GenericConversationMessage +}); + +// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/extends.js +const esm_extends = REQ_(8168); +// EXTERNAL MODULE: external "React" +const external_React_ = REQ_(1594); +const REaCt = REQ_.n(external_React_); +// EXTERNAL MODULE: ./js/chat/ui/messages/mixin.jsx +const mixin = REQ_(855); +// EXTERNAL MODULE: ./js/chat/ui/contacts.jsx +const ui_contacts = REQ_(8022); +// EXTERNAL MODULE: ./js/ui/utils.jsx +const utils = REQ_(6411); +;// ./js/chat/ui/messages/abstractGenericMessage.jsx + + + + +class AbstractGenericMessage extends mixin.M { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + } + getAvatar() { + const contact = this.getContact() || Message.getContactForMessage(this.props.message); + if (this.props.grouped) { + return null; + } + return contact ? JSX_(ui_contacts.eu, { + contact: this.getContact(), + className: "message avatar-wrapper small-rounded-avatar", + chatRoom: this.props.chatRoom + }) : null; + } + getName() { + const contact = this.getContact() || Message.getContactForMessage(this.props.message); + if (this.props.grouped) { + return null; + } + return contact ? JSX_(ui_contacts.bq, { + contact, + className: "message", + label: JSX_(utils.zT, null, M.getNameByHandle(contact.u)), + chatRoom: this.props.message.chatRoom, + dropdownDisabled: !!this.props.dialog + }) : null; + } + renderMessageActionButtons(buttons) { + if (!buttons) { + return null; + } + const cnt = buttons.length; + if (cnt === 0) { + return null; + } + return JSX_("div", { + className: `right-aligned-msg-buttons ${cnt && cnt > 1 ? `total-${cnt}` : ''}` + }, buttons); + } + render() { + const { + message, + grouped, + additionalClasses, + hideActionButtons + } = this.props; + if (message.deleted) { + return null; + } + return JSX_("div", { + ref: this.domRef, + "data-id": message.messageId, + className: ` + ${this.getClassNames ? this.getClassNames() : grouped ? 'grouped' : ''} + ${additionalClasses} + ${message.messageId} + message + body + ` + }, this.getAvatar && this.getAvatar(), JSX_("div", { + className: "message content-area selectable-txt" + }, this.getName && this.getName(), this.getMessageTimestamp ? this.getMessageTimestamp() : grouped ? null : JSX_("div", { + className: "message date-time simpletip", + "data-simpletip": time2date(this.getTimestamp(), 17), + "data-simpletipposition": "top", + "data-simpletipoffset": "4" + }, this.getTimestampAsString()), !hideActionButtons && this.getMessageActionButtons && this.renderMessageActionButtons(this.getMessageActionButtons()), this.getContents && this.getContents(), hideActionButtons ? null : this.getEmojisImages())); + } +} +// EXTERNAL MODULE: ./js/chat/ui/messages/utils.jsx +const messages_utils = REQ_(187); +;// ./js/chat/ui/messages/types/local.jsx + + + + + +const MESSAGE_TYPE = { + OUTGOING: 'outgoing-call', + INCOMING: 'incoming-call', + TIMEOUT: 'call-timeout', + STARTING: 'call-starting', + FEEDBACK: 'call-feedback', + INITIALISING: 'call-initialising', + ENDED: 'call-ended', + ENDED_REMOTE: 'remoteCallEnded', + FAILED: 'call-failed', + FAILED_MEDIA: 'call-failed-media', + HANDLED_ELSEWHERE: 'call-handled-elsewhere', + MISSED: 'call-missed', + REJECTED: 'call-rejected', + CANCELLED: 'call-canceled', + STARTED: 'call-started', + STARTED_REMOTE: 'remoteCallStarted', + ALTER_PARTICIPANTS: 'alterParticipants', + PRIVILEGE_CHANGE: 'privilegeChange', + TRUNCATED: 'truncated' +}; +class Local extends AbstractGenericMessage { + componentDidMount() { + super.componentDidMount(); + this._setClassNames(); + } + _roomIsGroup() { + return this.props.message.chatRoom.type === 'group' || this.props.message.chatRoom.type === 'public'; + } + _getParticipantNames(message) { + return message.meta && message.meta.participants && !!message.meta.participants.length && message.meta.participants.map(handle => `[[${megaChat.html(M.getNameByHandle(handle))}]]`); + } + _getExtraInfo(message) { + const { + meta, + type + } = message; + const participantNames = this._getParticipantNames(message); + const HAS_PARTICIPANTS = participantNames && !!participantNames.length && participantNames.length > 1; + const HAS_DURATION = meta && meta.duration; + const ENDED = type === MESSAGE_TYPE.ENDED || type === MESSAGE_TYPE.FAILED || type === MESSAGE_TYPE.CANCELLED; + let messageExtraInfo = [HAS_PARTICIPANTS ? mega.utils.trans.listToString(participantNames, l[20234]) : '']; + if (ENDED) { + messageExtraInfo = [...messageExtraInfo, HAS_PARTICIPANTS ? '. ' : '', HAS_DURATION ? l[7208].replace('[X]', `[[${secToDuration(meta.duration)}]]`) : '']; + } + return messageExtraInfo && messageExtraInfo.reduce((acc, cur) => (acc + cur).replace(/\[\[/g, '').replace(/]]/g, '')); + } + _setClassNames() { + let cssClass; + switch (this.props.message.type) { + case MESSAGE_TYPE.REJECTED: + cssClass = 'sprite-fm-theme icon-handset-rejected'; + break; + case MESSAGE_TYPE.MISSED: + cssClass = 'sprite-fm-theme icon-handset-missed'; + break; + case MESSAGE_TYPE.OUTGOING: + case MESSAGE_TYPE.HANDLED_ELSEWHERE: + cssClass = 'sprite-fm-theme icon-handset-outgoing'; + break; + case MESSAGE_TYPE.FAILED: + case MESSAGE_TYPE.FAILED_MEDIA: + cssClass = 'sprite-fm-theme icon-handset-failed'; + break; + case MESSAGE_TYPE.ENDED: + case MESSAGE_TYPE.TIMEOUT: + cssClass = 'sprite-fm-theme icon-handset-ended'; + break; + case MESSAGE_TYPE.CANCELLED: + cssClass = 'sprite-fm-theme icon-handset-cancelled'; + break; + case MESSAGE_TYPE.FEEDBACK: + case MESSAGE_TYPE.STARTING: + case MESSAGE_TYPE.STARTED: + cssClass = 'sprite-fm-mono icon-phone'; + break; + case MESSAGE_TYPE.INCOMING: + cssClass = 'sprite-fm-theme icon-handset-incoming'; + break; + default: + cssClass = `sprite-fm-mono ${ this.props.message.type}`; + break; + } + this.props.message.cssClass = cssClass; + } + _getIcon(message) { + const MESSAGE_ICONS = { + [MESSAGE_TYPE.STARTED]: ` `, + [MESSAGE_TYPE.ENDED]: ` `, + DEFAULT: ` ` + }; + return MESSAGE_ICONS[message.type] || MESSAGE_ICONS.DEFAULT; + } + _getText() { + const { + message + } = this.props; + const IS_GROUP = this._roomIsGroup(); + let messageText = (0,messages_utils.d)(message.type, IS_GROUP, message.chatRoom.isMeeting); + if (!messageText) { + return console.error(`Message with type: ${message.type} -- no text string defined. Message: ${message}`); + } + messageText = CallManager2._getMltiStrTxtCntsForMsg(message, messageText.splice ? messageText : [messageText], true); + messageText = megaChat.html(messageText); + message.textContents = String(messageText).replace("[[", "").replace("]]", ""); + if (IS_GROUP) { + messageText = ` + ${this._getIcon(message)} +
+ ${messageText} + ${this._getExtraInfo(message)} +
+ `; + } + return messageText; + } + _getAvatarsListing() { + const { + message + } = this.props; + if (this._roomIsGroup() && message.type === MESSAGE_TYPE.STARTED && message.messageId === `${MESSAGE_TYPE.STARTED}-${message.chatRoom.getActiveCallMessageId()}`) { + const unique = message.chatRoom.uniqueCallParts ? Object.keys(message.chatRoom.uniqueCallParts) : []; + return unique.map(handle => JSX_(ui_contacts.eu, { + key: handle, + contact: M.u[handle], + simpletip: true, + className: "message avatar-wrapper small-rounded-avatar" + })); + } + return null; + } + _getButtons() { + const { + message + } = this.props; + if (message.buttons && Object.keys(message.buttons).length) { + return JSX_("div", { + className: "buttons-block" + }, Object.keys(message.buttons).map(key => { + const button = message.buttons[key]; + return JSX_("button", { + key, + className: button.classes, + onClick: e => button.callback(e.target) + }, button.icon && JSX_("div", null, JSX_("i", { + className: `small-icon ${button.icon}` + })), JSX_("span", null, button.text)); + }), JSX_("div", { + className: "clear" + })); + } + } + getAvatar() { + const { + message, + grouped + } = this.props; + if (message.type === MESSAGE_TYPE.FEEDBACK) { + return null; + } + const $$AVATAR = JSX_(ui_contacts.eu, { + contact: message.authorContact, + className: "message avatar-wrapper small-rounded-avatar", + chatRoom: message.chatRoom + }); + const $$ICON = JSX_("div", { + className: "feedback call-status-block" + }, JSX_("i", { + className: `sprite-fm-mono ${message.cssClass}` + })); + return message.showInitiatorAvatar ? grouped ? null : $$AVATAR : $$ICON + ; + } + getMessageTimestamp() { + let _this$props$message; + const callId = (_this$props$message = this.props.message) == null || (_this$props$message = _this$props$message.meta) == null ? void 0 : _this$props$message.callId; + let debugMsg = ""; + if (d && callId) { + debugMsg = `: callId: ${callId}`; + } + return JSX_("div", { + className: "message date-time simpletip", + "data-simpletip": time2date(this.getTimestamp(), 17) + }, this.getTimestampAsString(), debugMsg); + } + getClassNames() { + const { + message: { + showInitiatorAvatar, + type + }, + grouped + } = this.props; + const classNames = [showInitiatorAvatar && grouped && 'grouped', this._roomIsGroup() && type !== MESSAGE_TYPE.OUTGOING && type !== MESSAGE_TYPE.INCOMING && 'with-border']; + return classNames.filter(className => className).join(' '); + } + getName() { + const { + message, + grouped + } = this.props; + const contact = this.getContact(); + return message.showInitiatorAvatar && !grouped ? JSX_(ui_contacts.bq, { + contact, + className: "message", + label: JSX_(utils.zT, null, message.authorContact ? M.getNameByHandle(message.authorContact.u) : ''), + chatRoom: message.chatRoom + }) : M.getNameByHandle(contact.u); + } + getContents() { + const { + message: { + getState + } + } = this.props; + return JSX_(REaCt().Fragment, null, JSX_("div", { + className: "message text-block" + }, JSX_("div", { + className: "message call-inner-block" + }, JSX_("div", { + className: "call-info" + }, JSX_("div", { + className: "call-info-container" + }, JSX_(utils.P9, { + className: "info-wrapper" + }, this._getText())), JSX_("div", { + className: "call-info-avatars" + }, this._getAvatarsListing(), JSX_("div", { + className: "clear" + }))))), getState && getState() === Message.STATE.NOT_SENT ? null : this._getButtons()); + } +} +// EXTERNAL MODULE: ./js/ui/dropdowns.jsx +const dropdowns = REQ_(1510); +// EXTERNAL MODULE: ./js/ui/buttons.jsx +const buttons = REQ_(5155); +;// ./js/chat/ui/messages/types/contact.jsx + + + + + + +class Contact extends AbstractGenericMessage { + constructor(...args) { + super(...args); + this.DIALOG = { + ADDED: addedEmail => msgDialog('info', l[150], l[5898].replace('[X]', addedEmail)), + DUPLICATE: () => msgDialog('warningb', '', l[17545]) + }; + } + haveMoreContactListeners() { + const { + message + } = this.props; + const textContents = message.textContents.substring(2, message.textContents.length); + const attachmentMeta = JSON.parse(textContents); + if (!attachmentMeta) { + return false; + } + const contacts = attachmentMeta.map(v => v.u); + return contacts.length ? contacts : false; + } + _doAddContact(contactEmail) { + return M.inviteContact(M.u[u_handle] ? M.u[u_handle].m : u_attr.email, contactEmail); + } + _handleAddContact(contactEmail) { + let _this$props$chatRoom; + if ((_this$props$chatRoom = this.props.chatRoom) != null && _this$props$chatRoom.isAnonymous()) { + return this._doAddContact(contactEmail).then(addedEmail => this.DIALOG.ADDED(addedEmail)).catch(this.DIALOG.DUPLICATE); + } + return Object.values(M.opc).some(opc => opc.m === contactEmail) ? this.DIALOG.DUPLICATE() : this._doAddContact(contactEmail).then(addedEmail => this.DIALOG.ADDED(addedEmail)) + ; + } + _getContactAvatar(contact, className) { + return JSX_(ui_contacts.eu, { + className: `avatar-wrapper ${className}`, + contact: M.u[contact.u], + chatRoom: this.props.chatRoom + }); + } + _getContactDeleteButton(message) { + if (message.isEditable()) { + return JSX_(REaCt().Fragment, null, JSX_("hr", null), JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-dialog-close", + label: l[83], + onClick: e => this.props.onDelete(e, message) + })); + } + } + _getContactCard(message, contact, contactEmail) { + const HAS_RELATIONSHIP = M.u[contact.u].c === 1; + let name = JSX_(ui_contacts.uA, { + emoji: true, + contact: M.u[contact.u] + }); + const { + chatRoom + } = this.props; + const isAnonView = chatRoom.isAnonymous(); + if (megaChat.FORCE_EMAIL_LOADING) { + name += `(${ contact.m })`; + } + return JSX_(buttons.$, { + ref: ref => { + this.buttonRef = ref; + }, + className: "tiny-button", + icon: "tiny-icon icons-sprite grey-dots" + }, JSX_(dropdowns.ms, { + className: "white-context-menu shared-contact-dropdown", + noArrow: true, + positionMy: "left bottom", + positionAt: "right bottom", + horizOffset: 4 + }, JSX_("div", { + className: "dropdown-avatar rounded" + }, this._getContactAvatar(contact, 'context-avatar'), isAnonView ? JSX_("div", { + className: "dropdown-user-name" + }) : JSX_("div", { + className: "dropdown-user-name" + }, JSX_("div", { + className: "name" + }, HAS_RELATIONSHIP && (this.isLoadingContactInfo() ? JSX_("em", { + className: "contact-name-loading" + }) : name), !HAS_RELATIONSHIP && name, JSX_(ui_contacts.i1, { + className: "small", + contact + })), JSX_("div", { + className: "email" + }, M.u[contact.u].m))), JSX_(ui_contacts.BE, { + contact: M.u[contact.u] + }), HAS_RELATIONSHIP && JSX_(REaCt().Fragment, null, JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-user-filled", + label: l[5868], + onClick: () => { + loadSubPage(`fm/chat/contacts/${ contact.u}`); + mBroadcaster.sendMessage('contact:open'); + } + }), JSX_("hr", null), JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-chat-filled", + label: l[8632], + onClick: () => { + loadSubPage(`fm/chat/p/${ contact.u}`); + mBroadcaster.sendMessage('chat:open'); + } + })), u_type && u_type > 2 && contact.u !== u_handle && !HAS_RELATIONSHIP && !is_eplusplus && JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-add", + label: l[71], + onClick: () => this._handleAddContact(contactEmail) + }), this._getContactDeleteButton(message))); + } + getContents() { + const { + message, + chatRoom + } = this.props; + const textContents = message.textContents.substr(2, message.textContents.length); + const attachmentMeta = JSON.parse(textContents); + const isAnonView = chatRoom.isAnonymous(); + if (!attachmentMeta) { + return console.error(`Message w/ type: ${message.type} -- no attachment meta defined. Message: ${message}`); + } + let contacts = []; + attachmentMeta.forEach(v => { + let _this$buttonRef; + const contact = M.u && v.u in M.u && M.u[v.u].m ? M.u[v.u] : v; + const contactEmail = contact.email ? contact.email : contact.m; + if (!M.u[contact.u]) { + M.u.set(contact.u, new MegaDataObject(MEGA_USER_STRUCT, { + 'u': contact.u, + 'name': contact.name, + 'm': contact.email ? contact.email : contactEmail, + 'c': undefined + })); + } else if (M.u[contact.u] && !M.u[contact.u].m) { + M.u[contact.u].m = contact.email ? contact.email : contactEmail; + } + contacts = [...contacts, JSX_("div", { + key: contact.u + }, isAnonView ? JSX_("div", { + className: "message shared-info" + }) : JSX_("div", { + className: "message shared-info" + }, JSX_("div", { + className: "message data-title selectable-txt", + onClick: (_this$buttonRef = this.buttonRef) == null ? void 0 : _this$buttonRef.onClick + }, JSX_(utils.zT, null, M.getNameByHandle(contact.u))), M.u[contact.u] ? JSX_(ui_contacts.n4, { + className: "right-align", + contact: M.u[contact.u] + }) : null, JSX_("div", { + className: "user-card-email selectable-txt" + }, contactEmail)), JSX_("div", { + className: "message shared-data" + }, JSX_("div", { + className: "data-block-view semi-big" + }, M.u[contact.u] ? JSX_(ui_contacts.i1, { + className: "small", + contact: M.u[contact.u] + }) : null, this._getContactCard(message, contact, contactEmail), this._getContactAvatar(contact, 'medium-avatar')), JSX_("div", { + className: "clear" + })))]; + }); + return JSX_("div", { + className: "message shared-block" + }, contacts); + } +} +;// ./js/chat/ui/messages/types/attachment.jsx + + + + +class Attachment extends AbstractGenericMessage { + _isRevoked(node) { + return !M.chd[node.ch] || node.revoked; + } + _isUserRegistered() { + return typeof u_type !== 'undefined' && u_type > 2; + } + getContents() { + const { + message, + chatRoom + } = this.props; + const contact = this.getContact(); + const NODE_DOESNT_EXISTS_ANYMORE = {}; + const attachmentMeta = message.getAttachmentMeta() || []; + const files = []; + for (let i = 0; i < attachmentMeta.length; i++) { + var _this$buttonRef; + const v = attachmentMeta[i]; + if (this._isRevoked(v)) { + continue; + } + const { + icon, + isImage, + isVideo, + isAudio, + isText, + showThumbnail, + isPreviewable + } = M.getMediaProperties(v); + let dropdown = null; + let noThumbPrev = ''; + var previewButton = null; + if (isPreviewable) { + if (!showThumbnail) { + noThumbPrev = 'no-thumb-prev'; + } + let previewLabel = isAudio ? l[17828] : isVideo ? l[16275] : l[1899]; + let previewIcon = isAudio ? 'icon-play' : isVideo ? 'icon-video-call-filled' : 'icon-preview-reveal'; + if (isText) { + previewLabel = l[16797]; + previewIcon = "icon-file-edit"; + } + previewButton = JSX_("span", { + key: "previewButton" + }, JSX_(dropdowns.tJ, { + label: previewLabel, + icon: `sprite-fm-mono ${previewIcon}`, + disabled: mega.paywall, + onClick: e => { + mega.ui.mInfoPanel.hide(); + this.props.onPreviewStart(v, e); + } + })); + } + dropdown = contact.u === u_handle ? JSX_(buttons.$, { + ref: ref => { + this.buttonRef = ref; + }, + className: "tiny-button", + icon: "tiny-icon icons-sprite grey-dots" + }, JSX_(dropdowns.ms, { + className: "white-context-menu attachments-dropdown", + noArrow: true, + positionMy: "left top", + positionAt: "left bottom", + horizOffset: -4, + vertOffset: 3, + onBeforeActiveChange: newState => { + if (newState === true) { + this.forceUpdate(); + } + }, + dropdownItemGenerator: dd => { + const linkButtons = []; + const firstGroupOfButtons = []; + let revokeButton = null; + let downloadButton = null; + let addToAlbumButton = null; + if (message.isEditable && message.isEditable()) { + revokeButton = JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-dialog-close", + label: l[83], + onClick: () => { + chatRoom.megaChat.plugins.chatdIntegration.updateMessage(chatRoom, message.internalId || message.orderValue, ""); + } + }); + } + if (!M.d[v.h] && !NODE_DOESNT_EXISTS_ANYMORE[v.h]) { + dbfetch.acquire(v.h).always(() => { + if (!M.d[v.h]) { + NODE_DOESNT_EXISTS_ANYMORE[v.h] = true; + dd.doRerender(); + } else { + dd.doRerender(); + } + }); + return JSX_("span", { + className: "loading" + }, l[5533]); + } else if (!NODE_DOESNT_EXISTS_ANYMORE[v.h]) { + downloadButton = JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-download-small", + label: l[1187], + disabled: mega.paywall, + onClick: () => this.props.onDownloadStart(v) + }); + if (M.getNodeRoot(v.h) !== M.RubbishID) { + this.props.onAddLinkButtons(v.h, linkButtons); + } + firstGroupOfButtons.push(JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-info", + label: l[6859], + key: "infoDialog", + onClick: () => { + mega.ui.mInfoPanel.show([v.ch]); + } + })); + this.props.onAddFavouriteButtons(v.h, firstGroupOfButtons); + linkButtons.push(JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-send-to-chat", + label: l[17764], + key: "sendToChat", + disabled: mega.paywall, + onClick: () => { + $.selected = [v.h]; + openSendToChatDialog(); + } + })); + if (M.isGalleryNode(v)) { + addToAlbumButton = JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono rectangle-stack-plus-small-regular-outline", + label: l.add_to_album, + disabled: mega.paywall, + onClick: () => mega.gallery.albums.addToAlbum([v.h]) + }); + } + } + if (!previewButton && firstGroupOfButtons.length === 0 && !downloadButton && !addToAlbumButton && linkButtons.length === 0 && !revokeButton) { + return null; + } + if (previewButton && (firstGroupOfButtons.length > 0 || downloadButton || addToAlbumButton || linkButtons.length > 0 || revokeButton)) { + previewButton = [previewButton, JSX_("hr", { + key: "preview-sep" + })]; + } + return JSX_("div", null, previewButton, firstGroupOfButtons, firstGroupOfButtons && firstGroupOfButtons.length > 0 ? JSX_("hr", null) : "", addToAlbumButton, addToAlbumButton ? JSX_("hr", null) : "", downloadButton, linkButtons, revokeButton && downloadButton ? JSX_("hr", null) : "", revokeButton); + } + })) : JSX_(buttons.$, { + ref: ref => { + this.buttonRef = ref; + }, + className: "tiny-button", + icon: "tiny-icon icons-sprite grey-dots" + }, JSX_(dropdowns.ms, { + className: "white-context-menu attachments-dropdown", + noArrow: true, + positionMy: "left top", + positionAt: "left bottom", + horizOffset: -4, + vertOffset: 3 + }, previewButton, previewButton && JSX_("hr", null), JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-download-small", + label: l[1187], + disabled: mega.paywall, + onClick: () => this.props.onDownloadStart(v) + }), !is_chatlink && this._isUserRegistered() && JSX_(REaCt().Fragment, null, JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-cloud", + label: l[1988], + disabled: mega.paywall, + onClick: () => this.props.onAddToCloudDrive(v, false) + }), JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-send-to-chat", + label: l[17764], + disabled: mega.paywall, + onClick: () => this.props.onAddToCloudDrive(v, true) + })))); + if (M.getNodeShare(v.h).down) { + dropdown = null; + } + const attachmentClasses = "message shared-data"; + let preview = JSX_("div", { + className: `data-block-view medium ${ noThumbPrev}`, + onClick: ({ + target + }) => { + if (isPreviewable && !target.classList.contains('tiny-button')) { + mega.ui.mInfoPanel.hide(); + this.props.onPreviewStart(v); + } + } + }, dropdown, JSX_("div", { + className: "data-block-bg" + }, JSX_("div", { + className: `item-type-icon-90 icon-${ icon }-90` + }))); + if (showThumbnail) { + const src = v.src || window.noThumbURI || ''; + let thumbClass = v.src ? '' : " no-thumb"; + let thumbOverlay = null; + if (isImage) { + thumbClass += " image"; + thumbOverlay = JSX_("div", { + className: "thumb-overlay", + onClick: () => { + mega.ui.mInfoPanel.hide(); + this.props.onPreviewStart(v); + } + }); + } else { + thumbClass = `${thumbClass } video ${ isPreviewable ? " previewable" : "non-previewable"}`; + thumbOverlay = JSX_("div", { + className: "thumb-overlay", + onClick: () => { + if (isPreviewable) { + mega.ui.mInfoPanel.hide(); + this.props.onPreviewStart(v); + } + } + }, isPreviewable && JSX_("div", { + className: "thumb-overlay-play" + }, JSX_("div", { + className: "thumb-overlay-circle" + }, JSX_("i", { + className: "sprite-fm-mono icon-play" + }))), JSX_("div", { + className: "video-thumb-details" + }, v.playtime && JSX_("i", { + className: "sprite-fm-mono icon-play" + }), JSX_("span", null, secondsToTimeShort(v.playtime || -1)))); + } + preview = src ? JSX_("div", { + id: v.ch, + className: `shared-link thumb ${thumbClass}` + }, thumbOverlay, dropdown, JSX_("img", { + alt: "", + className: `thumbnail-placeholder ${ v.h}`, + src, + key: `thumb-${ v.ch}`, + onClick: () => isPreviewable && this.props.onPreviewStart(v) + })) : preview; + } + files.push(JSX_("div", { + key: `attachment-${v.ch}`, + className: attachmentClasses + }, JSX_("div", { + className: "message shared-info", + onClick: (_this$buttonRef = this.buttonRef) == null ? void 0 : _this$buttonRef.onClick + }, JSX_("div", { + className: "message data-title selectable-txt" + }, l[17669], JSX_("span", { + className: "file-name" + }, v.name)), JSX_("div", { + className: "message file-size" + }, bytesToSize(v.s))), preview, JSX_("div", { + className: "clear" + }))); + } + return JSX_("div", { + className: "message shared-block" + }, files); + } +} +// EXTERNAL MODULE: ./js/chat/mixins.js +const mixins = REQ_(8264); +;// ./js/chat/ui/messages/types/partials/audioPlayer.jsx + + +class AudioPlayer extends mixins.w9 { + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.state = { + currentTime: null, + progressWidth: 0, + isBeingPlayed: false, + isPaused: false + }; + this.handleOnTimeUpdate = this.handleOnTimeUpdate.bind(this); + this.handleOnMouseDown = this.handleOnMouseDown.bind(this); + } + play() { + const audio = this.audioEl; + if (audio.paused) { + const result = audio.play(); + if (result instanceof Promise) { + result.catch(ex => { + if (ex.name !== 'AbortError') { + console.error(ex); + } + }); + } + const audios = document.getElementsByClassName('audio-player__player'); + Array.prototype.filter.call(audios, audioElement => audioElement.id !== this.props.audioId).forEach(audioElement => { + if (!audioElement.paused) { + audioElement.pause(); + } + }); + this.setState({ + isPaused: false + }); + } else { + audio.pause(); + this.setState({ + isPaused: true + }); + } + } + handleOnTimeUpdate() { + const { + currentTime, + duration + } = this.audioEl; + this.setState({ + currentTime: secondsToTimeShort(currentTime), + progressWidth: currentTime / duration * 100 + }); + } + handleOnMouseDown(event) { + event.preventDefault(); + const { + sliderPin, + slider + } = this; + const shiftX = event.clientX - sliderPin.getBoundingClientRect().left; + const onMouseMove = event => { + let newLeft = event.clientX - shiftX - slider.getBoundingClientRect().left; + if (newLeft < 0) { + newLeft = 0; + } + const rightEdge = slider.offsetWidth - sliderPin.offsetWidth; + if (newLeft > rightEdge) { + newLeft = rightEdge; + } + sliderPin.style.left = `${newLeft}px`; + const pinPosition = newLeft / slider.getBoundingClientRect().width; + const newTime = Math.ceil(this.props.playtime * pinPosition); + const newCurrentTime = secondsToTimeShort(newTime); + this.audioEl.currentTime = newTime; + this.setState({ + currentTime: newCurrentTime, + progressWidth: pinPosition > 1 ? 100 : pinPosition * 100 + }); + }; + function onMouseUp() { + document.removeEventListener('mouseup', onMouseUp); + document.removeEventListener('mousemove', onMouseMove); + } + document.addEventListener('mousemove', onMouseMove); + document.addEventListener('mouseup', onMouseUp); + sliderPin.ondragstart = () => false; + } + render() { + const { + source, + audioId, + loading, + playtime + } = this.props; + const { + progressWidth, + isBeingPlayed, + isPaused, + currentTime + } = this.state; + let playtimeStyles = null; + if (isBeingPlayed) { + playtimeStyles = { + color: 'var(--secondary-red)' + }; + } + let btnClass = 'icon-pause'; + if (!isBeingPlayed || isPaused) { + btnClass = 'icon-play'; + } + let controls = JSX_("span", { + onClick: () => { + this.play(); + if (this.props.source === null) { + this.props.getAudioFile(); + } + } + }, JSX_("i", { + className: `sprite-fm-mono ${btnClass}` + })); + if (loading) { + controls = JSX_("div", { + className: "small-blue-spinner audio-player__spinner" + }); + } + return JSX_("div", { + ref: this.domRef, + className: "audio-player" + }, controls, JSX_("div", { + className: "slider", + ref: slider => { + this.slider = slider; + } + }, JSX_("div", { + className: "slider__progress", + style: { + width: `${progressWidth}%` + } + }), JSX_("div", { + className: "slider__progress__pin", + style: { + left: `${progressWidth}%` + }, + ref: sliderPin => { + this.sliderPin = sliderPin; + }, + onMouseDown: this.handleOnMouseDown + })), JSX_("span", { + className: "audio-player__time", + style: playtimeStyles + }, currentTime || secondsToTimeShort(playtime)), JSX_("audio", { + src: source, + className: "audio-player__player", + id: audioId, + ref: audio => { + this.audioEl = audio; + }, + onPlaying: () => this.setState({ + isBeingPlayed: true + }), + onPause: () => this.setState({ + isPaused: true + }), + onEnded: () => this.setState({ + progressWidth: 0, + isBeingPlayed: false, + currentTime: 0 + }), + onTimeUpdate: this.handleOnTimeUpdate + })); + } +} +;// ./js/chat/ui/messages/types/partials/audioContainer.jsx + + +class AudioContainer extends REaCt().Component { + constructor(props) { + super(props); + this.state = { + audioBlobUrl: null, + loading: false + }; + this.getAudioFile = this.getAudioFile.bind(this); + } + getAudioFile() { + const { + mime, + h + } = this.props; + this.setState({ + loading: true + }); + if (mime !== 'audio/mp4') { + if (d) { + console.warn('cannot play this file type (%s)', mime, h, [this]); + } + return false; + } + M.gfsfetch(h, 0, -1).then(({ + buffer + }) => { + this.setState(() => { + return { + audioBlobUrl: mObjectURL([buffer], 'audio/mp4'), + loading: false + }; + }); + }).catch(ex => { + console.error(ex); + }); + return true; + } + componentWillUnmount() { + URL.revokeObjectURL(this.state.audioBlobUrl); + } + render() { + const { + audioBlobUrl, + loading + } = this.state; + const { + playtime, + mime, + audioId + } = this.props; + return JSX_("div", { + className: "audio-container" + }, JSX_(AudioPlayer, { + source: audioBlobUrl, + audioId, + loading, + mime, + getAudioFile: this.getAudioFile, + playtime + })); + } +} +AudioContainer.defaultProps = { + h: null, + mime: null +}; +;// ./js/chat/ui/messages/types/voiceClip.jsx + + + + + +class VoiceClip extends AbstractGenericMessage { + _getActionButtons() { + const { + isBeingEdited, + chatRoom, + message, + dialog, + onDelete + } = this.props; + if (message.isEditable() && !isBeingEdited() && !chatRoom.isReadOnly() && !dialog) { + return JSX_(buttons.$, { + className: "tiny-button", + icon: "tiny-icon icons-sprite grey-dots" + }, JSX_(dropdowns.ms, { + className: "white-context-menu attachments-dropdown", + noArrow: true, + positionMy: "left bottom", + positionAt: "right bottom", + horizOffset: 4 + }, JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-dialog-close", + label: l[1730], + onClick: ev => onDelete(ev, message) + }))); + } + return null; + } + _getAudioContainer() { + const { + message + } = this.props; + const attachmentMeta = message.getAttachmentMeta(); + if (attachmentMeta && attachmentMeta.length) { + return attachmentMeta.map(voiceClip => JSX_(AudioContainer, { + key: voiceClip.h, + h: voiceClip.h, + mime: voiceClip.mime, + playtime: voiceClip.playtime, + audioId: `vm${message.messageId}` + })); + } + } + getContents() { + return JSX_(REaCt().Fragment, null, this.props.message.getState() === Message.STATE.NOT_SENT ? null : this._getActionButtons(), this._getAudioContainer()); + } +} +;// ./js/chat/ui/messages/types/partials/metaRichpreviewLoading.jsx + + +class MetaRichpreviewLoading extends mixin.M { + render() { + return JSX_("div", { + className: "loading-spinner light small" + }, JSX_("div", { + className: "main-loader" + })); + } +} + +;// ./js/chat/ui/messages/types/partials/metaRichpreview.jsx + + + +class MetaRichpreview extends mixin.M { + getBase64Url(b64incoming) { + if (!b64incoming || !b64incoming.split) { + return; + } + let exti = b64incoming.split(":"); + const b64i = exti[1]; + exti = exti[0]; + return `data:image/${ exti };base64,${ b64i}`; + } + render() { + const self = this; + const {message} = this.props; + const output = []; + const metas = message.meta && message.meta.extra ? message.meta.extra : []; + const failedToLoad = message.meta.isLoading && unixtime() - message.meta.isLoading > 300; + const isLoading = !!message.meta.isLoading; + if (failedToLoad) { + return null; + } + for (let i = 0; i < metas.length; i++) { + const meta = metas[i]; + if (!meta.d && !meta.t && !message.meta.isLoading) { + continue; + } + const previewCss = {}; + if (meta.i) { + previewCss.backgroundImage = `url(${ self.getBase64Url(meta.i) })`; + previewCss.backgroundRepeat = "no-repeat"; + previewCss.backgroundPosition = "center center"; + } + var previewContainer; + if (isLoading) { + previewContainer = JSX_(MetaRichpreviewLoading, { + message, + isLoading: message.meta.isLoading + }); + } else { + let domainName = meta.url; + domainName = domainName.replace("https://", "").replace("http://", "").split("/")[0]; + previewContainer = JSX_("div", { + className: "message richpreview body" + }, meta.i ? JSX_("div", { + className: "message richpreview img-wrapper" + }, JSX_("div", { + className: "message richpreview preview", + style: previewCss + })) : undefined, JSX_("div", { + className: "message richpreview inner-wrapper" + }, JSX_("div", { + className: "message richpreview data-title selectable-txt" + }, JSX_("span", { + className: "message richpreview title" + }, meta.t)), JSX_("div", { + className: "message richpreview desc" + }, ellipsis(meta.d, 'end', 82)), JSX_("div", { + className: "message richpreview url-container" + }, meta.ic ? JSX_("span", { + className: "message richpreview url-favicon" + }, JSX_("img", { + src: self.getBase64Url(meta.ic), + width: 16, + height: 16, + onError: e => { + e.target.parentNode.removeChild(e.target); + }, + alt: "" + })) : "", JSX_("span", { + className: "message richpreview url" + }, domainName)))); + } + output.push(JSX_("div", { + key: meta.url, + className: `message richpreview container ${ meta.i ? "have-preview" : "no-preview" } ${ meta.d ? "have-description" : "no-description" } ${ isLoading ? "is-loading" : "done-loading"}`, + onClick: function (url) { + if (!message.meta.isLoading) { + window.open(url, "_blank", 'noopener,noreferrer'); + } + }.bind(this, meta.url) + }, previewContainer, JSX_("div", { + className: "clear" + }))); + } + return JSX_("div", { + className: "message richpreview previews-container" + }, output); + } +} + +;// ./js/chat/ui/messages/types/partials/metaRichpreviewConfirmation.jsx + + +class MetaRichprevConfirmation extends mixin.M { + doAllow() { + const {message} = this.props; + const {megaChat} = this.props.message.chatRoom; + delete message.meta.requiresConfirmation; + RichpreviewsFilter.confirmationDoConfirm(); + megaChat.plugins.richpreviewsFilter.processMessage({}, message); + message.trackDataChange(); + } + doNotNow() { + const {message} = this.props; + delete message.meta.requiresConfirmation; + RichpreviewsFilter.confirmationDoNotNow(); + message.trackDataChange(); + } + doNever() { + const {message} = this.props; + msgDialog('confirmation', l[870], l[18687], '', (e) => { + if (e) { + delete message.meta.requiresConfirmation; + RichpreviewsFilter.confirmationDoNever(); + message.trackDataChange(); + } + }); + } + render() { + const self = this; + let notNowButton = null; + let neverButton = null; + if (RichpreviewsFilter.confirmationCount >= 2) { + neverButton = JSX_("button", { + className: "mega-button right negative", + onClick () { + self.doNever(); + } + }, JSX_("span", null, l[1051])); + } + notNowButton = JSX_("button", { + className: "mega-button right", + onClick () { + self.doNotNow(); + } + }, JSX_("span", null, l[18682])); + return JSX_("div", { + className: "message richpreview previews-container" + }, JSX_("div", { + className: "message richpreview container confirmation" + }, JSX_("div", { + className: "message richpreview body" + }, JSX_("div", { + className: "message richpreview img-wrapper" + }, JSX_("div", { + className: " message richpreview preview-confirmation sprite-fm-illustration img-chat-url-preview " + })), JSX_("div", { + className: "message richpreview inner-wrapper" + }, JSX_("div", { + className: "message richpreview data-title selectable-txt" + }, JSX_("span", { + className: "message richpreview title" + }, l[18679])), JSX_("div", { + className: "message richpreview desc" + }, l[18680])), JSX_("div", { + className: "buttons-block" + }, JSX_("button", { + className: "mega-button right positive", + onClick: () => { + self.doAllow(); + } + }, JSX_("span", null, l[18681])), notNowButton, neverButton)), JSX_("div", { + className: "clear" + }))); + } +} + +;// ./js/chat/ui/messages/types/partials/geoLocation.jsx + +function GeoLocation(props) { + const { + latitude, + lng + } = props; + const handleOnclick = (lat, lng) => { + const openGmaps = () => { + window.open(`https://www.google.com/maps/search/?api=1&query=${lat},${lng}`, '_blank', 'noopener,noreferrer'); + }; + if (GeoLocationLinks.gmapsConfirmation === -1 || GeoLocationLinks.gmapsConfirmation === false) { + msgDialog('confirmation', 'geolocation-link', l[20788], l.confirm_ext_link, answer => { + if (answer) { + GeoLocationLinks.confirmationDoConfirm(); + closeDialog(); + openGmaps(); + } else { + GeoLocationLinks.confirmationDoNever(); + } + }); + } else if (GeoLocationLinks.gmapsConfirmation) { + openGmaps(); + } + }; + return JSX_("div", { + className: "geolocation-container" + }, JSX_("div", { + className: "geolocation", + onClick: () => handleOnclick(latitude, lng) + }, JSX_("div", { + className: "geolocation__details" + }, JSX_("div", { + className: "geolocation__icon" + }, JSX_("i", { + className: "sprite-fm-mono icon-location" + })), JSX_("ul", { + className: "geolocation__data-list" + }, JSX_("li", null, JSX_("span", { + className: "geolocation__title" + }, l[20789])), JSX_("li", null, JSX_("p", null, JSX_("span", { + className: "geolocation__coordinates-icon" + }), JSX_("span", { + className: "geolocation__coordinates" + }, "https://maps.google.com"))))))); +} + const geoLocation = GeoLocation; +;// ./js/chat/ui/messages/types/partials/metaRichpreviewMegaLinks.jsx + + + + + +class MetaRichpreviewMegaLinks extends mixin.M { + render() { + const {message} = this.props; + const {chatRoom} = this.props.message; + let previewContainer; + const output = []; + const megaLinks = message.megaLinks ? message.megaLinks : []; + for (let i = 0; i < megaLinks.length; i++) { + const megaLinkInfo = megaLinks[i]; + if (megaLinkInfo.failed) { + continue; + } + if (megaLinkInfo.hadLoaded() === false) { + if (megaLinkInfo.startedLoading() === false) { + megaLinkInfo.getInfo().then(() => { + const { + megaLinks + } = this.props.message; + const contactLinkHandles = megaLinks.filter(link => link.is_contactlink).map(link => link.info.h); + if (contactLinkHandles.length) { + this.addContactListenerIfMissing(contactLinkHandles); + } + }).catch(reportError).finally(() => { + message.trackDataChange(); + onIdle(() => { + this.safeForceUpdate(); + }); + }); + } + previewContainer = JSX_(MetaRichpreviewLoading, { + message, + isLoading: megaLinkInfo.hadLoaded() + }); + } else if (megaLinkInfo.is_contactlink) { + const fakeContact = M.u[megaLinkInfo.info.h] ? M.u[megaLinkInfo.info.h] : { + 'u': megaLinkInfo.info.h, + 'm': megaLinkInfo.info.e, + 'firstName': megaLinkInfo.info.fn, + 'lastName': megaLinkInfo.info.ln, + 'name': `${megaLinkInfo.info.fn } ${ megaLinkInfo.info.ln}` + }; + if (!M.u[fakeContact.u]) { + M.u.set(fakeContact.u, new MegaDataObject(MEGA_USER_STRUCT, { + 'u': fakeContact.u, + 'name': `${fakeContact.firstName } ${ fakeContact.lastName}`, + 'm': fakeContact.m ? fakeContact.m : "", + 'c': undefined + })); + } + const contact = M.u[megaLinkInfo.info.h]; + previewContainer = JSX_("div", { + key: megaLinkInfo.info.h, + className: "message shared-block contact-link" + }, JSX_("div", { + className: "message shared-info" + }, JSX_("div", { + className: "message data-title selectable-txt" + }, contact.name), JSX_(ui_contacts.n4, { + className: "right-align", + contact + }), JSX_("div", { + className: "user-card-email selectable-txt" + }, contact.m)), JSX_("div", { + className: "message shared-data" + }, JSX_("div", { + className: "data-block-view semi-big" + }, JSX_(ui_contacts.i1, { + className: "small", + contact + }), JSX_(ui_contacts.eu, { + className: "avatar-wrapper medium-avatar", + contact, + chatRoom + })), JSX_("div", { + className: "clear" + }))); + } else { + var desc; + const is_icon = megaLinkInfo.is_dir ? true : !(megaLinkInfo.havePreview() && megaLinkInfo.info.preview_url); + if (megaLinkInfo.is_chatlink) { + desc = l[8876].replace('%1', megaLinkInfo.info.ncm); + } else if (!megaLinkInfo.is_dir) { + desc = bytesToSize(megaLinkInfo.info.size); + } else { + const totalNumberOfFiles = megaLinkInfo.info.s[1]; + const numOfVersionedFiles = megaLinkInfo.info.s[4]; + const folderCount = megaLinkInfo.info.s[2]; + const totalFileSize = megaLinkInfo.info.size; + const versionsSize = megaLinkInfo.info.s[3]; + desc = JSX_("span", null, fm_contains(totalNumberOfFiles - numOfVersionedFiles, folderCount - 1), JSX_("br", null), bytesToSize(totalFileSize - versionsSize)); + } + previewContainer = JSX_("div", { + className: `message richpreview body ${ is_icon ? "have-icon" : "no-icon" } ${ megaLinkInfo.is_chatlink ? "is-chat" : ""}` + }, megaLinkInfo.havePreview() && megaLinkInfo.info.preview_url ? JSX_("div", { + className: "message richpreview img-wrapper" + }, JSX_("div", { + className: "message richpreview preview", + style: { + "backgroundImage": `url(${ megaLinkInfo.info.preview_url })` + } + })) : JSX_("div", { + className: "message richpreview img-wrapper" + }, megaLinkInfo.is_chatlink ? JSX_("i", { + className: "huge-icon conversations" + }) : JSX_("div", { + className: `message richpreview icon item-type-icon-90 icon-${ megaLinkInfo.is_dir ? "folder" : fileIcon(megaLinkInfo.info) }-90` + })), JSX_("div", { + className: "message richpreview inner-wrapper" + }, JSX_("div", { + className: "message richpreview data-title selectable-txt" + }, JSX_("span", { + className: "message richpreview title" + }, JSX_(utils.zT, null, megaLinkInfo.info.name || megaLinkInfo.info.topic || ""))), JSX_("div", { + className: "message richpreview desc" + }, desc), JSX_("div", { + className: "message richpreview url-container" + }, JSX_("span", { + className: "message richpreview url-favicon" + }, JSX_("img", { + src: `https://mega.${mega.tld}/favicon.ico?v=3&c=1`, + width: 16, + height: 16, + onError: e => { + if (e && e.target && e.target.parentNode) { + e.target.parentNode.removeChild(e.target); + } + }, + alt: "" + })), JSX_("span", { + className: "message richpreview url" + }, ellipsis(megaLinkInfo.getLink(), 'end', 40))))); + } + output.push(JSX_("div", { + key: `${megaLinkInfo.node_key }_${ output.length}`, + className: `message richpreview container ${ megaLinkInfo.havePreview() ? "have-preview" : "no-preview" } ${ megaLinkInfo.d ? "have-description" : "no-description" } ${ !megaLinkInfo.hadLoaded() ? "is-loading" : "done-loading"}`, + onClick: function (url, megaLinkInfo) { + if (megaLinkInfo.hadLoaded()) { + if (window.sfuClient && megaLinkInfo.is_chatlink) { + const { + chatRoom: callRoom + } = megaChat.activeCall; + const peers = callRoom ? callRoom.getParticipantsExceptMe(callRoom.getCallParticipants()).map(h => M.getNameByHandle(h)) : []; + const body = peers.length ? mega.utils.trans.listToString(peers, l.cancel_with_to_join) : l.cancel_to_join; + return msgDialog('confirmation', undefined, l.call_in_progress, body, e => e && window.open(url, '_blank', 'noopener,noreferrer')); + } + window.open(url, '_blank', 'noopener,noreferrer'); + } + }.bind(this, megaLinkInfo.getLink(), megaLinkInfo) + }, previewContainer, JSX_("div", { + className: "clear" + }))); + } + return JSX_("div", { + className: "message richpreview previews-container" + }, output); + } +} + +// EXTERNAL MODULE: ./js/chat/ui/typingArea.jsx + 5 modules +const typingArea = REQ_(4762); +// EXTERNAL MODULE: ./js/ui/perfectScrollbar.jsx +const perfectScrollbar = REQ_(1301); +;// ./js/chat/ui/messages/types/text.jsx + + + + + + + + + + + + +class Text extends AbstractGenericMessage { + constructor(props) { + super(props); + this.state = { + editText: '' + }; + } + isRichPreview(message) { + return message.metaType === Message.MESSAGE_META_TYPE.RICH_PREVIEW; + } + isGeoLocation(message) { + return message.metaType === Message.MESSAGE_META_TYPE.GEOLOCATION; + } + getClassNames() { + const { + message, + isBeingEdited, + grouped + } = this.props; + const REQUIRES_CONFIRMATION = this.isRichPreview(message) && message.meta.requiresConfirmation && !isBeingEdited() && (message.source === Message.SOURCE.SENT || message.confirmed === true); + return ` + ${REQUIRES_CONFIRMATION ? 'preview-requires-confirmation-container' : ''} + ${grouped ? 'grouped' : ''} + `; + } + renderMessageIndicators() { + const { + message, + spinnerElement, + isBeingEdited, + onRetry, + onCancelRetry + } = this.props; + if (!message || spinnerElement || isBeingEdited()) { + return null; + } + const state = message.getState == null ? void 0 : message.getState(); + if (![Message.STATE.NOT_SENT, Message.STATE.NOT_SENT_EXPIRED].includes(state)) { + return null; + } + const props = { + 'data-simpletipposition': 'top', + 'data-simpletipoffset': 8 + }; + return message.requiresManualRetry ? JSX_("div", { + className: "not-sent-indicator clickable" + }, JSX_("span", (0,esm_extends.A)({ + className: "simpletip" + }, props, { + "data-simpletip": l[8883], + onClick: ev => onRetry(ev, message) + }), JSX_("i", { + className: "small-icon refresh-circle" + })), JSX_("span", (0,esm_extends.A)({ + className: "simpletip" + }, props, { + "data-simpletip": l[8884], + onClick: ev => onCancelRetry(ev, message) + }), JSX_("i", { + className: "sprite-fm-mono icon-dialog-close" + }))) : JSX_("div", (0,esm_extends.A)({ + className: "not-sent-indicator simpletip" + }, props, { + "data-simpletip": l[8882] + }), JSX_("i", { + className: "small-icon yellow-triangle" + })); + } + getMessageActionButtons() { + const { + chatRoom, + message, + isBeingEdited + } = this.props; + if (isBeingEdited()) { + return []; + } + let extraPreButtons = []; + let messageActionButtons = null; + const IS_GEOLOCATION = this.isGeoLocation(message); + if (!message.deleted && this.isRichPreview(message)) { + if (!message.meta.requiresConfirmation) { + if (message.isEditable()) { + if (message.meta.isLoading) { + extraPreButtons = [...extraPreButtons, JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-eye-hidden", + key: "stop-link-preview", + label: l[18684], + className: "", + onClick: e => { + e.stopPropagation(); + e.preventDefault(); + chatRoom.megaChat.plugins.richpreviewsFilter.cancelLoading(chatRoom, message); + } + })]; + } else { + extraPreButtons = [...extraPreButtons, JSX_(dropdowns.tJ, { + key: "remove-link-preview", + icon: "sprite-fm-mono icon-eye-hidden", + label: l[18684], + className: "", + onClick: e => { + e.stopPropagation(); + e.preventDefault(); + chatRoom.megaChat.plugins.richpreviewsFilter.revertToText(chatRoom, message); + } + })]; + } + } + } else if (!isBeingEdited() && !(message.source === Message.SOURCE.SENT || message.confirmed === true)) { + extraPreButtons = [...extraPreButtons, JSX_(dropdowns.tJ, { + key: "insert-link-preview", + icon: "icons-sprite bold-eye", + label: l[18683], + className: "", + onClick: e => { + e.stopPropagation(); + e.preventDefault(); + chatRoom.megaChat.plugins.richpreviewsFilter.insertPreview(message); + } + })]; + } + } + if (!message.deleted && message.isEditable() && !isBeingEdited() && !chatRoom.isReadOnly() && !message.requiresManualRetry) { + const editButton = !IS_GEOLOCATION && JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-rename", + label: l[1342], + onClick: () => this.props.onEditToggle(true) + }); + messageActionButtons = JSX_(buttons.$, { + key: "delete-msg", + className: "tiny-button", + icon: "sprite-fm-mono icon-options" + }, JSX_(dropdowns.ms, { + className: "white-context-menu attachments-dropdown", + noArrow: true, + positionMy: "left bottom", + positionAt: "right bottom", + horizOffset: 4 + }, extraPreButtons, editButton, editButton ? JSX_("hr", null) : null, JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-dialog-close", + label: l[1730], + onClick: e => this.props.onDelete(e, message) + }))); + } + let parentButtons; + if (super.getMessageActionButtons) { + parentButtons = super.getMessageActionButtons(); + } + const returnedButtons = []; + if (messageActionButtons) { + returnedButtons.push(messageActionButtons); + } + if (message.messageHtml && message.messageHtml.includes('
') && message.messageHtml.includes('
')) { + returnedButtons.push(JSX_(buttons.$, { + key: "copy-msg", + className: "tiny-button simpletip copy-txt-block", + icon: "sprite-fm-mono icon-copy", + attrs: { + 'data-simpletip': l.copy_txt_block_tip, + 'data-simpletipoffset': '3', + 'data-simpletipposition': 'top' + }, + onClick: () => { + copyToClipboard(message.textContents.replace(/```/g, ''), l.copy_txt_block_toast); + } + })); + } + if (parentButtons) { + returnedButtons.push(parentButtons); + } + return returnedButtons; + } + getContents() { + const { + message, + chatRoom, + onUpdate, + isBeingEdited, + spinnerElement + } = this.props; + let textMessage = message.messageHtml; + const IS_GEOLOCATION = this.isGeoLocation(message); + const { + lng, + la: latitude + } = IS_GEOLOCATION && message.meta.extra[0]; + if (message.textContents === '' && !message.dialogType) { + message.deleted = true; + } + let subMessageComponent = []; + if (!message.deleted) { + if (this.isRichPreview(message)) { + if (!message.meta.requiresConfirmation) { + subMessageComponent = [...subMessageComponent, JSX_(MetaRichpreview, { + key: "richprev", + message, + chatRoom + })]; + } else if (!isBeingEdited()) { + if (message.source === Message.SOURCE.SENT || message.confirmed === true) { + subMessageComponent = [...subMessageComponent, JSX_(MetaRichprevConfirmation, { + key: "confirm", + message, + chatRoom + })]; + } + } + } + if (message.megaLinks) { + subMessageComponent = [...subMessageComponent, JSX_(MetaRichpreviewMegaLinks, { + key: "richprevml", + message, + chatRoom + })]; + } + } + let messageDisplayBlock; + if (isBeingEdited() === true) { + let msgContents = message.textContents; + msgContents = megaChat.plugins.emoticonsFilter.fromUtfToShort(msgContents); + messageDisplayBlock = JSX_(typingArea.T, { + iconClass: "small-icon writing-pen textarea-icon", + initialText: msgContents, + text: this.state.editText || msgContents, + chatRoom, + showButtons: true, + editing: true, + className: "edit-typing-area", + onUpdate: () => onUpdate ? onUpdate : null, + onConfirm: messageContents => { + this.props.onEditToggle(false); + if (this.props.onEditDone) { + Soon(() => { + const tmpMessageObj = { + textContents: messageContents + }; + megaChat.plugins.emoticonsFilter.processOutgoingMessage({}, tmpMessageObj); + this.props.onEditDone(tmpMessageObj.textContents); + if (this.isMounted()) { + this.forceUpdate(); + } + }); + } + return true; + }, + onResized: this.props.onResized ? this.props.onResized : false, + onValueChanged: val => { + this.setState({ + editText: val + }); + } + }); + } else { + if (message.updated > 0 && !message.metaType) { + textMessage = `${textMessage} ${l[8887]} `; + } + if (this.props.initTextScrolling) { + messageDisplayBlock = JSX_(perfectScrollbar.O, { + className: "message text-block scroll" + }, JSX_("div", { + className: "message text-scroll" + }, JSX_(utils.P9, null, textMessage))); + } else { + messageDisplayBlock = JSX_("div", { + className: "message text-block" + }, JSX_(utils.P9, null, textMessage)); + } + } + return JSX_(REaCt().Fragment, null, this.renderMessageIndicators(), IS_GEOLOCATION ? null : messageDisplayBlock, subMessageComponent, spinnerElement, IS_GEOLOCATION && JSX_(geoLocation, { + latitude, + lng + })); + } +} +// EXTERNAL MODULE: ./js/chat/ui/gifPanel/utils.jsx +const gifPanel_utils = REQ_(1635); +;// ./js/chat/ui/messages/types/giphy.jsx + + + + + +class Giphy extends AbstractGenericMessage { + constructor(...args) { + super(...args); + this.gifRef = REaCt().createRef(); + this.viewStateListener = `viewstateChange.giphy--${this.getUniqueId()}`; + this.state = { + src: undefined + }; + } + componentDidMount() { + super.componentDidMount(); + megaChat.rebind(this.viewStateListener, ({ + data + }) => { + const gifRef = this.gifRef && this.gifRef.current; + if (gifRef) { + const { + state + } = data; + if (state === 'active' && gifRef.paused || state !== 'active' && !gifRef.paused) { + this.toggle(); + } + } + }); + } + componentWillUnmount() { + super.componentWillUnmount(); + megaChat.off(this.viewStateListener); + } + onVisibilityChange(isIntersecting) { + this.setState({ + src: isIntersecting ? gifPanel_utils.nC.convert(this.props.message.meta.src) : undefined + }, () => { + let _this$gifRef; + (_this$gifRef = this.gifRef) == null || (_this$gifRef = _this$gifRef.current) == null || _this$gifRef[isIntersecting ? 'load' : 'pause'](); + this.safeForceUpdate(); + }); + } + toggle() { + const video = this.gifRef.current; + Promise.resolve(video[video.paused ? 'play' : 'pause']()).catch(nop); + } + getMessageActionButtons() { + const { + onDelete, + message + } = this.props; + const $$BUTTONS = [message.isEditable() && JSX_(buttons.$, { + key: "delete-GIPHY-button", + className: "tiny-button", + icon: "sprite-fm-mono icon-options" + }, JSX_(dropdowns.ms, { + className: "white-context-menu attachments-dropdown", + noArrow: true, + positionMy: "left bottom", + positionAt: "right bottom", + horizOffset: 4 + }, JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono icon-dialog-close", + label: l[1730], + onClick: e => onDelete(e, message) + }))), super.getMessageActionButtons && super.getMessageActionButtons()]; + return $$BUTTONS.filter(button => button); + } + getContents() { + const { + message, + hideActionButtons + } = this.props; + const { + s, + w, + h, + src + } = message.meta; + const autoPlay = parseInt(s, 10) < 4e6; + return JSX_("video", { + className: "giphy-block", + ref: this.gifRef, + title: message.textContents, + autoPlay, + loop: true, + muted: true, + controls: false, + width: w, + height: h, + style: { + cursor: autoPlay ? 'default' : 'pointer', + height: `${h}px` + }, + onClick: () => !autoPlay && this.toggle(), + src: hideActionButtons ? gifPanel_utils.nC.convert(src) : this.state.src + }); + } +} +;// ./js/chat/ui/messages/generic.jsx + + + + + + + + + + +class GenericConversationMessage extends mixin.M { + constructor(props) { + super(props); + this.containerRef = REaCt().createRef(); + this.state = { + editing: this.props.editing + }; + this.pid = `__geom_${ String(Math.random()).substr(2)}`; + } + isBeingEdited() { + return this.state.editing === true || this.props.editing === true; + } + componentDidUpdate(oldProps, oldState) { + const isBeingEdited = this.isBeingEdited(); + const isMounted = this.isMounted(); + if (isBeingEdited && isMounted) { + let _this$containerRef; + const $generic = $((_this$containerRef = this.containerRef) == null ? void 0 : _this$containerRef.current); + const $textarea = $('textarea', $generic); + if ($textarea.length > 0 && !$textarea.is(":focus")) { + $textarea.trigger("focus"); + moveCursortoToEnd($textarea[0]); + } + if (!oldState.editing && this.props.onEditStarted) { + this.props.onEditStarted($generic); + moveCursortoToEnd($textarea); + } + } + if (isMounted && !isBeingEdited && oldState.editing === true && this.props.onUpdate) { + this.props.onUpdate(); + } + } + componentDidMount() { + let _this$containerRef2; + super.componentDidMount(); + const $node = $((_this$containerRef2 = this.containerRef) == null ? void 0 : _this$containerRef2.current); + if (this.isBeingEdited() && this.isMounted()) { + const $textarea = $('textarea', $node); + if ($textarea.length > 0 && !$textarea.is(':focus')) { + $textarea.trigger('focus'); + moveCursortoToEnd($textarea[0]); + } + } + } + haveMoreContactListeners() { + if (!this.props.message || !this.props.message.meta) { + return false; + } + if (this.props.message.meta && this.props.message.meta.participants) { + return this.props.message.meta.participants; + } + return false; + } + doDelete(e, msg) { + e.preventDefault(e); + e.stopPropagation(e); + if (msg.getState() === Message.STATE.NOT_SENT_EXPIRED) { + this.doCancelRetry(e, msg); + } else { + this.props.onDeleteClicked(this.props.message); + } + } + doCancelRetry(e, msg) { + e.preventDefault(e); + e.stopPropagation(e); + const {chatRoom} = this.props.message; + const {messageId} = msg; + chatRoom.messagesBuff.messages.removeByKey(messageId); + chatRoom.megaChat.plugins.chatdIntegration.discardMessage(chatRoom, messageId); + } + doRetry(e, msg) { + e.preventDefault(e); + e.stopPropagation(e); + const {chatRoom} = this.props.message; + this.doCancelRetry(e, msg); + chatRoom._sendMessageToTransport(msg).then(internalId => { + msg.internalId = internalId; + this.safeForceUpdate(); + }); + } + _favourite(h) { + if (M.isInvalidUserStatus()) { + return; + } + const newFavState = Number(!M.isFavourite(h)); + M.favourite([h], newFavState); + } + _addFavouriteButtons(h, arr) { + const self = this; + if (M.getNodeRights(h) > 1) { + const isFav = M.isFavourite(h); + arr.push(JSX_(dropdowns.tJ, { + icon: ` + sprite-fm-mono + context + ${isFav ? 'icon-favourite-removed' : 'icon-favourite'} + `, + label: isFav ? l[5872] : l[5871], + isFav, + key: "fav", + disabled: mega.paywall, + onClick: e => { + self._favourite(h); + e.stopPropagation(); + e.preventDefault(); + return false; + } + })); + return isFav; + } + return false; + } + _isNodeHavingALink(h) { + return M.getNodeShare(h) !== false; + } + _addLinkButtons(h, arr) { + const self = this; + const haveLink = self._isNodeHavingALink(h) === true; + const getManageLinkText = haveLink ? l[6909] : l[5622]; + arr.push(JSX_(dropdowns.tJ, { + icon: `sprite-fm-mono icon-link${haveLink ? '-gear' : ''}`, + key: "getLinkButton", + label: getManageLinkText, + disabled: mega.paywall, + onClick: self._getLink.bind(self, h) + })); + if (haveLink) { + arr.push(JSX_(dropdowns.tJ, { + icon: "sprite-fm-mono context icon-link-remove", + key: "removeLinkButton", + label: l[6821], + disabled: mega.paywall, + onClick: self._removeLink.bind(self, h) + })); + return true; + } + return false; + } + _startDownload(v) { + M.addDownload([v]); + } + _addToCloudDrive(v, openSendToChat) { + $.selected = [v.h]; + $.chatAttachmentShare = true; + if ($.dialog === 'onboardingDialog') { + closeDialog(); + } + if (openSendToChat) { + openSendToChatDialog(); + return; + } + openSaveToDialog(v, (node, target) => { + target = target || M.RootID; + M.injectNodes(node, target, res => { + if (!Array.isArray(res) || !res.length) { + if (d) { + console.warn('Unable to inject nodes... no longer existing?', res); + } + } else { + mega.ui.toast.show(mega.icu.format(l.toast_import_file, res.length).replace('%s', M.getNameByHandle(target)), 4, l[16797], { + actionButtonCallback() { + M.openFolder(target).then(() => { + if (window.selectionManager) { + let reset = false; + for (let i = res.length; i--;) { + const n = M.getNodeByHandle(res[i]); + if (n.p === target) { + if (reset) { + selectionManager.add_to_selection(n.h); + } else { + selectionManager.resetTo(n.h); + reset = true; + } + } + } + } + }).catch(dump); + } + }); + } + }); + }, false); + } + _getLink(h, e) { + if (u_type === 0) { + ephemeralDialog(l[1005]); + } else { + $.selected = [h]; + mega.Share.initCopyrightsDialog([h]); + } + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + } + _removeLink(h, e) { + if (u_type === 0) { + ephemeralDialog(l[1005]); + } else { + const exportLink = new mega.Share.ExportLink({ + 'updateUI': true, + 'nodesToProcess': [h] + }); + exportLink.removeExportLink(); + } + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + } + _startPreview(v, e) { + if ($(e && e.target).is('.tiny-button')) { + return; + } + assert(M.chat, 'Not in chat.'); + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + M.viewMediaFile(v); + } + render() { + const { + message, + chatRoom + } = this.props; + const {megaChat} = this.props.message.chatRoom; + const {textContents} = message; + let additionalClasses = ""; + let spinnerElement = null; + let messageIsNowBeingSent = false; + if (this.props.className) { + additionalClasses += this.props.className; + } + if (message instanceof Message) { + if (!message.wasRendered || !message.messageHtml) { + message.messageHtml = htmlentities(textContents).replace(/\n/gi, "
").replace(/\t/g, ' '); + message.processedBy = {}; + const evtObj = { + message, + room: chatRoom + }; + megaChat.trigger('onPreBeforeRenderMessage', evtObj); + const event = new MegaDataEvent('onBeforeRenderMessage'); + megaChat.trigger(event, evtObj); + megaChat.trigger('onPostBeforeRenderMessage', evtObj); + if (event.isPropagationStopped()) { + this.logger.warn(`Event propagation stopped receiving (rendering) of message: ${message}`); + return false; + } + message.wasRendered = 1; + } + const state = message.getState(); + const stateText = message.getStateText(state); + if (state === Message.STATE.NOT_SENT) { + messageIsNowBeingSent = unixtime() - message.delay < 5; + if (messageIsNowBeingSent) { + additionalClasses += ' sending'; + spinnerElement = JSX_("div", { + className: "small-blue-spinner" + }); + if (!message.sending) { + message.sending = true; + delay(this.pid + message.messageId, () => { + if (chatRoom.messagesBuff.messages[message.messageId] && message.sending === true) { + chatRoom.messagesBuff.trackDataChange(); + if (this.isMounted()) { + this.forceUpdate(); + } + } + }, (5 - (unixtime() - message.delay)) * 1000); + } + } else { + additionalClasses += ' not-sent'; + if (message.sending === true) { + message.sending = false; + message.trigger('onChange', [message, 'sending', true, false]); + } + if (message.requiresManualRetry) { + additionalClasses += ' retrying requires-manual-retry'; + } else { + additionalClasses += ' retrying'; + } + } + } else { + additionalClasses += ` ${ stateText}`; + } + } + const MESSAGE = { + TYPE: { + ATTACHMENT: textContents[1] === Message.MANAGEMENT_MESSAGE_TYPES.ATTACHMENT, + CONTACT: textContents[1] === Message.MANAGEMENT_MESSAGE_TYPES.CONTACT, + REVOKE_ATTACHMENT: textContents[1] === Message.MANAGEMENT_MESSAGE_TYPES.REVOKE_ATTACHMENT, + VOICE_CLIP: textContents[1] === Message.MANAGEMENT_MESSAGE_TYPES.VOICE_CLIP, + GIPHY: message.metaType && message.metaType === Message.MESSAGE_META_TYPE.GIPHY, + TEXT: textContents[0] !== Message.MANAGEMENT_MESSAGE_TYPES.MANAGEMENT, + INLINE: !(message instanceof Message) && message.type && !!message.type.length, + REVOKED: message.revoked + }, + props: { + ...this.props, + additionalClasses + }, + isBeingEdited: () => this.isBeingEdited(), + onDelete: (e, message) => this.doDelete(e, message) + }; + const $$CONTAINER = children => JSX_("div", { + ref: this.containerRef + }, children); + switch (true) { + case MESSAGE.TYPE.REVOKED || MESSAGE.TYPE.REVOKE_ATTACHMENT: + return null; + case MESSAGE.TYPE.ATTACHMENT: + return $$CONTAINER(JSX_(Attachment, (0,esm_extends.A)({}, MESSAGE.props, { + onPreviewStart: (v, e) => this._startPreview(v, e), + onDownloadStart: v => this._startDownload(v), + onAddLinkButtons: (h, arr) => this._addLinkButtons(h, arr), + onAddToCloudDrive: (v, openSendToChat) => this._addToCloudDrive(v, openSendToChat), + onAddFavouriteButtons: (h, arr) => this._addFavouriteButtons(h, arr) + }))); + case MESSAGE.TYPE.CONTACT: + return $$CONTAINER(JSX_(Contact, (0,esm_extends.A)({}, MESSAGE.props, { + onDelete: MESSAGE.onDelete + }))); + case MESSAGE.TYPE.VOICE_CLIP: + return $$CONTAINER(JSX_(VoiceClip, (0,esm_extends.A)({}, MESSAGE.props, { + isBeingEdited: MESSAGE.isBeingEdited, + onDelete: MESSAGE.onDelete + }))); + case MESSAGE.TYPE.INLINE: + return $$CONTAINER(JSX_(Local, MESSAGE.props)); + case MESSAGE.TYPE.GIPHY: + return $$CONTAINER(JSX_(Giphy, (0,esm_extends.A)({}, MESSAGE.props, { + onDelete: MESSAGE.onDelete + }))); + case MESSAGE.TYPE.TEXT: + return $$CONTAINER(JSX_(Text, (0,esm_extends.A)({}, MESSAGE.props, { + onEditToggle: editing => this.setState({ + editing + }), + onDelete: MESSAGE.onDelete, + onRetry: (e, message) => this.doRetry(e, message), + onCancelRetry: (e, message) => this.doCancelRetry(e, message), + isBeingEdited: MESSAGE.isBeingEdited, + spinnerElement + }))); + default: + return null; + } + } +} + + }, + + 4762 +(_, EXP_, REQ_) { + + +// EXPORTS +REQ_.d(EXP_, { + T: () => TypingArea +}); + +// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/applyDecoratedDescriptor.js +const applyDecoratedDescriptor = REQ_(793); +// EXTERNAL MODULE: external "React" +const external_React_ = REQ_(1594); +const REaCt = REQ_.n(external_React_); +// EXTERNAL MODULE: ./js/ui/utils.jsx +const utils = REQ_(6411); +// EXTERNAL MODULE: ./js/chat/mixins.js +const mixins = REQ_(8264); +// EXTERNAL MODULE: ./js/ui/emojiDropdown.jsx +const emojiDropdown = REQ_(1165); +// EXTERNAL MODULE: ./js/ui/buttons.jsx +const ui_buttons = REQ_(5155); +;// ./js/chat/ui/emojiAutocomplete.jsx + + + +class EmojiAutocomplete extends mixins.w9 { + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.state = { + 'selected': 0 + }; + this.loading = false; + this.data_emojis = []; + } + preload_emojis() { + if (this.loading === false) { + this.loading = true; + megaChat.getEmojiDataSet('emojis').then(emojis => { + this.loading = 0; + this.data_emojis = emojis; + this.safeForceUpdate(); + }); + } + } + unbindKeyEvents() { + $(document).off(`keydown.emojiAutocomplete${ this.getUniqueId()}`); + } + bindKeyEvents() { + const self = this; + $(document).rebind(`keydown.emojiAutocomplete${ self.getUniqueId()}`, (e) => { + if (!self.props.emojiSearchQuery) { + self.unbindKeyEvents(); + return; + } + let key = e.keyCode || e.which; + if (!$(e.target).is("textarea")) { + console.error("this should never happen."); + return; + } + if (e.altKey || e.metaKey) { + return; + } + let selected = $.isNumeric(self.state.selected) ? self.state.selected : 0; + if (document.body.classList.contains('rtl') && (key === 37 || key === 39)) { + key = key === 37 ? 39 : 37; + } + let handled = false; + if (!e.shiftKey && (key === 37 || key === 38)) { + selected = selected - 1; + selected = selected < 0 ? self.maxFound - 1 : selected; + if (self.found[selected] && self.state.selected !== selected) { + self.setState({ + selected, + 'prefilled': true + }); + handled = true; + self.props.onPrefill(false, `:${ self.found[selected].n }:`); + } + } else if (!e.shiftKey && (key === 39 || key === 40 || key === 9)) { + selected = selected + (key === 9 ? e.shiftKey ? -1 : 1 : 1); + selected = selected < 0 ? Object.keys(self.found).length - 1 : selected; + selected = selected >= self.props.maxEmojis || selected >= Object.keys(self.found).length ? 0 : selected; + if (self.found[selected] && (key === 9 || self.state.selected !== selected)) { + self.setState({ + selected, + 'prefilled': true + }); + self.props.onPrefill(false, `:${ self.found[selected].n }:`); + handled = true; + } + } else if (key === 13) { + self.unbindKeyEvents(); + if (selected === -1) { + if (self.found.length > 0) { + for (let i = 0; i < self.found.length; i++) { + if (`:${ self.found[i].n }:` === `${self.props.emojiSearchQuery }:`) { + self.props.onSelect(false, `:${ self.found[0].n }:`); + handled = true; + } + } + } + if (!handled && key === 13) { + self.props.onCancel(); + } + return; + } else if (self.found.length > 0 && self.found[selected]) { + self.props.onSelect(false, `:${ self.found[selected].n }:`); + handled = true; + } else { + self.props.onCancel(); + } + } else if (key === 27) { + self.unbindKeyEvents(); + self.props.onCancel(); + handled = true; + } + if (handled) { + e.preventDefault(); + e.stopPropagation(); + return false; + } else { + if (self.isMounted()) { + self.setState({ + 'prefilled': false + }); + } + } + }); + } + componentDidUpdate() { + if (!this.props.emojiSearchQuery) { + this.unbindKeyEvents(); + } else { + this.bindKeyEvents(); + } + } + componentWillUnmount() { + super.componentWillUnmount(); + this.unbindKeyEvents(); + } + render() { + const self = this; + if (!self.props.emojiSearchQuery) { + return null; + } + self.preload_emojis(); + if (self.loading) { + return JSX_("div", { + className: "textarea-autofill-bl" + }, JSX_("div", { + className: "textarea-autofill-info" + }, l[5533])); + } + const q = self.props.emojiSearchQuery.substr(1, self.props.emojiSearchQuery.length); + let exactMatch = []; + let partialMatch = []; + const emojis = self.data_emojis || []; + for (var i = 0; i < emojis.length; i++) { + const emoji = emojis[i]; + const match = emoji.n.indexOf(q); + if (match !== -1) { + if (match === 0) { + exactMatch.push(emoji); + } else if (partialMatch.length < self.props.maxEmojis - exactMatch.length) { + partialMatch.push(emoji); + } + } + if (exactMatch.length >= self.props.maxEmojis) { + break; + } + } + exactMatch.sort((a, b) => { + if (a.n === q) { + return -1; + } else if (b.n === q) { + return 1; + } else { + return 0; + } + }); + const found = exactMatch.concat(partialMatch).slice(0, self.props.maxEmojis); + exactMatch = partialMatch = null; + this.maxFound = found.length; + this.found = found; + if (!found || found.length === 0) { + queueMicrotask(() => { + self.props.onCancel(); + }); + return null; + } + const emojisDomList = []; + for (var i = 0; i < found.length; i++) { + const meta = found[i]; + const filename = twemoji.convert.toCodePoint(meta.u); + emojisDomList.push(JSX_("div", { + className: `emoji-preview shadow ${ this.state.selected === i ? "active" : ""}`, + key: `${meta.n }_${ this.state.selected === i ? "selected" : "inselected"}`, + title: `:${ meta.n }:`, + onClick (e) { + self.props.onSelect(e, e.target.title); + self.unbindKeyEvents(); + } + }, JSX_("img", { + width: "20", + height: "20", + className: "emoji emoji-loading", + draggable: "false", + alt: meta.u, + onLoad: e => { + e.target.classList.remove('emoji-loading'); + }, + onError: e => { + e.target.classList.remove('emoji-loading'); + e.target.classList.add('emoji-loading-error'); + }, + src: `${staticpath }images/mega/twemojis/2_v2/72x72/${ filename }.png` + }), JSX_("div", { + className: "emoji title" + }, `:${ meta.n }:`))); + } + return JSX_("div", { + ref: this.domRef, + className: "textarea-autofill-bl" + }, JSX_(utils.P9, { + tag: "div", + className: "textarea-autofill-info" + }, l.emoji_suggestion_instruction), JSX_("div", { + className: "textarea-autofill-emoji" + }, emojisDomList)); + } +} +EmojiAutocomplete.defaultProps = { + 'requiresUpdateOnResize': true, + 'emojiSearchQuery': false, + 'disableCheckingVisibility': true, + 'maxEmojis': 12 +}; +// EXTERNAL MODULE: ./js/ui/perfectScrollbar.jsx +const perfectScrollbar = REQ_(1301); +// EXTERNAL MODULE: ./js/chat/ui/gifPanel/utils.jsx +const gifPanel_utils = REQ_(1635); +;// ./js/chat/ui/gifPanel/searchField.jsx +let _SearchField; + + +class SearchField extends REaCt().Component { + render() { + const { + value, + searching, + onChange, + onReset, + onBack + } = this.props; + return JSX_("div", { + className: "gif-panel-search" + }, JSX_("div", { + className: "gif-search-field" + }, searching ? JSX_("i", { + className: "sprite-fm-mono icon-left", + onClick: onBack + }) : JSX_("i", { + className: "sprite-fm-mono icon-preview-reveal" + }), JSX_("input", { + ref: SearchField.inputRef, + type: "text", + placeholder: gifPanel_utils.kg.SEARCH, + autoFocus: true, + value, + onChange + }), searching && JSX_("i", { + className: "sprite-fm-mono icon-close-component", + onClick: onReset + })), JSX_("div", { + className: "giphy-logo" + }, JSX_("img", { + src: `${staticpath }images/mega/giphy.gif`, + alt: "PWRD BY GIPHY" + }))); + } +} +_SearchField = SearchField; +SearchField.inputRef = REaCt().createRef(); +SearchField.focus = () => _SearchField.inputRef && _SearchField.inputRef.current && _SearchField.inputRef.current.focus(); +SearchField.hasValue = () => _SearchField.inputRef && _SearchField.inputRef.current && !!_SearchField.inputRef.current.value.length; +;// ./js/chat/ui/gifPanel/result.jsx + + +class Result extends REaCt().Component { + constructor(...args) { + super(...args); + this.resultRef = REaCt().createRef(); + } + componentDidMount() { + let _this$props$onMount, _this$props; + (_this$props$onMount = (_this$props = this.props).onMount) == null || _this$props$onMount.call(_this$props, this.resultRef.current); + } + componentWillUnmount() { + let _this$props$onUnmount, _this$props2; + (_this$props$onUnmount = (_this$props2 = this.props).onUnmount) == null || _this$props$onUnmount.call(_this$props2, this.resultRef.current, 'unobserve'); + } + render() { + const { + image, + title, + onClick + } = this.props; + return JSX_("div", { + className: ` + ${NODE_CONTAINER_CLASS} + ${onClick ? 'clickable' : ''} + `, + style: { + height: parseInt(image.height) + } + }, JSX_("div", { + ref: this.resultRef, + className: NODE_CLASS, + style: { + backgroundImage: HAS_INTERSECTION_OBSERVER ? '' : `url(${image.url})` + }, + "data-url": image.url, + onClick + }, JSX_("span", null, title))); + } +} +;// ./js/chat/ui/gifPanel/resultContainer.jsx + + + +const HAS_INTERSECTION_OBSERVER = typeof IntersectionObserver !== 'undefined'; +const NODE_CONTAINER_CLASS = 'node-container'; +const NODE_CLASS = 'node'; +const RESULT_CONTAINER_CLASS = 'gif-panel-results'; +const RESULTS_END_CLASS = 'results-end'; +const Nil = ({ + children +}) => JSX_("div", { + className: "no-results-container" +}, JSX_("div", { + className: "no-results-content" +}, JSX_("i", { + className: "huge-icon sad-smile" +}), JSX_("span", null, children))); +class ResultContainer extends REaCt().Component { + constructor(...args) { + super(...args); + this.intersectionObserver = null; + this.initializeIntersectionObserver = () => { + if (HAS_INTERSECTION_OBSERVER) { + this.intersectionObserver = new IntersectionObserver(entries => { + for (let i = 0; i < entries.length; i++) { + var _target$classList, _target$classList2; + const entry = entries[i]; + const {target} = entry; + if ((_target$classList = target.classList) != null && _target$classList.contains(NODE_CLASS)) { + target.style.backgroundImage = entry.isIntersecting ? `url(${target.dataset.url})` : null; + } + if (entry.isIntersecting && (_target$classList2 = target.classList) != null && _target$classList2.contains(RESULTS_END_CLASS)) { + this.props.onPaginate(); + } + } + }); + } + }; + this.toggleIntersectionObserver = (node, action = 'observe') => { + if (node && this.intersectionObserver) { + this.intersectionObserver[action](node); + } + }; + } + componentDidMount() { + this.initializeIntersectionObserver(); + } + componentWillUnmount() { + if (this.intersectionObserver) { + this.intersectionObserver.disconnect(); + this.intersectionObserver = null; + } + } + render() { + const { + loading, + results, + bottom, + unavailable, + onClick + } = this.props; + if (unavailable) { + return JSX_(Nil, null, gifPanel_utils.kg.NOT_AVAILABLE); + } + if (loading && results.length < 1) { + return JSX_("div", { + className: RESULT_CONTAINER_CLASS + }, Array.from({ + length: gifPanel_utils.nC.LIMIT + }, (element, index) => JSX_("div", { + key: index, + className: NODE_CONTAINER_CLASS + }, JSX_("div", { + className: NODE_CLASS, + style: { + height: Math.floor(Math.random() * 150) + 100 + } + })))); + } + if (!loading && results.length < 1) { + return JSX_(Nil, null, gifPanel_utils.kg.NO_RESULTS); + } + if (results.length) { + return JSX_(REaCt().Fragment, null, JSX_("div", { + className: RESULT_CONTAINER_CLASS + }, results.map(({ + slug, + images: { + fixed_width_downsampled + }, + title + }, index) => { + return JSX_(Result, { + key: `${slug}--${index}`, + image: fixed_width_downsampled, + title, + onClick: () => onClick(results[index]), + onMount: this.toggleIntersectionObserver, + onUnmount: this.toggleIntersectionObserver + }); + })), JSX_("div", { + className: RESULTS_END_CLASS, + ref: node => this.toggleIntersectionObserver(node), + style: { + visibility: bottom ? 'visible' : 'hidden' + } + }, JSX_("img", { + className: "emoji", + alt: "\\ud83d\\ude10", + src: `${staticpath}/images/mega/twemojis/2_v2/72x72/1f610.png` + }), JSX_("strong", null, gifPanel_utils.kg.END_OF_RESULTS))); + } + return null; + } +} +;// ./js/chat/ui/gifPanel/gifPanel.jsx + + + + + +class GifPanel extends REaCt().Component { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + this.pathRef = ''; + this.controllerRef = null; + this.fetchRef = null; + this.delayProcID = null; + this.defaultState = { + value: '', + searching: false, + results: [], + loading: true, + offset: 0, + bottom: false, + unavailable: false + }; + this.state = { + ...this.defaultState + }; + this.getContainerHeight = () => window.innerHeight * 0.6 > gifPanel_utils.L9 ? gifPanel_utils.L9 : window.innerHeight * 0.6; + this.getFormattedPath = path => { + const PATH = path + (path.indexOf('?') === -1 ? '?' : '&'); + const LIMIT = `limit=${gifPanel_utils.nC.LIMIT}`; + return `${gifPanel_utils.nC.HOSTNAME + gifPanel_utils.nC.ENDPOINT}/${PATH + LIMIT}`; + }; + this.clickedOutsideComponent = ev => { + const $target = ev && $(ev.target); + return $target.parents(`.${gifPanel_utils.Hc}`).length === 0 && ['.small-icon.tiny-reset', '.small-icon.gif'].every(outsideElement => !$target.is(outsideElement)); + }; + this.bindEvents = () => { + $(document).rebind('mousedown.gifPanel', ev => { + if (this.clickedOutsideComponent(ev)) { + this.props.onToggle(); + } + }).rebind('keydown.gifPanel', ({ + keyCode + }) => { + if (keyCode && keyCode === 27) { + return SearchField.hasValue() ? this.doReset() : this.props.onToggle(); + } + }); + }; + this.unbindEvents = () => { + if (this.delayProcID) { + delay.cancel(this.delayProcID); + } + $(document).unbind('.gifPanel'); + }; + this.doFetch = path => { + this.setState({ + loading: true, + unavailable: false + }, () => { + this.pathRef = path; + this.controllerRef = typeof AbortController === 'function' && new AbortController(); + this.fetchRef = fetch(this.getFormattedPath(path), { + signal: this.controllerRef.signal + }).then(response => response.json()).then(({ + data + }) => { + this.fetchRef = this.pathRef = null; + if (this.domRef.current) { + if (data && data.length) { + return this.setState(state => ({ + results: [...state.results, ...data], + loading: false + })); + } + return this.setState({ + bottom: true, + loading: false + }, () => this.resultContainerRef && this.resultContainerRef.reinitialise()); + } + }).catch(ex => { + return ex.name === 'AbortError' ? null : this.setState({ + unavailable: true + }); + }); + }); + }; + this.doPaginate = () => { + const { + value, + loading, + searching + } = this.state; + if (!loading) { + this.setState(state => ({ + offset: state.offset + gifPanel_utils.nC.OFFSET + }), () => { + this.doFetch(searching ? `search?q=${escape(value)}&offset=${this.state.offset}` : `trending?offset=${this.state.offset}`); + }); + } + }; + this.doReset = () => { + this.setState({ + ...this.defaultState + }, () => { + this.doFetch('trending'); + onIdle(() => SearchField.focus()); + this.resultContainerRef.scrollToY(0); + }); + }; + this.handleChange = ev => { + const { + value + } = ev.target; + const searching = value.length >= 2; + if (value.length === 0) { + return this.doReset(); + } + if (this.fetchRef !== null && this.pathRef === 'trending' && this.controllerRef) { + this.controllerRef.abort(); + this.fetchRef = this.pathRef = null; + } + this.setState(state => ({ + ...this.defaultState, + value, + searching, + results: searching ? [] : state.results + }), () => { + this.resultContainerRef.scrollToY(0); + this.delayProcID = searching ? delay('gif-search', () => this.doFetch(`search?q=${escape(value)}`), 1600) : null; + }); + }; + this.handleBack = () => this.doReset(); + this.doSend = result => { + const { + mp4, + webp, + mp4_size, + webp_size, + width, + height + } = result.images.fixed_height; + const message = Message.MANAGEMENT_MESSAGE_TYPES.MANAGEMENT + Message.MANAGEMENT_MESSAGE_TYPES.CONTAINS_META + Message.MESSAGE_META_TYPE.GIPHY + JSON.stringify({ + textMessage: result.title, + src: gifPanel_utils.nC.convert(mp4), + src_webp: gifPanel_utils.nC.convert(webp), + s: mp4_size, + s_webp: webp_size, + w: width, + h: height + }); + this.props.chatRoom.sendMessage(message); + this.props.onToggle(); + }; + } + componentDidMount() { + if (this.state.results && this.state.results.length === 0) { + this.doFetch('trending'); + } + this.bindEvents(); + } + componentWillUnmount() { + this.unbindEvents(); + } + render() { + const { + value, + searching, + results, + loading, + bottom, + unavailable + } = this.state; + return JSX_("div", { + ref: this.domRef, + className: "gif-panel-wrapper" + }, JSX_("div", { + className: "gif-panel", + style: { + height: this.getContainerHeight() + } + }, JSX_("div", { + className: "gif-panel-header" + }, JSX_(SearchField, { + value, + searching, + onChange: this.handleChange, + onReset: this.doReset, + onBack: this.handleBack + })), JSX_("div", { + className: "gif-panel-content" + }, JSX_(perfectScrollbar.O, { + ref: container => { + this.resultContainerRef = container; + }, + options: { + 'suppressScrollX': true + } + }, JSX_(ResultContainer, { + results, + loading, + bottom, + unavailable, + onPaginate: this.doPaginate, + onClick: this.doSend + }))))); + } +} +;// ./js/chat/ui/typingArea.jsx + +let _dec, _class; + + + + + + + + +const TypingArea = (_dec = (0,mixins.hG)(54, true), _class = class TypingArea extends mixins.w9 { + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.state = { + emojiSearchQuery: false, + textareaHeight: 20, + gifPanelActive: false + }; + this.getTextareaMaxHeight = () => { + const { + containerRef + } = this.props; + if (containerRef && containerRef.current) { + return this.isMounted() ? containerRef.current.offsetHeight * 0.4 : 100; + } + return 100; + }; + const { + chatRoom + } = props; + this.logger = d && MegaLogger.getLogger("TypingArea", {}, chatRoom && chatRoom.logger || megaChat.logger); + this.onEmojiClicked = this.onEmojiClicked.bind(this); + this.onTypeAreaKeyUp = this.onTypeAreaKeyUp.bind(this); + this.onTypeAreaKeyDown = this.onTypeAreaKeyDown.bind(this); + this.onTypeAreaBlur = this.onTypeAreaBlur.bind(this); + this.onTypeAreaChange = this.onTypeAreaChange.bind(this); + this.onCopyCapture = this.onCopyCapture.bind(this); + this.onPasteCapture = this.onPasteCapture.bind(this); + this.onCutCapture = this.onCutCapture.bind(this); + this.state.typedMessage = this.props.initialText || ''; + } + onEmojiClicked(e, slug) { + if (this.props.disabled) { + e.preventDefault(); + e.stopPropagation(); + return; + } + slug = slug[0] === ':' || slug.substr(-1) === ':' ? slug : `:${slug}:`; + const textarea = $('.messages-textarea', this.domRef.current)[0]; + const cursorPosition = this.getCursorPosition(textarea); + const { + text, + onValueChanged + } = this.props; + const val = text.slice(0, cursorPosition) + slug + text.slice(cursorPosition); + onValueChanged(val); + textarea.selectionEnd = cursorPosition + slug.length; + this.onTypeAreaChange(e, val); + } + stoppedTyping() { + if (this.props.disabled || !this.props.chatRoom) { + return; + } + this.iAmTyping = false; + this.props.chatRoom.trigger('stoppedTyping'); + } + typing() { + if (this.props.disabled || !this.props.chatRoom) { + return; + } + const self = this; + const now = Date.now(); + delay(this.getReactId(), () => self.iAmTyping && self.stoppedTyping(), 4e3); + if (!self.iAmTyping || now - self.lastTypingStamp > 4e3) { + self.iAmTyping = true; + self.lastTypingStamp = now; + self.props.chatRoom.trigger('typing'); + } + } + triggerOnUpdate(forced) { + const self = this; + if (!self.props.onUpdate || !self.isMounted()) { + return; + } + let shouldTriggerUpdate = forced ? forced : false; + if (!shouldTriggerUpdate && self.props.text !== self.lastTypedMessage) { + self.lastTypedMessage = self.props.text; + shouldTriggerUpdate = true; + } + if (!shouldTriggerUpdate) { + const $textarea = $('.chat-textarea:visible textarea:visible', self.domRef.current); + if (!self._lastTextareaHeight || self._lastTextareaHeight !== $textarea.height()) { + self._lastTextareaHeight = $textarea.height(); + shouldTriggerUpdate = true; + if (self.props.onResized) { + self.props.onResized(); + } + } + } + if (shouldTriggerUpdate) { + self.props.onUpdate(); + } + } + onCancelClicked() { + const self = this; + self.props.onValueChanged(''); + if (self.props.chatRoom && self.iAmTyping) { + self.stoppedTyping(); + } + self.onConfirmTrigger(false); + self.triggerOnUpdate(); + } + onSaveClicked() { + const self = this; + if (self.props.disabled || !self.isMounted()) { + return; + } + const val = $.trim($('.chat-textarea:visible textarea:visible', this.domRef.current).val()); + if (self.onConfirmTrigger(val) !== true) { + self.props.onValueChanged(''); + } + if (self.props.chatRoom && self.iAmTyping) { + self.stoppedTyping(); + } + self.triggerOnUpdate(); + } + onConfirmTrigger(val) { + const { + onConfirm, + persist, + chatRoom + } = this.props; + const result = onConfirm(val); + if (val !== false && result !== false) { + $('.textarea-scroll', this.domRef.current).scrollTop(0); + } + if (persist) { + const { + persistedTypeArea + } = chatRoom.megaChat.plugins; + if (persistedTypeArea) { + if (d > 2) { + this.logger.info('Removing persisted-typed value...'); + } + persistedTypeArea.removePersistedTypedValue(chatRoom); + } + } + return result; + } + onTypeAreaKeyDown(e) { + if (this.props.disabled) { + e.preventDefault(); + e.stopPropagation(); + return; + } + const self = this; + const key = e.keyCode || e.which; + const element = e.target; + const val = $.trim(element.value); + if (self.state.emojiSearchQuery) { + return; + } + if (key === 13 && !e.shiftKey && !e.ctrlKey && !e.altKey) { + if (e.isPropagationStopped() || e.isDefaultPrevented()) { + return; + } + if (self.onConfirmTrigger(val) !== true) { + self.props.onValueChanged(''); + $(document).trigger('closeDropdowns'); + } + e.preventDefault(); + e.stopPropagation(); + if (self.props.chatRoom && self.iAmTyping) { + self.stoppedTyping(); + } + } + } + onTypeAreaKeyUp(e) { + if (this.props.disabled) { + e.preventDefault(); + e.stopPropagation(); + return; + } + const self = this; + const key = e.keyCode || e.which; + const element = e.target; + const val = $.trim(element.value); + if (key === 13 && !e.shiftKey && !e.ctrlKey && !e.altKey) { + e.preventDefault(); + e.stopPropagation(); + } else if (key === 13) { + if (self.state.emojiSearchQuery) { + return; + } + if (e.altKey) { + let content = element.value; + const cursorPos = self.getCursorPosition(element); + content = `${content.substring(0, cursorPos) }\n${ content.substring(cursorPos, content.length)}`; + self.props.onValueChanged(content); + self.onUpdateCursorPosition = cursorPos + 1; + e.preventDefault(); + } else if ($.trim(val).length === 0) { + e.preventDefault(); + } + } else if (key === 38) { + if (self.state.emojiSearchQuery) { + return; + } + if ($.trim(val).length === 0) { + if (self.props.onUpEditPressed && self.props.onUpEditPressed() === true) { + e.preventDefault(); + } + } + } else if (key === 27) { + if (self.state.emojiSearchQuery) { + return; + } + if (self.props.showButtons === true) { + e.preventDefault(); + self.onCancelClicked(e); + } + } else { + if (self.prefillMode && (key === 8 || key === 32 || key === 186 || key === 13)) { + self.prefillMode = false; + } + const currentContent = element.value; + const currentCursorPos = self.getCursorPosition(element) - 1; + if (self.prefillMode && (currentCursorPos > self.state.emojiEndPos || currentCursorPos < self.state.emojiStartPos)) { + self.prefillMode = false; + self.setState({ + 'emojiSearchQuery': false, + 'emojiStartPos': false, + 'emojiEndPos': false + }); + return; + } + if (self.prefillMode) { + return; + } + const char = String.fromCharCode(key); + if (key === 16 || key === 17 || key === 18 || key === 91 || key === 8 || key === 37 || key === 39 || key === 40 || key === 38 || key === 9 || /[\w:-]/.test(char)) { + const parsedResult = mega.utils.emojiCodeParser(currentContent, currentCursorPos); + self.setState({ + 'emojiSearchQuery': parsedResult[0], + 'emojiStartPos': parsedResult[1], + 'emojiEndPos': parsedResult[2] + }); + return; + } + if (self.state.emojiSearchQuery) { + self.setState({ + 'emojiSearchQuery': false + }); + } + } + } + onTypeAreaBlur(e) { + if (this.props.disabled) { + e.preventDefault(); + e.stopPropagation(); + return; + } + const self = this; + if (self.state.emojiSearchQuery) { + setTimeout(() => { + if (self.isMounted()) { + self.setState({ + 'emojiSearchQuery': false, + 'emojiStartPos': false, + 'emojiEndPos': false + }); + } + }, 300); + } + } + onTypeAreaChange(e, value) { + if (this.props.disabled) { + e.preventDefault(); + e.stopPropagation(); + return; + } + const self = this; + value = String(value || e.target.value || '').replace(/^\s+/, ''); + if (self.props.text !== value) { + self.props.onValueChanged(value); + self.forceUpdate(); + } + if (value.length) { + self.typing(); + } else { + self.stoppedTyping(); + } + if (this.props.persist) { + const { + chatRoom + } = this.props; + const { + megaChat + } = chatRoom; + const { + persistedTypeArea + } = megaChat.plugins; + if (persistedTypeArea) { + if (d > 2) { + this.logger.debug('%s persisted-typed value...', value.length ? 'Updating' : 'Removing'); + } + if (value.length) { + persistedTypeArea.updatePersistedTypedValue(chatRoom, value); + } else { + persistedTypeArea.removePersistedTypedValue(chatRoom); + } + } + } + self.updateScroll(); + } + focusTypeArea() { + if (this.props.disabled) { + return; + } + if ($('.chat-textarea:visible textarea:visible', this.domRef.current).length > 0 && !$('.chat-textarea:visible textarea:visible:first', this.domRef.current).is(":focus")) { + moveCursortoToEnd($('.chat-textarea:visible:first textarea', this.domRef.current)[0]); + } + } + componentDidMount() { + super.componentDidMount(); + this._lastTextareaHeight = 20; + this.lastTypedMessage = this.props.initialText || this.lastTypedMessage; + chatGlobalEventManager.addEventListener('resize', `typingArea${this.getUniqueId()}`, () => this.handleWindowResize()); + this.triggerOnUpdate(true); + this.updateScroll(); + megaChat.rebind(`viewstateChange.gifpanel${this.getUniqueId()}`, e => { + const { + gifPanelActive + } = this.state; + const { + state + } = e.data; + if (state === 'active' && !gifPanelActive && this.gifResume) { + this.setState({ + gifPanelActive: true + }); + delete this.gifResume; + } else if (state !== 'active' && gifPanelActive && !this.gifResume) { + this.gifResume = true; + this.setState({ + gifPanelActive: false + }); + } + }); + } + UNSAFE_componentWillMount() { + const { + chatRoom, + initialText, + persist, + onValueChanged + } = this.props; + const { + megaChat, + roomId + } = chatRoom; + const { + persistedTypeArea + } = megaChat.plugins; + if (persist && persistedTypeArea) { + if (!initialText) { + persistedTypeArea.getPersistedTypedValue(chatRoom).then(res => { + if (res && this.isMounted() && !this.props.text) { + onValueChanged(res); + } + }).catch(ex => { + if (this.logger && ex !== undefined) { + this.logger.warn(`Failed to retrieve persistedTypeArea for ${roomId}: ${ex}`, [ex]); + } + }); + } + persistedTypeArea.addChangeListener(this.getUniqueId(), (e, k, v) => { + if (roomId === k) { + onValueChanged(v || ''); + this.triggerOnUpdate(true); + } + }); + } + } + componentWillUnmount() { + super.componentWillUnmount(); + const self = this; + self.triggerOnUpdate(); + if (megaChat.plugins.persistedTypeArea) { + megaChat.plugins.persistedTypeArea.removeChangeListener(self.getUniqueId()); + } + chatGlobalEventManager.removeEventListener('resize', `typingArea${ self.getUniqueId()}`); + megaChat.off(`viewstateChange.gifpanel${this.getUniqueId()}`); + } + componentDidUpdate() { + if (this.isComponentEventuallyVisible() && !window.getSelection().toString() && $('textarea:focus,select:focus,input:focus').filter(":visible").length === 0) { + this.focusTypeArea(); + } + this.updateScroll(); + if (this.onUpdateCursorPosition) { + const el = $('.chat-textarea:visible:first textarea:visible', this.domRef.current)[0]; + el.selectionStart = el.selectionEnd = this.onUpdateCursorPosition; + this.onUpdateCursorPosition = false; + } + } + updateScroll() { + if (!this.isComponentEventuallyVisible() || !this.$node && !this.domRef && !this.domRef.current) { + return; + } + const $node = this.$node = this.$node || this.domRef.current; + const $textarea = this.$textarea = this.$textarea || $('textarea:first', $node); + const $scrollBlock = this.$scrollBlock = this.$scrollBlock || $textarea.closest('.textarea-scroll'); + const $preview = $('.message-preview', $scrollBlock).safeHTML(`${escapeHTML(this.props.text).replace(/\n/g, '
')}
`); + const textareaHeight = $preview.height(); + $scrollBlock.height(Math.min(textareaHeight, this.getTextareaMaxHeight())); + if (textareaHeight !== this._lastTextareaHeight) { + this._lastTextareaHeight = textareaHeight; + this.setState({ + textareaHeight + }); + if (this.props.onResized) { + this.props.onResized(); + } + $textarea.height(textareaHeight); + } + if (this.textareaScroll) { + this.textareaScroll.reinitialise(); + } + } + getCursorPosition(el) { + let pos = 0; + if ('selectionStart' in el) { + pos = el.selectionStart; + } else if ('selection' in document) { + el.focus(); + const sel = document.selection.createRange(), + selLength = document.selection.createRange().text.length; + sel.moveStart('character', -el.value.length); + pos = sel.text.length - selLength; + } + return pos; + } + customIsEventuallyVisible() { + return this.props.chatRoom.isCurrentlyActive; + } + handleWindowResize(e) { + if (!this.isComponentEventuallyVisible()) { + return; + } + if (e) { + this.updateScroll(); + } + this.triggerOnUpdate(); + } + isActive() { + return document.hasFocus() && this.$messages && this.$messages.is(":visible"); + } + resetPrefillMode() { + this.prefillMode = false; + } + onCopyCapture() { + this.resetPrefillMode(); + } + onCutCapture() { + this.resetPrefillMode(); + } + onPasteCapture() { + this.resetPrefillMode(); + } + render() { + const self = this; + const room = this.props.chatRoom; + let buttons = null; + if (self.props.showButtons === true) { + buttons = [JSX_(ui_buttons.$, { + key: "save", + className: `${"mega-button right"} positive`, + label: l[776], + onClick: self.onSaveClicked.bind(self) + }), JSX_(ui_buttons.$, { + key: "cancel", + className: "mega-button right", + label: l.msg_dlg_cancel, + onClick: self.onCancelClicked.bind(self) + })]; + } + const textareaStyles = { + height: self.state.textareaHeight + }; + const textareaScrollBlockStyles = {}; + const newHeight = Math.min(self.state.textareaHeight, self.getTextareaMaxHeight()); + if (newHeight > 0) { + textareaScrollBlockStyles.height = newHeight; + } + let emojiAutocomplete = null; + if (self.state.emojiSearchQuery) { + emojiAutocomplete = JSX_(EmojiAutocomplete, { + emojiSearchQuery: self.state.emojiSearchQuery, + emojiStartPos: self.state.emojiStartPos, + emojiEndPos: self.state.emojiEndPos, + typedMessage: self.props.text, + onPrefill (e, emojiAlias) { + if ($.isNumeric(self.state.emojiStartPos) && $.isNumeric(self.state.emojiEndPos)) { + const msg = self.props.text; + const pre = msg.substr(0, self.state.emojiStartPos); + let post = msg.substr(self.state.emojiEndPos + 1, msg.length); + const startPos = self.state.emojiStartPos; + const fwdPos = startPos + emojiAlias.length; + let endPos = fwdPos; + self.onUpdateCursorPosition = fwdPos; + self.prefillMode = true; + if (post.substr(0, 2) == "::" && emojiAlias.substr(-1) == ":") { + emojiAlias = emojiAlias.substr(0, emojiAlias.length - 1); + endPos -= 1; + } else { + post = post ? post.substr(0, 1) !== " " ? ` ${ post}` : post : " "; + self.onUpdateCursorPosition++; + } + self.setState({ + 'emojiEndPos': endPos + }); + self.props.onValueChanged(pre + emojiAlias + post); + } + }, + onSelect (e, emojiAlias, forceSend) { + if ($.isNumeric(self.state.emojiStartPos) && $.isNumeric(self.state.emojiEndPos)) { + const msg = self.props.text; + const pre = msg.substr(0, self.state.emojiStartPos); + let post = msg.substr(self.state.emojiEndPos + 1, msg.length); + if (post.substr(0, 2) == "::" && emojiAlias.substr(-1) == ":") { + emojiAlias = emojiAlias.substr(0, emojiAlias.length - 1); + } else { + post = post ? post.substr(0, 1) !== " " ? ` ${ post}` : post : " "; + } + const val = pre + emojiAlias + post; + self.prefillMode = false; + self.setState({ + 'emojiSearchQuery': false, + 'emojiStartPos': false, + 'emojiEndPos': false + }); + self.props.onValueChanged(val); + if (forceSend) { + if (self.onConfirmTrigger($.trim(val)) !== true) { + self.props.onValueChanged(''); + } + } + } + }, + onCancel () { + self.prefillMode = false; + self.setState({ + 'emojiSearchQuery': false, + 'emojiStartPos': false, + 'emojiEndPos': false + }); + } + }); + } + const disabledTextarea = !!(room.pubCu25519KeyIsMissing === true || this.props.disabled); + return JSX_("div", { + ref: this.domRef, + className: ` + typingarea-component + ${this.props.className} + ` + }, this.state.gifPanelActive && JSX_(GifPanel, { + chatRoom: this.props.chatRoom, + onToggle: () => { + this.setState({ + gifPanelActive: false + }); + delete this.gifResume; + } + }), JSX_("div", { + className: ` + chat-textarea + ${this.props.className} + ` + }, emojiAutocomplete, self.props.children, self.props.editing ? null : JSX_(ui_buttons.$, { + className: ` + popup-button + gif-button + ${this.state.gifPanelActive ? 'active' : ''} + `, + icon: "small-icon gif", + disabled: this.props.disabled, + onClick: () => this.setState(state => { + delete this.gifResume; + return { + gifPanelActive: !state.gifPanelActive + }; + }) + }), JSX_(ui_buttons.$, { + className: "popup-button emoji-button", + icon: "sprite-fm-theme icon-emoji", + iconHovered: "sprite-fm-theme icon-emoji-active", + disabled: this.props.disabled + }, JSX_(emojiDropdown.A, { + className: "popup emoji", + vertOffset: 17, + onClick: this.onEmojiClicked + })), JSX_("hr", null), JSX_(perfectScrollbar.O, { + chatRoom: self.props.chatRoom, + className: "chat-textarea-scroll textarea-scroll", + options: { + 'suppressScrollX': true + }, + style: textareaScrollBlockStyles, + ref: ref => { + self.textareaScroll = ref; + } + }, JSX_("div", { + className: "messages-textarea-placeholder" + }, self.props.text ? null : JSX_(utils.zT, null, (l[18763] || `Write message to \u201c%s\u201d\u2026`).replace('%s', room.getRoomTitle()))), JSX_("textarea", { + className: ` + ${"messages-textarea"} + ${disabledTextarea ? 'disabled' : ''} + `, + onKeyUp: this.onTypeAreaKeyUp, + onKeyDown: this.onTypeAreaKeyDown, + onBlur: this.onTypeAreaBlur, + onChange: this.onTypeAreaChange, + onCopyCapture: this.onCopyCapture, + onPasteCapture: this.onPasteCapture, + onCutCapture: this.onCutCapture, + value: self.props.text, + style: textareaStyles, + disabled: disabledTextarea, + readOnly: disabledTextarea + }), JSX_("div", { + className: "message-preview" + }))), buttons); + } +}, (0,applyDecoratedDescriptor.A)(_class.prototype, "handleWindowResize", [_dec], Object.getOwnPropertyDescriptor(_class.prototype, "handleWindowResize"), _class.prototype), _class); + + }, + + 5009 +(_, EXP_, REQ_) { + + REQ_.d(EXP_, { + A: () => __WEBPACK_DEFAULT_EXPORT__ + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _chat_mixins1__ = REQ_(8264); + + +class ToggleCheckbox extends _chat_mixins1__ .w9 { + constructor(props) { + super(props); + this.domRef = react0___default().createRef(); + this.onToggle = () => { + const newState = !this.state.value; + this.setState({ + value: newState + }); + if (this.props.onToggle) { + this.props.onToggle(newState); + } + }; + this.state = { + value: this.props.value + }; + } + render() { + return JSX_("div", { + ref: this.domRef, + className: ` + mega-switch + ${this.props.className} + ${this.state.value ? 'toggle-on' : ''} + `, + role: "switch", + "aria-checked": !!this.state.value, + onClick: this.onToggle + }, JSX_("div", { + className: `mega-feature-switch sprite-fm-mono-after + ${this.state.value ? 'icon-check-after' : 'icon-minimise-after'}` + })); + } +} + const __WEBPACK_DEFAULT_EXPORT__ = { + ToggleCheckbox +}; + + } + +}]); \ No newline at end of file diff --git a/js/chat/bundle.js b/js/chat/bundle.js index f4a53bcdfd..4febb66f0c 100644 --- a/js/chat/bundle.js +++ b/js/chat/bundle.js @@ -1,26872 +1,5208 @@ /** @file automatically generated, do not edit it. */ -(() => { // webpackBootstrap - const __webpack_modules__ = { + (() => { // webpackBootstrap + window.JSX_=React.createElement; + const __webpack_modules__ = { -326 + 8676 (_, EXP_, REQ_) { "use strict"; + REQ_.d(EXP_, { + r: () => chatGlobalEventManager + }); +const ChatGlobalEventManager = function () {}; +lazy(ChatGlobalEventManager.prototype, 'listeners', function () { + window.addEventListener('hashchange', ev => this.triggered(ev)); + $(window).rebind('resize.chatGlobalEventManager', ev => this.triggered(ev)); + const listeners = Object.create(null); + listeners.resize = Object.create(null); + listeners.hashchange = Object.create(null); + return listeners; +}); +ChatGlobalEventManager.prototype.addEventListener = function (eventName, namespace, cb) { + this.listeners[eventName][namespace] = this.listeners[namespace] || cb; +}; +ChatGlobalEventManager.prototype.removeEventListener = function (eventName, namespace) { + delete this.listeners[eventName][namespace]; +}; +ChatGlobalEventManager.prototype.triggered = SoonFc(140, function _chatEVDispatcher(ev) { + if (M.chat) { + const listeners = this.listeners[ev.type]; + for (const k in listeners) { + listeners[k](ev); + } + } +}); +const chatGlobalEventManager = new ChatGlobalEventManager(); -// UNUSED EXPORTS: default + }, -// EXTERNAL MODULE: external "React" -const React_ = REQ_(594); -const REaCt = REQ_.n(React_); -// EXTERNAL MODULE: external "ReactDOM" -const ReactDOM_ = REQ_(206); -// EXTERNAL MODULE: ./js/chat/ui/conversations.jsx + 21 modules -const conversations = REQ_(732); -;// ./js/chat/chatRouting.jsx -let _ChatRouting; -class ChatRouting { - constructor(megaChatInstance) { - this.megaChat = megaChatInstance; + 7057 +(_, EXP_, REQ_) { + +"use strict"; + REQ_.d(EXP_, { + U_: () => MCO_FLAGS, + zd: () => RETENTION_FORMAT + }); + const _utils_jsx0__ = REQ_(5779); + +const RETENTION_FORMAT = { + HOURS: 'hour', + DAYS: 'day', + WEEKS: 'week', + MONTHS: 'month', + DISABLED: 'none' +}; +const MCO_FLAGS = { + OPEN_INVITE: 'oi', + SPEAK_REQUEST: 'sr', + WAITING_ROOM: 'w' +}; +window.RETENTION_FORMAT = RETENTION_FORMAT; +window.MCO_FLAGS = MCO_FLAGS; +const ChatRoom = function (megaChat, roomId, type, users, ctime, lastActivity, chatId, chatShard, chatdUrl, noUI, publicChatHandle, publicChatKey, ck, isMeeting, retentionTime, mcoFlags, organiser) { + const self = this; + this.logger = MegaLogger.getLogger(`room[${ roomId }]`, {}, megaChat.logger); + this.megaChat = megaChat; + MegaDataObject.call(this, { + state: null, + users: [], + roomId: null, + type: null, + messages: [], + ctime: 0, + lastActivity: 0, + callRequest: null, + isCurrentlyActive: false, + _messagesQueue: [], + unreadCount: 0, + chatId: undefined, + chatdUrl: undefined, + chatShard: undefined, + members: {}, + membersSet: false, + membersLoaded: false, + topic: '', + flags: 0x00, + publicLink: null, + observers: 0, + dnd: null, + alwaysNotify: null, + retentionTime: 0, + activeCallIds: null, + meetingsLoading: null, + options: {}, + scheduledMeeting: undefined, + historyTimedOut: false + }); + this.roomId = roomId; + this.instanceIndex = ChatRoom.INSTANCE_INDEX++; + this.type = type; + this.ctime = ctime; + this.lastActivity = lastActivity ? lastActivity : 0; + this.chatd = megaChat.plugins.chatdIntegration.chatd; + this.chatId = chatId; + this.chatIdBin = chatId ? base64urldecode(chatId) : ""; + this.chatShard = chatShard; + this.chatdUrl = chatdUrl; + this.publicLink = null; + this.publicChatHandle = publicChatHandle; + this.publicChatKey = publicChatKey; + this.ck = ck; + this.scrolledToBottom = 1; + this.callRequest = null; + this.shownMessages = {}; + this.retentionTime = retentionTime; + this.activeSearches = 0; + this.activeCallIds = new MegaDataMap(this); + this.ringingCalls = new MegaDataMap(this); + this.isMeeting = isMeeting; + this.isNote = type === 'private' && roomId === u_handle; + this.callUserLimited = false; + this.members = Object.create(null); + Object.defineProperty(this.members, 'hasOwnProperty', { + value(p) { + return p in this; + } + }); + if (type === "private") { + users.forEach((userHandle) => { + self.members[userHandle] = 3; + }); + } else { + users.forEach((userHandle) => { + self.members[userHandle] = 0; + }); } - openCustomView(sectionName) { - const {megaChat} = this; - megaChat.routingSection = sectionName; - megaChat.hideAllChats(); - delete megaChat.lastOpenedChat; + this.options = {}; + mcoFlags = mcoFlags || {}; + for (const flag of Object.values(MCO_FLAGS)) { + this.options[flag] = mcoFlags[flag] || 0; } - route(resolve, reject, location, event, isLandingPage) { - if (!M.chat) { - console.error('This function is meant to navigate within the chat...'); - return; + this.organiser = organiser; + this.setState(ChatRoom.STATE.INITIALIZED); + this.isCurrentlyActive = false; + if (d) { + this.rebind('onStateChange.chatRoomDebug', (e, oldState, newState) => { + self.logger.debug("Will change state from: ", ChatRoom.stateToText(oldState), " to ", ChatRoom.stateToText(newState)); + }); + } + self.rebind('onStateChange.chatRoom', (e, oldState, newState) => { + if (newState === ChatRoom.STATE.READY && !self.isReadOnly() && self.chatd && self.isOnline() && self.chatIdBin) { + if (d > 2) { + self.logger.warn('Restoring persisted messages...', self.type, self.isCurrentlyActive); + } + const cim = self.getChatIdMessages(); + cim.restore(true); } - const args = String(location || '').split('/').map(String.trim).filter(String); - if (args[0] === 'fm') { - args.shift(); + }); + self.rebind('onMessagesBuffAppend.lastActivity', (e, msg) => { + if (is_chatlink || self.isNote) { + return; } - if (args[0] === 'chat') { - args.shift(); + const ts = msg.delay ? msg.delay : msg.ts; + if (!ts) { + return; } - if (args[0] && args[0].length > 8 && args[0].substring(0, 8) === 'contacts') { - location = location.replace(args[0], 'contacts'); - args[0] = 'contacts'; + const contactForMessage = msg && Message.getContactForMessage(msg); + if (contactForMessage && contactForMessage.u !== u_handle) { + if (!contactForMessage.ats || contactForMessage.ats < ts) { + contactForMessage.ats = ts; + } } - const [section] = args; - const { - megaChat - } = this; - if (d) { - megaChat.logger.warn('navigate(%s)', location, args); + if (self.lastActivity && self.lastActivity >= ts) { + if (msg.deleted) { + const { + delay, + ts + } = self.messagesBuff.getLastMessageFromServer(); + self.lastActivity = delay || ts; + } + return; } - args.route = { - location, - section, - args - }; - if (isLandingPage) { - megaChat.eventuallyInitMeetingUI(); + self.lastActivity = ts; + if (msg.userId === u_handle) { + self.didInteraction(u_handle, ts); + return; } - megaChat.routingSection = 'chat'; - megaChat.routingSubSection = null; - megaChat.routingParams = null; - const handler = ChatRouting.gPageHandlers[section || 'start']; - if (handler) { - handler.call(this, args.route).then(resolve).catch(reject); - resolve = null; - } else { - let roomId = String(args[(section === 'c' || section === 'g' || section === 'p') | 0] || ''); - if (roomId.includes('#')) { - let key = roomId.split('#'); - roomId = key[0]; - key = key[1]; - megaChat.publicChatKeys[roomId] = key; - roomId = megaChat.handleToId[roomId] || roomId; - } - const room = megaChat.getChatById(roomId); - if (room) { - room.show(); - args.route.location = room.getRoomUrl(); - } else if (!roomId || roomId === u_handle || roomId.length !== 11 && !is_chatlink) { - ChatRouting.gPageHandlers.redirect(args.route, 'fm/chat').then(resolve).catch(reject); - resolve = null; - } else if (section === 'p') { - megaChat.smartOpenChat([u_handle, roomId], 'private', undefined, undefined, undefined, true).then(resolve).catch(reject); - resolve = null; + if (self.type === "private") { + const targetUserId = self.getParticipantsExceptMe()[0]; + let targetUserNode; + if (M.u[targetUserId]) { + targetUserNode = M.u[targetUserId]; + } else if (msg.userId) { + targetUserNode = M.u[msg.userId]; } else { - megaChat.plugins.chatdIntegration.openChat(roomId).then(chatId => { - megaChat.getChatById(chatId).show(); - return chatId; - }).catch(ex => { - if (d && ex !== ENOENT) { - console.warn('If "%s" is a chat, something went wrong..', roomId, ex); - } - if (page !== location) { - return EEXPIRED; - } - megaChat.cleanup(true); - if (ex === ENOENT || ex === EBLOCKED && megaChat.publicChatKeys[roomId]) { - msgDialog('warninga', '', l[20641], l[20642], () => { - loadSubPage(is_chatlink ? 'start' : 'fm/chat', event); - }); - } else { - if (String(location).startsWith('chat')) { - location = 'fm/chat'; - } - loadSubPage(location, location.includes('chat') ? 'override' : event); - } - return EACCESS; - }).then(resolve).catch(reject); - resolve = null; + console.error("Missing participant in a 1on1 room."); + return; } - } - if (resolve) { - onIdle(resolve); - } - if (args.route.location !== location) { - location = args.route.location; - } - const method = page === 'chat' || page === 'fm/chat' || page === location || event && event.type === 'popstate' ? 'replaceState' : 'pushState'; - mBroadcaster.sendMessage('beforepagechange', location); - M.currentdirid = String(page = location).replace('fm/', ''); - if (location.substr(0, 13) === "chat/contacts") { - location = `fm/${ location}`; - } - if (location === 'chat') { - location = 'fm/chat'; - } - history[method]({ - subpage: location - }, "", (hashLogic ? '#' : '/') + location); - mBroadcaster.sendMessage('pagechange', page); - } - initFmAndChat(targetChatId) { - assert(!fminitialized); - return new Promise((res, rej) => { - M.currentdirid = targetChatId ? `fm/chat/${ targetChatId}` : undefined; - loadSubPage('fm'); - mBroadcaster.once('chat_initialized', () => { - authring.onAuthringReady().then(res, rej); - }); - }); - } - reinitAndOpenExistingChat(chatId, publicChatHandle = false, cbBeforeOpen = undefined) { - const chatUrl = `fm/chat/c/${ chatId}`; - publicChatHandle = publicChatHandle || megaChat.initialPubChatHandle; - megaChat.destroy(); - is_chatlink = false; - loadingDialog.pshow(); - return new Promise((resolve, reject) => { - this.initFmAndChat(chatId).always(() => { - megaChat.initialPubChatHandle = publicChatHandle; - megaChat.initialChatId = chatId; - const next = () => { - mBroadcaster.once('pagechange', () => { - onIdle(() => { - loadingDialog.phide(); - megaChat.renderListing(chatUrl, true).catch(ex => { - console.error("Failed to megaChat.renderListing:", ex); - reject(ex); - }).always(() => { - megaChat.updateKeysInProtocolHandlers(); - const chatRoom = megaChat.getChatById(chatId); - assert(chatRoom); - if (chatRoom.state === ChatRoom.STATE.READY) { - resolve(chatRoom); - } else { - chatRoom.rebind('onMessagesHistoryDone.reinitAndOpenExistingChat', () => { - if (chatRoom.state === ChatRoom.STATE.READY) { - resolve(chatRoom); - chatRoom.unbind('onMessagesHistoryDone.reinitAndOpenExistingChat'); - } - }); - } - }); - }); - }); - loadSubPage(chatUrl); + assert(targetUserNode && targetUserNode.u, 'No hash found for participant'); + assert(M.u[targetUserNode.u], 'User not found in M.u'); + if (targetUserNode) { + self.didInteraction(targetUserNode.u, self.lastActivity); + } + } else if (self.type === "group" || self.type === "public") { + let contactHash; + if (msg.authorContact) { + contactHash = msg.authorContact.u; + } else if (msg.userId) { + contactHash = msg.userId; + } + if (contactHash && M.u[contactHash]) { + self.didInteraction(contactHash, self.lastActivity); + } + assert(contactHash, 'Invalid hash for user (extracted from inc. message)'); + } else { + throw new Error("Not implemented"); + } + }); + self.rebind('onMembersUpdated.coreRoomDataMngmt', (e, eventData) => { + if (self.state === ChatRoom.STATE.LEFT && eventData.priv >= 0 && eventData.priv < 255) { + self.membersLoaded = false; + self.setState(ChatRoom.STATE.JOINING, true); + } + let queuedMembersUpdatedEvent = false; + if (self.membersLoaded === false) { + if (eventData.priv >= 0 && eventData.priv < 255) { + const addParticipant = function addParticipant() { + self.protocolHandler.addParticipant(eventData.userId); + self.members[eventData.userId] = eventData.priv; + ChatdIntegration._ensureContactExists([eventData.userId]); + self.trigger('onMembersUpdatedUI', eventData); }; - if (cbBeforeOpen) { - cbBeforeOpen().then(next, ex => { - console.error("Failed to execute `cbBeforeOpen`, got a reject of the returned promise:", ex); + if (is_chatlink) { + megaChat.initContacts([eventData.userId]); + } + ChatdIntegration._waitForProtocolHandler(self, addParticipant); + queuedMembersUpdatedEvent = true; + } + } else if (eventData.priv === 255 || eventData.priv === -1) { + const deleteParticipant = function deleteParticipant() { + if (eventData.userId === u_handle) { + Object.keys(self.members).forEach((userId) => { + self.protocolHandler.removeParticipant(userId); + self.members[userId] = userId === u_handle ? ChatRoom.MembersSet.PRIVILEGE_STATE.LEFT : ChatRoom.MembersSet.PRIVILEGE_STATE.READONLY; }); } else { - next(); + self.protocolHandler.removeParticipant(eventData.userId); + delete self.members[eventData.userId]; } - }).catch(ex => reject(ex)); + self.trigger('onMembersUpdatedUI', eventData); + }; + ChatdIntegration._waitForProtocolHandler(self, deleteParticipant); + queuedMembersUpdatedEvent = true; + } + if (eventData.userId === u_handle) { + self.membersLoaded = true; + } + if (!queuedMembersUpdatedEvent) { + self.members[eventData.userId] = eventData.priv; + self.trigger('onMembersUpdatedUI', eventData); + } + }); + if (is_chatlink && !is_chatlink.callId && !this.options.w) { + const unbind = () => { + self.unbind('onMessagesHistoryDone.chatlinkAlreadyIn'); + self.unbind('onMembersUpdated.chatlinkAlreadyIn'); + }; + self.rebind('onMembersUpdated.chatlinkAlreadyIn', (e, eventData) => { + if (eventData.userId === u_handle && eventData.priv >= 0) { + unbind(); + return this.megaChat.routing.reinitAndOpenExistingChat(this.chatId, this.publicChatHandle); + } }); - } - reinitAndJoinPublicChat(chatId, initialPubChatHandle, publicChatKey) { - initialPubChatHandle = initialPubChatHandle || megaChat.initialPubChatHandle; - megaChat.destroy(); - is_chatlink = false; - loadingDialog.pshow(); - return new Promise((res, rej) => { - this.initFmAndChat(chatId).then(() => { - megaChat.initialPubChatHandle = initialPubChatHandle; - megaChat.initialChatId = chatId; - const mciphReq = megaChat.plugins.chatdIntegration.getMciphReqFromHandleAndKey(initialPubChatHandle, publicChatKey); - const isReady = chatRoom => { - if (chatRoom.state === ChatRoom.STATE.READY) { - res(chatRoom); - loadingDialog.phide(); - } else { - chatRoom.rebind('onMessagesHistoryDone.reinitAndOpenExistingChat', () => { - if (chatRoom.state === ChatRoom.STATE.READY) { - res(chatRoom); - loadingDialog.phide(); - chatRoom.unbind('onMessagesHistoryDone.reinitAndOpenExistingChat'); - } - }); - } - }; - const join = () => { - const existingRoom = megaChat.getChatById(chatId); - if (!existingRoom) { - megaChat.rebind('onRoomInitialized.reinitAndJoinPublicChat', (e, megaRoom) => { - if (megaRoom.chatId === chatId) { - megaRoom.setActive(); - isReady(megaRoom); - megaChat.unbind('onRoomInitialized.reinitAndJoinPublicChat'); - } - }); - } else { - existingRoom.setActive(); - isReady(existingRoom); - } - }; - join(); - asyncApiReq(mciphReq).then(join).catch(ex => { - if (ex === EEXIST) { - join(); - } else { - loadingDialog.phide(); - console.error("Bad response for mciphReq:", mciphReq, ex); - rej(ex); - } - }); - }); + self.rebind('onMessagesHistoryDone.chatlinkAlreadyIn', (e, data) => { + if (!data.chatdPersist) { + unbind(); + } }); } -} -_ChatRouting = ChatRouting; -ChatRouting.gPageHandlers = { - async start({ - location - }) { - return megaChat.onChatsHistoryReady(15e3).then(() => { - return page === location ? megaChat.renderListing() : EACCESS; - }); - }, - async redirect(target, path = 'fm/chat') { - target.location = path; - return _ChatRouting.gPageHandlers.start(target); - }, - async new_meeting(target) { - megaChat.trigger('onStartNewMeeting'); - return _ChatRouting.gPageHandlers.redirect(target); - }, - async contacts({ - section, - args - }) { - this.openCustomView(section); - const [, target = ''] = args; - if (target.length === 11) { - megaChat.routingSubSection = "contact"; - megaChat.routingParams = target; - } else if (target === "received" || target === "sent") { - megaChat.routingSubSection = target; + self.rebind('onMembersUpdatedUI.chatRoomMembersSync', (e, eventData) => { + if (eventData.userId === u_handle) { + self.messagesBuff.joined = true; + if (eventData.priv === 255 || eventData.priv === -1) { + if (self.state === ChatRoom.STATE.JOINING) { + self.setState(ChatRoom.STATE.LEFT); + } + } else { + if (self.state === ChatRoom.STATE.JOINING) { + self.setState(ChatRoom.STATE.READY); + } + } + } + self.trackDataChange(); + }); + self.getParticipantsExceptMe().forEach((userHandle) => { + const contact = M.u[userHandle]; + if (contact && contact.c) { + getLastInteractionWith(contact.u); } + }); + self.megaChat.trigger('onRoomCreated', [self]); + if (this.type === "public" && self.megaChat.publicChatKeys[self.chatId]) { + self.publicChatKey = self.megaChat.publicChatKeys[self.chatId]; } -}; -// EXTERNAL MODULE: ./js/chat/ui/messages/scheduleMetaChange.jsx -const scheduleMetaChange = REQ_(757); -// EXTERNAL MODULE: ./js/chat/chatRoom.jsx + 1 modules -const chat_chatRoom = REQ_(553); -// EXTERNAL MODULE: ./js/chat/ui/meetings/schedule/helpers.jsx -const helpers = REQ_(110); -;// ./js/chat/meetingsManager.jsx - - - -class Occurrence { - constructor(megaChat, occurrence) { - const { - decodeData - } = megaChat.plugins.meetingsManager; - this.megaChat = megaChat; - this.id = occurrence.id; - this.uid = `${occurrence.cid}-${occurrence.o || occurrence.s}`; - this.chatId = occurrence.cid; - this.parentId = occurrence.p; - this.start = occurrence.s * 1000; - this.startInitial = parseInt(occurrence.o) * 1000 || undefined; - this.end = occurrence.e * 1000; - this.timezone = decodeData(occurrence.tz); - this.title = decodeData(occurrence.t); - this.description = decodeData(occurrence.d); - this.ownerHandle = occurrence.u; - this.flags = occurrence.f; - this.canceled = occurrence.c; - this.scheduledMeeting = occurrence.scheduledMeeting; - } - get isUpcoming() { - return !this.canceled && this.end > Date.now(); - } - cancel() { - const { - encodeData - } = this.megaChat.plugins.meetingsManager; - const req = { - a: 'mcsmp', - p: this.parentId || this.id, - ...this.parentId && { - id: this.id - }, - cid: this.chatId, - o: this.start / 1000, - s: this.start / 1000, - e: this.end / 1000, - tz: encodeData(this.timezone), - t: encodeData(this.title), - d: encodeData(this.description) || '', - f: this.scheduledMeeting.flags, - c: 1 - }; - asyncApiReq(req).catch(ex => console.error('Occurrence > cancel ->', ex)); - } - update(startDateTime, endDateTime) { - const { - encodeData - } = this.megaChat.plugins.meetingsManager; - const req = { - a: 'mcsmp', - cid: this.chatId, - p: this.parentId || this.id, - ...this.parentId && { - id: this.id - }, - o: this.start / 1000, - s: startDateTime / 1000, - e: endDateTime / 1000, - tz: encodeData(this.timezone), - t: encodeData(this.title), - d: encodeData(this.description) || '', - f: this.scheduledMeeting.flags - }; - asyncApiReq(req).catch(ex => console.error('Occurrence > update ->', ex)); + $(window).rebind(`focus.${ self.roomId}`, () => { + if (self.isCurrentlyActive) { + self.trigger("onChatShown"); + } + }); + self.megaChat.rebind(`onRoomDestroy.${ self.roomId}`, (e, room) => { + if (room.roomId == self.roomId) { + $(window).off(`focus.${ self.roomId}`); + } + }); + self.initialMessageHistLoaded = false; + let timer = null; + const _historyIsAvailable = ev => { + self.initialMessageHistLoaded = ev ? true : -1; + if (timer) { + timer.abort(); + timer = null; + } + self.unbind('onMarkAsJoinRequested.initHist'); + self.unbind('onHistoryDecrypted.initHist'); + self.unbind('onMessagesHistoryDone.initHist'); + }; + self.rebind('onHistoryDecrypted.initHist', _historyIsAvailable); + self.rebind('onMessagesHistoryDone.initHist', _historyIsAvailable); + self.rebind('onMarkAsJoinRequested.initHist', () => { + (timer = tSleep(300)).then(() => { + if (d) { + self.logger.warn("Timed out waiting to load hist for:", self.chatId || self.roomId); + } + this.historyTimedOut = true; + this.trigger('onHistTimeoutChange'); + timer = null; + _historyIsAvailable(false); + }); + }); + self.rebind('onRoomDisconnected', () => { + if (!self.call) { + for (const activeCallId of self.activeCallIds.keys()) { + self.activeCallIds.remove(activeCallId); + } + megaChat.updateSectionUnreadCount(); + } + }); + this.rebind(`onCallUserLimitExceeded.${chatId}`, () => { + if (this.callUserLimited) { + return; + } + (this.callUserLimited = tSleep(60)).always(() => { + this.callUserLimited = false; + this.trackDataChange(); + }); + }); + this.membersSetFromApi = new ChatRoom.MembersSet(this); + if (publicChatHandle) { + this.onPublicChatRoomInitialized(); } -} -class ScheduledMeeting { - constructor(megaChat, meetingInfo, fromActionPacket) { - const { - decodeData - } = megaChat.plugins.meetingsManager; - this.megaChat = megaChat; - this.id = meetingInfo.id; - this.chatId = meetingInfo.cid; - this.parentId = meetingInfo.p; - this.start = meetingInfo.s * 1000; - this.startInitial = parseInt(meetingInfo.o) * 1000 || undefined; - this.end = meetingInfo.e * 1000; - this.timezone = decodeData(meetingInfo.tz); - this.title = decodeData(meetingInfo.t); - this.description = decodeData(meetingInfo.d); - this.flags = meetingInfo.f; - this.canceled = meetingInfo.c; - this.recurring = meetingInfo.r && { - frequency: meetingInfo.r.f || undefined, - interval: meetingInfo.r.i || 0, - end: meetingInfo.r.u * 1000 || undefined, - weekDays: meetingInfo.r.wd || [], - monthDays: meetingInfo.r.md || [], - offset: meetingInfo.r.mwd && meetingInfo.r.mwd.length ? { - value: meetingInfo.r.mwd[0][0], - weekDay: meetingInfo.r.mwd[0][1] - } : [] - }; - this.occurrences = new MegaDataMap(); - this.nextOccurrenceStart = this.start; - this.nextOccurrenceEnd = this.end; - this.isCompleted = false; - this.ownerHandle = meetingInfo.u; - this.chatRoom = meetingInfo.chatRoom; - this.chatRoom.scheduledMeeting = this.isRoot ? this : this.parent; - if (fromActionPacket) { - this.initializeFromActionPacket(); + return this; +}; +inherits(ChatRoom, MegaDataObject); +ChatRoom.STATE = { + 'INITIALIZED': 5, + 'JOINING': 10, + 'JOINED': 20, + 'READY': 150, + 'ENDED': 190, + 'LEAVING': 200, + 'LEFT': 250 +}; +ChatRoom.INSTANCE_INDEX = 0; +ChatRoom.ANONYMOUS_PARTICIPANT = mega.BID; +ChatRoom.ARCHIVED = 0x01; +ChatRoom.TOPIC_MAX_LENGTH = 30; +ChatRoom.SCHEDULED_MEETINGS_INTERVAL = 1.8e6; +ChatRoom._fnRequireParticipantKeys = function (fn, scope) { + return function (...args) { + const participants = this.protocolHandler.getTrackedParticipants(); + return ChatdIntegration._ensureKeysAreLoaded(undefined, participants).then(() => { + return fn.apply(scope || this, args); + }).catch(ex => { + this.logger.error("Failed to retrieve keys..", ex); + }); + }; +}; +ChatRoom.MembersSet = function (chatRoom) { + this.chatRoom = chatRoom; + this.members = {}; +}; +ChatRoom.MembersSet.PRIVILEGE_STATE = { + NOT_AVAILABLE: -5, + OPERATOR: 3, + FULL: 2, + READONLY: 0, + LEFT: -1 +}; +ChatRoom.encryptTopic = function (protocolHandler, newTopic, participants, isPublic = false) { + if (protocolHandler instanceof strongvelope.ProtocolHandler && participants.size > 0) { + const topic = protocolHandler.embeddedEncryptTo(newTopic, strongvelope.MESSAGE_TYPES.TOPIC_CHANGE, participants, undefined, isPublic); + if (topic) { + return base64urlencode(topic); } } - get isRoot() { - return !this.parentId; + return false; +}; +ChatRoom.MembersSet.prototype.trackFromActionPacket = function (ap, isMcf) { + const self = this; + const apMembers = {}; + (ap.u || []).forEach((r) => { + apMembers[r.u] = r.p; + }); + Object.keys(self.members).forEach((u_h) => { + if (typeof apMembers[u_h] === 'undefined') { + self.remove(u_h); + } else if (apMembers[u_h] !== self.members[u_h]) { + self.update(u_h, apMembers[u_h]); + } + }); + Object.keys(apMembers).forEach((u_h) => { + if (typeof self.members[u_h] === 'undefined') { + const priv2 = apMembers[u_h]; + !isMcf ? self.add(u_h, priv2) : self.init(u_h, priv2); + } else if (apMembers[u_h] !== self.members[u_h]) { + self.update(u_h, apMembers[u_h]); + } + }); + if (!isMcf && ap.m === 1 && !ap.n && ap.url && ap.ou !== u_handle && typeof ap.p === 'undefined' && !ap.topicChange) { + self.chatRoom.trigger('onMeAdded', ap.ou); } - get isCanceled() { - return !!this.canceled; +}; +ChatRoom.MembersSet.prototype.init = function (handle, privilege) { + this.members[handle] = privilege; + this.chatRoom.trackDataChange(); +}; +ChatRoom.MembersSet.prototype.update = function (handle, privilege) { + this.members[handle] = privilege; + this.chatRoom.trackDataChange(); +}; +ChatRoom.MembersSet.prototype.add = function (handle, privilege) { + this.members[handle] = privilege; + if (handle === u_handle) { + this.chatRoom.trigger('onMeJoined'); } - get isPast() { - return (this.isRecurring ? this.recurring.end : this.end) < Date.now(); + this.chatRoom.trackDataChange(); +}; +ChatRoom.MembersSet.prototype.remove = function (handle) { + delete this.members[handle]; + if (handle === u_handle) { + this.chatRoom.trigger('onMeLeft'); } - get isUpcoming() { - return !(this.isCanceled || this.isPast || this.isCompleted); + this.chatRoom.trackDataChange(); +}; +ChatRoom.prototype.trackMemberUpdatesFromActionPacket = function (ap, isMcf) { + if (!ap.u) { + return; } - get isRecurring() { - return !!this.recurring; + if (this.membersSetFromApi) { + this.membersSetFromApi.trackFromActionPacket(ap, isMcf); } - get isNear() { - return this.start - Date.now() < ChatRoom.SCHEDULED_MEETINGS_INTERVAL; +}; +ChatRoom.prototype.getCallParticipants = function () { + const ids = this.activeCallIds.keys(); + if (ids.length === 0) { + return []; } - get iAmOwner() { - if (this.ownerHandle) { - return this.ownerHandle === u_handle; - } - return null; + return this.activeCallIds[ids[0]]; +}; +ChatRoom.prototype.getChatIdMessages = function () { + return this.chatd.chatIdMessages[this.chatIdBin]; +}; +ChatRoom.prototype.getRetentionFormat = function (retentionTime) { + retentionTime = retentionTime || this.retentionTime; + switch (true) { + case retentionTime === 0: + return RETENTION_FORMAT.DISABLED; + case retentionTime % daysToSeconds(30) === 0 || retentionTime >= 31536000: + return RETENTION_FORMAT.MONTHS; + case retentionTime % daysToSeconds(7) === 0: + return RETENTION_FORMAT.WEEKS; + case retentionTime % daysToSeconds(1) === 0: + return RETENTION_FORMAT.DAYS; + default: + return RETENTION_FORMAT.HOURS; } - get parent() { - return this.isRoot ? null : this.megaChat.plugins.meetingsManager.getMeetingById(this.parentId); +}; +ChatRoom.prototype.getRetentionTimeFormatted = function (retentionTime) { + retentionTime = retentionTime || this.retentionTime; + switch (this.getRetentionFormat(retentionTime)) { + case RETENTION_FORMAT.MONTHS: + return Math.floor(secondsToDays(retentionTime) / 30); + case RETENTION_FORMAT.WEEKS: + return secondsToDays(retentionTime) / 7; + case RETENTION_FORMAT.DAYS: + return secondsToDays(retentionTime); + case RETENTION_FORMAT.HOURS: + return secondsToHours(retentionTime); + case RETENTION_FORMAT.DISABLED: + return 0; } - setNextOccurrence() { - const upcomingOccurrences = Object.values(this.occurrences).filter(o => o.isUpcoming); - if (!upcomingOccurrences || !upcomingOccurrences.length) { - this.isCompleted = this.isRecurring; - return; - } - const sortedOccurrences = upcomingOccurrences.sort((a, b) => a.start - b.start); - this.nextOccurrenceStart = sortedOccurrences[0].start; - this.nextOccurrenceEnd = sortedOccurrences[0].end; +}; +ChatRoom.prototype.getRetentionLabel = function (retentionTime) { + retentionTime = retentionTime || this.retentionTime; + const days = secondsToDays(retentionTime); + const months = Math.floor(days / 30); + const hours = secondsToHours(retentionTime); + switch (this.getRetentionFormat(retentionTime)) { + case RETENTION_FORMAT.DISABLED: + return l.disabled_chat_history_cleaning_status; + case RETENTION_FORMAT.MONTHS: + return mega.icu.format(l.months_chat_history_plural, months); + case RETENTION_FORMAT.WEEKS: + return mega.icu.format(l.weeks_chat_history_plural, days / 7); + case RETENTION_FORMAT.DAYS: + return mega.icu.format(l.days_chat_history_plural, days); + case RETENTION_FORMAT.HOURS: + return mega.icu.format(l.hours_chat_history_plural, hours); } - async getOccurrences(options) { - const { - from, - to, - count - } = options || {}; - const { - meetingsManager - } = this.megaChat.plugins; - const req = { - a: 'mcsmfo', - cid: this.chatId, - ...from && { - cf: Math.round(from / 1000) - }, - ...to && { - ct: Math.round(to / 1000) - }, - ...count && { - cc: count +}; +ChatRoom.prototype.setRetention = function (time) { + asyncApiReq({ + "a": "mcsr", + "id": this.chatId, + "d": time, + "ds": 1 + }); +}; +ChatRoom.prototype.removeMessagesByRetentionTime = function () { + const self = this; + const {messages} = self.messagesBuff; + if (messages.length === 0 || this.retentionTime === 0) { + return; + } + const newest = messages.getItem(messages.length - 1); + let lowestValue = newest.orderValue; + let deleteFrom = null; + let lastMessage = null; + const deletePreviousTo = (new Date() - self.retentionTime * 1000) / 1000; + const cp = self.megaChat.plugins.chatdIntegration.chatd.chatdPersist; + let finished = false; + if (typeof cp !== 'undefined') { + const done = function (message) { + if (message) { + if (self.retentionTime > 0 && self.messagesBuff.messages.length > 0) { + self.messagesBuff._removeMessagesBefore(message.messageId); + } + cp.removeMessagesBefore(self.chatId, message.orderValue); } }; - if (is_chatlink) { - req.ph = is_chatlink.ph; - delete req.cid; - } - const occurrences = await asyncApiReq(req); - if (Array.isArray(occurrences)) { - if (!options) { - this.occurrences.clear(); - } - for (let i = 0; i < occurrences.length; i++) { - const occurrence = new Occurrence(this.megaChat, { - scheduledMeeting: this, - ...occurrences[i] - }); - this.occurrences.set(occurrence.uid, occurrence); - } - this.isCompleted = false; - this.setNextOccurrence(); - this.megaChat.trigger(meetingsManager.EVENTS.OCCURRENCES_UPDATE, this); + if (newest.delay < deletePreviousTo) { + cp.clearChatHistoryForChat(self.chatId); + return; } - return this.occurrences; - } - getOccurrencesById(occurrenceId) { - const occurrences = Object.values(this.occurrences.toJS()).filter(o => o.id === occurrenceId); - return occurrences.length ? occurrences : false; - } - initializeFromActionPacket() { - const { - megaChat, - isUpcoming, - isCanceled, - isRecurring, - parent - } = this; - if (isUpcoming && isRecurring || parent) { - return parent ? (() => { - const occurrences = Object.values(parent.occurrences); - if (occurrences.length <= 20) { - return parent.getOccurrences().catch(nop); - } - occurrences.sort((a, b) => a.start - b.start); - const { - chatId, - start, - startInitial - } = this; - const currentIndex = occurrences.findIndex(o => o.uid === `${chatId}-${(startInitial || start) / 1000}`); - const previous = occurrences[currentIndex - 1]; - if (!previous) { - return parent.getOccurrences().catch(nop); - } - const movedBack = start <= previous.start; - let tmp = 0; - let newStart = movedBack ? Date.now() : previous.end; - const maxIdx = movedBack ? currentIndex + 1 : occurrences.length; - const startIdx = movedBack ? 0 : currentIndex; - for (let i = startIdx; i < maxIdx; i++) { - if (++tmp % 20 === 0) { - parent.getOccurrences({ - from: newStart, - to: occurrences[i].end, - count: 20 - }).catch(dump); - newStart = occurrences[i].end; - tmp = 0; + const removeMsgs = function () { + cp._paginateMessages(lowestValue, Chatd.MESSAGE_HISTORY_LOAD_COUNT, self.chatId).then((messages) => { + messages = messages[0]; + if (messages.length) { + for (let i = 0; i < messages.length; i++) { + const message = messages[i]; + if (message.msgObject.delay < deletePreviousTo) { + deleteFrom = lastMessage || message; + break; + } + lastMessage = message; + lowestValue = message.orderValue; } - parent.occurrences.remove(occurrences[i].uid); + } else { + finished = true; } - if (tmp) { - parent.getOccurrences({ - from: newStart, - count: tmp, - to: movedBack ? occurrences[currentIndex].end : occurrences[occurrences.length - 1].end - }).catch(dump); + if (!finished && !deleteFrom) { + onIdle(removeMsgs); + } else { + done(deleteFrom); } - })() : this.getOccurrences().catch(nop); - } - megaChat.trigger(megaChat.plugins.meetingsManager.EVENTS[isCanceled ? 'CANCEL' : 'INITIALIZE'], this); + }); + }; + removeMsgs(); } - isSameAsOpts(opts) { - const { - timezone, - startDateTime, - endDateTime, - topic, - description, - f, - recurring - } = opts; - if (this.timezone !== timezone || this.start !== startDateTime || this.end !== endDateTime) { - return false; - } - if (this.title !== topic) { - return false; - } - if (this.description !== description) { - return false; - } - if (this.flags !== f) { - return false; - } - if (!!this.recurring ^ !!recurring) { - return false; - } - if (this.recurring) { - if (this.recurring.frequency !== recurring.frequency || this.recurring.interval !== (recurring.interval || 0)) { - return false; - } - if (this.recurring.end !== recurring.end) { - return false; - } - let diff = array.diff(this.recurring.weekDays, recurring.weekDays || []); - if (diff.removed.length + diff.added.length) { - return false; - } - diff = array.diff(this.recurring.monthDays, recurring.monthDays || []); - if (diff.removed.length + diff.added.length) { - return false; - } - if (Array.isArray(this.recurring.offset) && !Array.isArray(recurring.offset) || !Array.isArray(this.recurring.offset) && Array.isArray(recurring.offset)) { - return false; - } - if ((this.recurring.offset.value || 0) !== (recurring.offset.value || 0) || (this.recurring.offset.weekDay || 0) !== (recurring.offset.weekDay || 0)) { - return false; + if (self.retentionTime > 0 && self.messagesBuff.messages.length > 0) { + let message; + while (message = self.messagesBuff.messages.getItem(0)) { + if (message.delay < deletePreviousTo) { + if (!self.messagesBuff.messages.removeByKey(message.messageId)) { + break; + } + } else { + break; } } - return true; } -} -class MeetingsManager { - constructor(megaChat) { - this.EVENTS = { - INITIALIZE: 'onMeetingInitialize', - EDIT: 'onMeetingEdit', - CANCEL: 'onMeetingCancel', - LEAVE: 'onMeetingLeave', - OCCURRENCES_UPDATE: 'onOccurrencesUpdate' - }; - this.startDayStrings = [l.schedule_occur_sun, l.schedule_occur_mon, l.schedule_occur_tue, l.schedule_occur_wed, l.schedule_occur_thu, l.schedule_occur_fri, l.schedule_occur_sat]; - this.midDayStrings = [l.schedule_occur_sun_mid, l.schedule_occur_mon_mid, l.schedule_occur_tue_mid, l.schedule_occur_wed_mid, l.schedule_occur_thu_mid, l.schedule_occur_fri_mid, l.schedule_occur_sat_mid]; - this.NOTIF_TITLES = { - recur: { - desc: { - update: l.schedule_notif_update_desc - }, - name: { - update: l.schedule_mgmt_title - }, - time: { - occur: l.schedule_mgmt_update_occur, - all: l.schedule_mgmt_update_recur - }, - convert: l.schedule_mgmt_update_convert_recur, - inv: l.schedule_notif_invite_recur, - multi: l.schedule_notif_update_multi, - cancel: { - occur: l.schedule_mgmt_cancel_occur, - all: l.schedule_mgmt_cancel_recur - } - }, - once: { - desc: { - update: l.schedule_notif_update_desc - }, - name: { - update: l.schedule_mgmt_title - }, - time: { - occur: '', - all: l.schedule_mgmt_update - }, - convert: l.schedule_mgmt_update_convert, - inv: l.schedule_notif_invite, - multi: l.schedule_notif_update_multi, - cancel: { - occur: '', - all: l.schedule_mgmt_cancel - } - } - }; - this.OCCUR_STRINGS = { - recur: { - daily: { - continuous: { - occur: l.schedule_recur_time_daily_cont, - skip: l.scheduled_recur_time_daily_skip_cont - }, - limited: { - occur: l.schedule_recur_time_daily, - skip: l.scheduled_recur_time_daily_skip - } - }, - weekly: { - continuous: { - list: l.schedule_recur_time_week_cont_list, - spec: l.schedule_recur_time_week_cont - }, - limited: { - list: l.schedule_recur_time_week_list, - spec: l.schedule_recur_time_week - } - }, - monthly: { - continuous: { - num: l.schedule_recur_time_num_day_month_cont, - pos: [[l.schedule_recur_time_first_day_month_6_cont, l.schedule_recur_time_first_day_month_0_cont, l.schedule_recur_time_first_day_month_1_cont, l.schedule_recur_time_first_day_month_2_cont, l.schedule_recur_time_first_day_month_3_cont, l.schedule_recur_time_first_day_month_4_cont, l.schedule_recur_time_first_day_month_5_cont], [l.schedule_recur_time_second_day_month_6_cont, l.schedule_recur_time_second_day_month_0_cont, l.schedule_recur_time_second_day_month_1_cont, l.schedule_recur_time_second_day_month_2_cont, l.schedule_recur_time_second_day_month_3_cont, l.schedule_recur_time_second_day_month_4_cont, l.schedule_recur_time_second_day_month_5_cont], [l.schedule_recur_time_third_day_month_6_cont, l.schedule_recur_time_third_day_month_0_cont, l.schedule_recur_time_third_day_month_1_cont, l.schedule_recur_time_third_day_month_2_cont, l.schedule_recur_time_third_day_month_3_cont, l.schedule_recur_time_third_day_month_4_cont, l.schedule_recur_time_third_day_month_5_cont], [l.schedule_recur_time_fourth_day_month_6_cont, l.schedule_recur_time_fourth_day_month_0_cont, l.schedule_recur_time_fourth_day_month_1_cont, l.schedule_recur_time_fourth_day_month_2_cont, l.schedule_recur_time_fourth_day_month_3_cont, l.schedule_recur_time_fourth_day_month_4_cont, l.schedule_recur_time_fourth_day_month_5_cont], [l.schedule_recur_time_fifth_day_month_6_cont, l.schedule_recur_time_fifth_day_month_0_cont, l.schedule_recur_time_fifth_day_month_1_cont, l.schedule_recur_time_fifth_day_month_2_cont, l.schedule_recur_time_fifth_day_month_3_cont, l.schedule_recur_time_fifth_day_month_4_cont, l.schedule_recur_time_fifth_day_month_5_cont]], - last: [l.schedule_recur_time_fifth_day_month_6_cont, l.schedule_recur_time_fifth_day_month_0_cont, l.schedule_recur_time_fifth_day_month_1_cont, l.schedule_recur_time_fifth_day_month_2_cont, l.schedule_recur_time_fifth_day_month_3_cont, l.schedule_recur_time_fifth_day_month_4_cont, l.schedule_recur_time_fifth_day_month_5_cont] - }, - limited: { - num: l.schedule_recur_time_num_day_month, - pos: [[l.schedule_recur_time_first_day_month_6, l.schedule_recur_time_first_day_month_0, l.schedule_recur_time_first_day_month_1, l.schedule_recur_time_first_day_month_2, l.schedule_recur_time_first_day_month_3, l.schedule_recur_time_first_day_month_4, l.schedule_recur_time_first_day_month_5], [l.schedule_recur_time_second_day_month_6, l.schedule_recur_time_second_day_month_0, l.schedule_recur_time_second_day_month_1, l.schedule_recur_time_second_day_month_2, l.schedule_recur_time_second_day_month_3, l.schedule_recur_time_second_day_month_4, l.schedule_recur_time_second_day_month_5], [l.schedule_recur_time_third_day_month_6, l.schedule_recur_time_third_day_month_0, l.schedule_recur_time_third_day_month_1, l.schedule_recur_time_third_day_month_2, l.schedule_recur_time_third_day_month_3, l.schedule_recur_time_third_day_month_4, l.schedule_recur_time_third_day_month_5], [l.schedule_recur_time_fourth_day_month_6, l.schedule_recur_time_fourth_day_month_0, l.schedule_recur_time_fourth_day_month_1, l.schedule_recur_time_fourth_day_month_2, l.schedule_recur_time_fourth_day_month_3, l.schedule_recur_time_fourth_day_month_4, l.schedule_recur_time_fourth_day_month_5], [l.schedule_recur_time_fifth_day_month_6, l.schedule_recur_time_fifth_day_month_0, l.schedule_recur_time_fifth_day_month_1, l.schedule_recur_time_fifth_day_month_2, l.schedule_recur_time_fifth_day_month_3, l.schedule_recur_time_fifth_day_month_4, l.schedule_recur_time_fifth_day_month_5]], - last: [l.schedule_recur_time_last_day_month_6, l.schedule_recur_time_last_day_month_0, l.schedule_recur_time_last_day_month_1, l.schedule_recur_time_last_day_month_2, l.schedule_recur_time_last_day_month_3, l.schedule_recur_time_last_day_month_4, l.schedule_recur_time_last_day_month_5] - } - }, - [scheduleMetaChange.A.MODE.CANCELLED]: { - occur: l.schedule_occurrence_time, - all: '' - } - }, - once: { - [scheduleMetaChange.A.MODE.CREATED]: { - occur: l.schedule_occurrence_time - }, - [scheduleMetaChange.A.MODE.EDITED]: { - occur: l.schedule_occurrence_time_recur - }, - [scheduleMetaChange.A.MODE.CANCELLED]: { - occur: '' - } - } - }; - this.megaChat = megaChat; - this.scheduledMeetings = megaChat.scheduledMeetings || new MegaDataMap(); - this._goneOccurrences = {}; - this.megaChat.rebind(this.EVENTS.CANCEL, ({ - data - }) => this.archiveMeeting(data)); - this.megaChat.rebind(this.EVENTS.LEAVE, ({ - data - }) => this.detachMeeting(data)); - this.megaChat.rebind(`${this.EVENTS.OCCURRENCES_UPDATE}.tracker`, ({ - data - }) => { - if (!this._goneOccurrences[data.chatId]) { - return; - } - const { - chatId - } = data; - for (const scheduledId of Object.keys(this._goneOccurrences[chatId])) { - if (this._goneOccurrences[chatId][scheduledId] === -1) { - this._goneOccurrences[chatId][scheduledId] = this.scheduledMeetings[scheduledId] ? 0 : 1; - } - } - }); +}; +ChatRoom.prototype.isOnline = function () { + const shard = this.chatd.shards[this.chatShard]; + return shard ? shard.isOnline() : false; +}; +ChatRoom.prototype.isOnlineForCalls = function () { + const chatdChat = this.getChatIdMessages(); + if (!chatdChat) { + return false; } - checkForNotifications() { - const time = Date.now(); - const upcomingMeetings = Object.values(this.scheduledMeetings.toJS()).filter(c => c.isUpcoming); - for (const meeting of upcomingMeetings) { - if (pushNotificationSettings.isAllowedForChatId(meeting.chatId)) { - if (meeting.nextOccurrenceStart >= time + 9e5 && meeting.nextOccurrenceStart <= time + 96e4) { - const ss = Math.floor(meeting.nextOccurrenceStart / 1000); - const ns = Math.floor(time / 1000) + 900; - if (ss - ns <= 10) { - this.megaChat.trigger('onScheduleUpcoming', meeting); - } else { - tSleep(ss - ns).always(() => { - this.megaChat.trigger('onScheduleUpcoming', meeting); - }); - } - } else if (meeting.nextOccurrenceStart >= time && meeting.nextOccurrenceStart < time + 6e4) { - const ss = Math.floor(meeting.nextOccurrenceStart / 1000); - const ns = Math.floor(time / 1000); - tSleep(ss - ns).always(() => { - this.megaChat.trigger('onScheduleStarting', meeting); - }); - } - } + return chatdChat.loginState() >= LoginState.HISTDONE; +}; +ChatRoom.prototype.isArchived = function () { + const self = this; + return self.flags & ChatRoom.ARCHIVED; +}; +ChatRoom.prototype.isAnonymous = function () { + return is_chatlink && this.type === "public" && this.publicChatHandle && this.publicChatKey && this.publicChatHandle === megaChat.initialPubChatHandle; +}; +ChatRoom.prototype.isDisplayable = function () { + return !this.isArchived() || this.call; +}; +ChatRoom.prototype.isMuted = function () { + return pushNotificationSettings.getDnd(this.chatId) || pushNotificationSettings.getDnd(this.chatId) === 0; +}; +ChatRoom.prototype.persistToFmdb = function () { + const self = this; + if (fmdb) { + const users = []; + if (self.members) { + Object.keys(self.members).forEach((user_handle) => { + users.push({ + u: user_handle, + p: self.members[user_handle] + }); + }); + } + if (self.chatId && self.chatShard !== undefined) { + const roomInfo = { + 'id': self.chatId, + 'cs': self.chatShard, + 'g': self.type === "group" || self.type === "public" ? 1 : 0, + 'u': users, + 'ts': self.ctime, + 'ct': self.ct, + 'ck': self.ck ? self.ck : null, + 'f': self.flags, + 'm': self.type === "public" ? 1 : 0 + }; + fmdb.add('mcf', { + id: roomInfo.id, + d: roomInfo + }); } } - encodeData(data) { - return data && base64urlencode(to8(data)); - } - decodeData(data) { - return data && from8(base64urldecode(data)); +}; +ChatRoom.prototype.updateFlags = function (f, updateUI) { + const self = this; + const flagChange = self.flags !== f; + self.flags = f; + if (self.isArchived()) { + megaChat.archivedChatsCount++; + } else { + megaChat.archivedChatsCount--; } - getMeetingById(meetingId) { - return this.scheduledMeetings[meetingId]; + self.persistToFmdb(); + if (updateUI && flagChange) { + if (megaChat.currentlyOpenedChat && megaChat.chats[megaChat.currentlyOpenedChat] && megaChat.chats[megaChat.currentlyOpenedChat].chatId === self.chatId) { + loadSubPage('fm/chat/'); + } else { + megaChat.refreshConversations(); + } } - getMeetingOrOccurrenceParent(meetingId) { - const meeting = this.scheduledMeetings[meetingId]; - if (!meeting) { + this.trackDataChange(); +}; +ChatRoom.stateToText = function (state) { + let txt = null; + $.each(ChatRoom.STATE, (k, v) => { + if (state === v) { + txt = k; return false; } - if (meeting.parentId) { - return this.getMeetingOrOccurrenceParent(meeting.parentId); - } - return meeting; + }); + return txt; +}; +ChatRoom.prototype.setState = function (newState, isRecover) { + const self = this; + assert(newState, 'Missing state'); + if (newState === self.state) { + self.logger.debug("Ignoring .setState, newState === oldState, current state: ", self.getStateAsText()); + return; } - getRoomByMeetingId() {} - async createMeeting(meetingInfo) { - await this.megaChat.createAndShowGroupRoomFor(meetingInfo.participants, meetingInfo.topic, { - keyRotation: false, - createChatLink: meetingInfo.link, - isMeeting: true, - openInvite: meetingInfo.openInvite, - waitingRoom: meetingInfo.waitingRoom, - scheduledMeeting: { - a: 'mcsmp', - s: meetingInfo.startDateTime / 1000, - e: meetingInfo.endDateTime / 1000, - tz: this.encodeData(meetingInfo.timezone), - t: this.encodeData(meetingInfo.topic), - d: this.encodeData(meetingInfo.description), - f: meetingInfo.sendInvite ? 0x01 : 0x00, - ...meetingInfo.recurring && { - r: { - f: meetingInfo.recurring.frequency, - wd: meetingInfo.recurring.weekDays, - md: meetingInfo.recurring.monthDays, - mwd: meetingInfo.recurring.offset, - ...meetingInfo.recurring.end && { - u: meetingInfo.recurring.end / 1000 - }, - ...meetingInfo.recurring.interval && { - i: meetingInfo.recurring.interval - } - } - } - } - }); + if (self.state) { + assert(newState === ChatRoom.STATE.JOINING && isRecover || newState === ChatRoom.STATE.INITIALIZED && isRecover || newState > self.state, `Invalid state change. Current:${ ChatRoom.stateToText(self.state) }to${ ChatRoom.stateToText(newState)}`); } - async updateMeeting(meetingInfo, chatRoom) { - const { - scheduledMeeting, - chatId, - publicLink, - options - } = chatRoom; - await megaChat.plugins.chatdIntegration.updateScheduledMeeting(meetingInfo, scheduledMeeting.id, chatId); - const nextParticipants = meetingInfo.participants; - const prevParticipants = chatRoom.getParticipantsExceptMe(); - const participantsDiff = JSON.stringify(nextParticipants) !== JSON.stringify(prevParticipants); - if (participantsDiff) { - const removed = prevParticipants.filter(h => !nextParticipants.includes(h)); - const added = nextParticipants.filter(h => !prevParticipants.includes(h)); - if (removed.length) { - for (let i = removed.length; i--;) { - chatRoom.trigger('onRemoveUserRequest', [removed[i]]); - } - } - if (added.length) { - chatRoom.trigger('onAddUserRequest', [added]); - } - } - if (!!meetingInfo.link !== !!publicLink) { - chatRoom.updatePublicHandle(!meetingInfo.link, meetingInfo.link); - } - if (meetingInfo.waitingRoom !== options[chat_chatRoom.MCO_FLAGS.WAITING_ROOM]) { - chatRoom.toggleWaitingRoom(); + const oldState = self.state; + self.state = newState; + self.trigger('onStateChange', [oldState, newState]); +}; +ChatRoom.prototype.getStateAsText = function () { + const self = this; + return ChatRoom.stateToText(self.state); +}; +ChatRoom.prototype.getParticipants = function () { + const self = this; + return Object.keys(self.members); +}; +ChatRoom.prototype.getParticipantsExceptMe = function (userHandles) { + const res = clone(userHandles || this.getParticipants()); + array.remove(res, u_handle, true); + return res; +}; +ChatRoom.prototype.getParticipantsTruncated = function (maxMembers = 5, maxLength = ChatRoom.TOPIC_MAX_LENGTH) { + const truncatedParticipantNames = []; + const members = Object.keys(this.members); + for (let i = 0; i < members.length; i++) { + const handle = members[i]; + const name = M.getNameByHandle(handle); + if (!handle || !name || handle === u_handle) { + continue; } - if (meetingInfo.openInvite !== options[chat_chatRoom.MCO_FLAGS.OPEN_INVITE]) { - chatRoom.toggleOpenInvite(); + if (i > maxMembers) { + break; } + truncatedParticipantNames.push(name.length > maxLength ? `${name.substr(0, maxLength) }...` : name); } - cancelMeeting(scheduledMeeting, chatId) { - return this.megaChat.plugins.chatdIntegration.cancelScheduledMeeting(scheduledMeeting, chatId); + if (truncatedParticipantNames.length === maxMembers) { + truncatedParticipantNames.push('...'); } - deleteMeeting(scheduledMeetingId, chatId) { - return this.megaChat.plugins.chatdIntegration.deleteScheduledMeeting(scheduledMeetingId, chatId); + return truncatedParticipantNames.join(', '); +}; +ChatRoom.prototype.getRoomTitle = function () { + const formattedDate = l[19077].replace('%s1', new Date(this.ctime * 1000).toLocaleString()); + if (this.isNote) { + return l.note_label; } - attachMeeting(meetingInfo, fromActionPacket) { - const chatRoom = meetingInfo.chatRoom || this.megaChat.getChatById(meetingInfo.cid); - if (chatRoom) { - const scheduledMeeting = new ScheduledMeeting(this.megaChat, { - chatRoom, - ...meetingInfo - }, fromActionPacket); - this.scheduledMeetings.set(meetingInfo.id, scheduledMeeting); - return scheduledMeeting; - } + if (this.type === 'private') { + const participants = this.getParticipantsExceptMe(); + return participants && Array.isArray(participants) ? M.getNameByHandle(participants[0]) : formattedDate; } - detachMeeting(scheduledMeeting) { - if (scheduledMeeting) { - this.archiveMeeting(scheduledMeeting); - scheduledMeeting.chatRoom.scheduledMeeting = null; - this.scheduledMeetings.remove(scheduledMeeting.id); - if (fmdb) { - fmdb.del('mcsm', scheduledMeeting.id); - } + if (this.topic === '' || !this.topic) { + return this.getParticipantsTruncated() || formattedDate; + } + const formattedTopic = this.getTruncatedRoomTopic(); + const isCanceled = this.scheduledMeeting && this.scheduledMeeting.isCanceled; + return isCanceled ? `${formattedTopic} ${l.canceled_meeting}` : formattedTopic; +}; +ChatRoom.prototype.getTruncatedRoomTopic = function (maxLength = ChatRoom.TOPIC_MAX_LENGTH) { + return this.topic && this.topic.length > maxLength ? `${this.topic.substr(0, maxLength) }...` : this.topic; +}; +ChatRoom.prototype.setRoomTopic = async function (newTopic) { + if (newTopic && newTopic.trim().length && newTopic !== this.getRoomTitle()) { + this.scrolledToBottom = true; + const participants = this.protocolHandler.getTrackedParticipants(); + await ChatdIntegration._ensureKeysAreLoaded(undefined, participants); + const topic = this.protocolHandler.embeddedEncryptTo(newTopic, strongvelope.MESSAGE_TYPES.TOPIC_CHANGE, participants, undefined, this.type === 'public'); + if (topic) { + return api.req({ + a: 'mcst', + id: this.chatId, + ct: base64urlencode(topic), + v: Chatd.VERSION + }); } } - archiveMeeting(scheduledMeeting) { - const { - chatRoom - } = scheduledMeeting; - tSleep(2).then(() => chatRoom.hasMessages(true) ? null : chatRoom.archive()); +}; +ChatRoom.prototype.leave = function (notify) { + const valid = this.type === 'group' || this.type === 'public'; + console.assert(valid, `Can't leave room "${this.roomId}" of type "${this.type}"`); + if (!valid) { + return; } - filterUpcomingMeetings(conversations) { - const upcomingMeetings = Object.values(conversations || {}).filter(c => { - return c.isDisplayable() && c.isMeeting && c.scheduledMeeting && c.scheduledMeeting.isUpcoming && c.iAmInRoom() && !c.havePendingCall(); - }).sort((a, b) => a.scheduledMeeting.nextOccurrenceStart - b.scheduledMeeting.nextOccurrenceStart || a.ctime - b.ctime); - const nextOccurrences = upcomingMeetings.reduce((nextOccurrences, chatRoom) => { - const { - nextOccurrenceStart - } = chatRoom.scheduledMeeting; - if ((0,helpers.cK)(nextOccurrenceStart)) { - nextOccurrences.today.push(chatRoom); - } else if ((0,helpers.ef)(nextOccurrenceStart)) { - nextOccurrences.tomorrow.push(chatRoom); - } else { - const date = time2date(nextOccurrenceStart / 1000, 19); - if (!nextOccurrences.rest[date]) { - nextOccurrences.rest[date] = []; - } - nextOccurrences.rest[date].push(chatRoom); - } - return nextOccurrences; - }, { - today: [], - tomorrow: [], - rest: {} - }); - return { - upcomingMeetings, - nextOccurrences - }; + this._leaving = true; + this.topic = ''; + if (notify) { + this.trigger('onLeaveChatRequested'); } - getOccurrenceStrings(meta) { - const res = []; - const { - prevTiming, - timeRules, - mode, - occurrence, - recurring, - converted - } = meta; - const { - MODE - } = scheduleMetaChange.A; - if (!mode) { - return res; - } - const { - OCCUR_STRINGS - } = this; - let string; - if (recurring) { - res.push(this._parseOccurrence(timeRules, mode, occurrence)); - if (prevTiming && !(occurrence && mode === MODE.CANCELLED)) { - res.push(this._parseOccurrence(prevTiming, mode, occurrence)); - } - } else { - const { - startTime, - endTime - } = timeRules; - string = OCCUR_STRINGS.once[mode].occur; - res.push(string.replace('%1', toLocaleTime(startTime)).replace('%2', toLocaleTime(endTime)).replace('%6', time2date(startTime, 20)).replace('%s', time2date(startTime, 11))); - if (prevTiming) { - const { - startTime: pStartTime, - endTime: pEndTime - } = prevTiming; - if (converted) { - res.push(this._parseOccurrence(prevTiming, mode, occurrence)); - } else { - res.push(string.replace('%1', toLocaleTime(pStartTime)).replace('%2', toLocaleTime(pEndTime)).replace('%6', time2date(pStartTime, 20)).replace('%s', time2date(pStartTime, 11))); - } - } + if (this.state !== ChatRoom.STATE.LEFT) { + this.setState(ChatRoom.STATE.LEAVING); + this.setState(ChatRoom.STATE.LEFT); + } + if (this.activeCallIds.length) { + for (const activeCallId of this.activeCallIds.keys()) { + this.activeCallIds.remove(activeCallId); } - return res; + megaChat.updateSectionUnreadCount(); } - _parseOccurrence(timeRules, mode, occurrence) { - const { - startTime, - endTime, - days, - dayInt, - interval, - month, - recurEnd, - skipDay - } = timeRules; - const { - recur, - once - } = this.OCCUR_STRINGS; - const occurrenceEnd = recurEnd ? 'limited' : 'continuous'; - let string = ''; - if (recur[mode]) { - return occurrence ? recur[mode].occur.replace('%1', toLocaleTime(startTime)).replace('%2', toLocaleTime(endTime)).replace('%s', time2date(startTime, 11)) : recur[mode].all; - } else if (month) { - const { - count, - occur - } = month; - string = count < 0 ? mega.icu.format(recur.monthly[occurrenceEnd].last[occur], interval) : mega.icu.format(recur.monthly[occurrenceEnd].pos[count][occur], interval); - return string.replace('%1', toLocaleTime(startTime)).replace('%2', toLocaleTime(endTime)).replace('%3', time2date(startTime, 2)).replace('%4', time2date(recurEnd, 2)); - } else if (days) { - if (days.length > 1) { - if (days.length === 7) { - return recur.daily[occurrenceEnd].occur.replace('%1', toLocaleTime(startTime)).replace('%2', toLocaleTime(endTime)).replace('%3', time2date(startTime, 2)).replace('%4', time2date(recurEnd, 2)); - } - const weekDays = days.map((day, idx) => { - if (idx) { - return this.midDayStrings[day]; - } - return this.startDayStrings[day]; - }); - string = mega.icu.format(recur.weekly[occurrenceEnd].list, interval); - string = mega.utils.trans.listToString(weekDays, string); - } else { - string = mega.icu.format(recur.weekly[occurrenceEnd].spec, interval).replace('%s', this.startDayStrings[days[0]]); - } - return string.replace('%1', toLocaleTime(startTime)).replace('%2', toLocaleTime(endTime)).replace('%3', time2date(startTime, 2)).replace('%4', time2date(recurEnd, 2)); - } else if (dayInt) { - string = mega.icu.format(recur.monthly[occurrenceEnd].num, interval); - return string.replace('%1', toLocaleTime(startTime)).replace('%2', toLocaleTime(endTime)).replace('%3', time2date(startTime, 2)).replace('%4', time2date(recurEnd, 2)).replace('%5', dayInt); - } else if (skipDay) { - string = mega.icu.format(recur.daily[occurrenceEnd].skip, interval); - return string.replace('%1', toLocaleTime(startTime)).replace('%2', toLocaleTime(endTime)).replace('%3', time2date(startTime, 2)).replace('%4', time2date(recurEnd, 2)); - } - string = once[mode].occur; - return string.replace('%1', toLocaleTime(startTime)).replace('%2', toLocaleTime(endTime)).replace('%6', time2date(startTime, 20)).replace('%s', time2date(startTime, 11)); - } - getFormattingMeta(scheduledId, data, chatRoom) { - const { - MODE - } = scheduleMetaChange.A; - const meta = { - userId: data.sender || false, - timeRules: {}, - mode: MODE.EDITED, - handle: scheduledId, - cid: chatRoom.chatId - }; - const changeSet = data.schedChange || data.cs || false; - if (changeSet) { - const { - s, - e, - c, - r, - t, - d: desc - } = changeSet; - let onlyTitle = typeof t !== 'undefined'; - if (Array.isArray(c) && c[1]) { - meta.mode = MODE.CANCELLED; - } - if (Array.isArray(s)) { - meta.prevTiming = { - startTime: s[0] - }; - meta.timeRules.startTime = s[1] || s[0]; - } - const meeting = this.getMeetingOrOccurrenceParent(scheduledId); - if (Array.isArray(e)) { - if (!meta.prevTiming) { - meta.prevTiming = { - startTime: meeting ? Math.floor(meeting.start / 1000) : 0 - }; - meta.timeRules.startTime = meta.prevTiming.startTime; - } - meta.prevTiming.endTime = e[0]; - meta.timeRules.endTime = e[1] || e[0]; - onlyTitle = false; - } - if (desc) { - meta.description = true; - onlyTitle = false; - } - if (Array.isArray(r)) { - const parseR = r => r ? typeof r === 'string' ? JSON.parse(r) : r : false; - const prev = parseR(r[0]); - const next = parseR(r[1]); - if (r.length === 1) { - meta.converted = false; - meta.timeRules = this._recurringTimings(prev, meta.timeRules || {}); - meta.prevTiming = this._recurringTimings(prev, meta.prevTiming); - meta.recurring = r[0] !== ''; - } else { - meta.converted = !!(!!prev ^ !!next); - if (prev) { - meta.prevTiming = this._recurringTimings(prev, meta.prevTiming || {}); - } - meta.timeRules = this._recurringTimings(next, meta.timeRules || {}); - meta.recurring = next !== false; - } - onlyTitle = false; - } - if (!meeting || meeting.id !== scheduledId) { - meta.occurrence = true; - meta.recurring = true; - } - if (Array.isArray(t)) { - meta.topicChange = true; - meta.onlyTitle = onlyTitle; - meta.topic = this.decodeData(t[1]); - meta.oldTopic = this.decodeData(t[0]); - } - return meta; - } - return this.noCsMeta(scheduledId, data, chatRoom); - } - _recurringTimings(meta, obj) { - if (!meta) { - return obj; - } - obj.recurEnd = meta.u || false; - obj.interval = meta.i || 1; - if (meta.wd) { - obj.days = meta.wd.sort((a, b) => a - b).map(wd => wd === 7 ? 0 : wd); - } - if (meta.md) { - obj.dayInt = meta.md[0]; +}; +ChatRoom.prototype.archive = function () { + const self = this; + const mask = 0x01; + const flags = ChatRoom.ARCHIVED; + asyncApiReq({ + 'a': 'mcsf', + 'id': self.chatId, + 'm': 1, + 'f': flags, + 'v': Chatd.VERSION + }).then(r => { + if (r === 0) { + self.updateFlags(flags, true); } - if (meta.mwd) { - obj.month = meta.mwd.map(oc => { - return { - count: (oc[0] || 1) - 1, - occur: oc[1] ? oc[1] === 7 ? 0 : oc[1] : 1 - }; - })[0]; + }); +}; +ChatRoom.prototype.unarchive = function () { + const self = this; + const mask = 0x01; + const flags = 0x00; + asyncApiReq({ + 'a': 'mcsf', + 'id': self.chatId, + 'm': 1, + 'f': 0, + 'v': Chatd.VERSION + }).then(res => { + if (res === 0) { + self.updateFlags(0, true); } - if (meta.f === 'd' && meta.i > 1) { - obj.skipDay = true; - } else if (meta.f === 'd' && meta.i === 1) { - obj.days = [1, 2, 3, 4, 5, 6, 0]; + }); +}; +ChatRoom.prototype.destroy = function (notifyOtherDevices, noRedirect) { + const self = this; + self.megaChat.trigger('onRoomDestroy', [self]); + const mc = self.megaChat; + const roomJid = self.roomId; + if (!self.stateIsLeftOrLeaving()) { + self.leave(notifyOtherDevices); + } else if (self.type === "public" && self.publicChatHandle) { + if (typeof self.members[u_handle] === 'undefined') { + self.megaChat.plugins.chatdIntegration.handleLeave(self); } - return obj; } - noCsMeta(scheduledId, data, chatRoom) { - const meta = { - timeRules: {}, - userId: data.sender || false, - ap: data, - handle: scheduledId, - cid: chatRoom.chatId - }; - if (!this.getMeetingOrOccurrenceParent(scheduledId) && !chatRoom.scheduledMeeting) { - const res = this._checkOccurrenceAwait(chatRoom, scheduledId, meta); - if (res) { - return res; - } + if (self.isCurrentlyActive) { + self.isCurrentlyActive = false; + } + Soon(() => { + mc.chats.remove(roomJid); + if (!noRedirect && u_type === 3) { + loadSubPage('fm/chat'); } - const meeting = this.getMeetingOrOccurrenceParent(scheduledId) || chatRoom.scheduledMeeting; - assert(meeting, `Invalid scheduled meeting state for ${scheduledId} msg`); - const toS = ms => Math.floor(ms / 1000); - const { - MODE - } = scheduleMetaChange.A; - meta.timeRules.startTime = toS(meeting.start); - meta.timeRules.endTime = toS(meeting.end); - meta.topic = meeting.title; - meta.recurring = !!meeting.recurring; - meta.mode = meeting.canceled ? MODE.CANCELLED : MODE.CREATED; - meta.occurrence = meta.recurring && meeting.id !== scheduledId; - if (!meta.occurrence && !meeting.canceled) { - meta.mode = MODE.CREATED; + }); +}; +ChatRoom.prototype.updatePublicHandle = async function (remove, cim, force) { + if (force) { + this.publicLink = null; + } + if (!remove && this.publicLink) { + return this.publicLink; + } + return asyncApiReq({ + a: 'mcph', + id: this.chatId, + v: Chatd.VERSION, + cim: cim ? 1 : 0, + d: remove ? 1 : undefined + }).then(res => { + assert(remove && res === 0 || Array.isArray(res) && res[1].length === 8); + this.publicLink = remove ? null : `chat/${res[1]}#${this.protocolHandler.getUnifiedKey()}`; + }).catch(ex => { + this.logger.warn('updatePublicHandle', ex); + this.publicLink = null; + }); +}; +ChatRoom.prototype.iAmInRoom = function () { + return !(!this.members.hasOwnProperty(u_handle) || this.members[u_handle] === -1); +}; +ChatRoom.prototype.joinViaPublicHandle = function () { + const self = this; + if (!fminitialized && is_chatlink) { + if (u_type) { + return new Promise((res, rej) => { + self.megaChat.plugins.chatdIntegration.joinChatViaPublicHandle(self).then(() => { + self.megaChat.routing.reinitAndOpenExistingChat(self.chatId, self.publicChatHandle).then(res, rej); + }, ex => { + console.error("Failed joining a chat room (u_type)", ex); + rej(ex); + }); + }); } - const cal = ms => { - const date = new Date(ms); - return { - date: date.getDate(), - month: time2date(toS(ms), 12) - }; - }; - if (meta.occurrence) { - const occurrences = meeting.getOccurrencesById(scheduledId); - if (!occurrences) { - meta.mode = MODE.EDITED; - const res = this._checkOccurrenceAwait(chatRoom, scheduledId, meta); - if (res) { - return res; - } - meta.ap = data; - return meta; - } - meta.mode = occurrences.some(o => o.canceled) ? MODE.CANCELLED : MODE.EDITED; - meta.calendar = cal(occurrences[0].start); - const timeDiff = meta.timeRules.endTime - meta.timeRules.startTime; - meta.timeRules.startTime = toS(occurrences[0].start); - meta.timeRules.endTime = toS(occurrences[0].end); - if (occurrences.length === 1 && occurrences[0].startInitial) { - meta.prevTiming = { - startTime: toS(occurrences[0].startInitial) - }; - meta.prevTiming.endTime = meta.prevTiming.startTime + timeDiff; - } - } else if (meta.recurring) { - const { - end, - weekDays = [], - interval, - monthDays = [], - offset, - frequency - } = meeting.recurring; - meta.recurring = true; - meta.timeRules.recurEnd = end ? toS(end) : false; - meta.timeRules.interval = interval || 1; - if (frequency === 'd' && interval > 1) { - meta.timeRules.skipDay = true; - } else if (frequency === 'd' && interval === 1) { - meta.timeRules.days = [1, 2, 3, 4, 5, 6, 0]; - } - if (weekDays.length) { - meta.timeRules.days = weekDays.sort((a, b) => a - b).map(wd => wd === 7 ? 0 : wd); - } - if (monthDays.length) { - meta.timeRules.dayInt = monthDays[0]; - } - if (!Array.isArray(offset)) { - meta.timeRules.month = { - count: (offset.value || 1) - 1, - occur: offset.weekDay ? offset.weekDay === 7 ? 0 : offset.weekDay : 1 - }; - } - meta.calendar = cal(meeting.start); - } else { - meta.calendar = cal(meeting.start); - } - if (!meta.occurrence && meeting.canceled && $.len(meta.timeRules)) { - meta.mode = MODE.CREATED; - } - delete meta.ap; - return meta; + return; } - _checkOccurrenceAwait(chatRoom, scheduledId, meta) { - if (!this._goneOccurrences[chatRoom.chatId]) { - this._goneOccurrences[chatRoom.chatId] = {}; - } - if (typeof this._goneOccurrences[chatRoom.chatId][scheduledId] === 'undefined') { - this._goneOccurrences[chatRoom.chatId][scheduledId] = -1; - return meta; - } - const datum = this._goneOccurrences[chatRoom.chatId]; - if (datum[scheduledId] === -1) { - return meta; - } else if (datum[scheduledId] === 1) { - meta.gone = true; - return meta; - } + if (!self.iAmInRoom() && self.type === "public" && self.publicChatHandle) { + return megaChat.plugins.chatdIntegration.joinChatViaPublicHandle(self); + } + return Promise.reject(); +}; +ChatRoom.prototype.switchOffPublicMode = ChatRoom._fnRequireParticipantKeys(function () { + let { + topic, + protocolHandler, + chatId + } = this; + if (topic) { + topic = protocolHandler.embeddedEncryptTo(topic, strongvelope.MESSAGE_TYPES.TOPIC_CHANGE, protocolHandler.getTrackedParticipants(), true, false); + topic = base64urlencode(topic); + } + return asyncApiReq({ + a: 'mcscm', + id: chatId, + ct: topic || undefined, + v: Chatd.VERSION + }).then(() => { + protocolHandler.switchOffOpenMode(); + }); +}); +ChatRoom.prototype.show = function () { + if (this.isCurrentlyActive) { return false; } - areMetaObjectsSame(obj1, obj2) { - if (obj1 && !obj2 || !obj1 && obj2) { - return false; - } - const keys = Object.keys(obj1); - if (keys.length !== $.len(obj2)) { - return false; - } - const diff = array.diff(keys, Object.keys(obj2)); - if (diff.removed.length + diff.added.length) { - return false; - } - for (const key of keys) { - if (!obj2.hasOwnProperty(key)) { - return false; - } - if (obj1[key] instanceof Object && obj2[key] instanceof Object) { - if (!this.areMetaObjectsSame(obj1[key], obj2[key])) { - return false; - } - } else if (Array.isArray(obj1[key]) && Array.isArray(obj2[key])) { - const keyDiff = array.diff(obj1[key], obj2[key]); - if (keyDiff.removed.length + keyDiff.added.length) { - return false; - } - } else if (obj1[key] !== obj2[key]) { - return false; - } + this.megaChat.hideAllChats(); + if (d) { + this.logger.debug(' ---- show'); + } + $.tresizer(); + onIdle(() => { + this.scrollToChat(); + this.trackDataChange(); + }); + this.isCurrentlyActive = true; + this.lastShownInUI = Date.now(); + this.megaChat.setAttachments(this.roomId); + this.megaChat.lastOpenedChat = this.roomId; + this.megaChat.currentlyOpenedChat = this.roomId; + this.trigger('activity'); + this.trigger('onChatShown'); + let tmp = this.megaChat.rootDOMNode; + if (tmp = tmp.querySelector('.conversation-panels')) { + tmp.classList.remove('hidden'); + if (tmp = tmp.querySelector(`.conversation-panel[data-room-id="${this.chatId}"]`)) { + tmp.classList.remove('hidden'); } - return true; } -} -const meetingsManager = MeetingsManager; -window.MeetingsManager = MeetingsManager; -// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/applyDecoratedDescriptor.js -const applyDecoratedDescriptor = REQ_(793); -// EXTERNAL MODULE: ./js/chat/mixins.js -const mixins = REQ_(137); -;// ./js/chat/chatOnboarding.jsx - -let _dec, _class; - - -const ChatOnboarding = (_dec = (0,mixins.hG)(1000), _class = class ChatOnboarding { - constructor(megaChat) { - this.finished = false; - if (u_type === 3 && !is_mobile) { - this.state = { - [OBV4_FLAGS.CHAT]: -1 - }; - this.megaChat = megaChat; - this.flagMap = attribCache.bitMapsManager.exists('obv4') ? attribCache.bitMapsManager.get('obv4') : new MegaDataBitMap('obv4', false, Object.values(OBV4_FLAGS)); - const keys = Object.keys(this.state); - const promises = keys.map(key => this.flagMap.get(key)); - Promise.allSettled(promises).then(res => { - for (let i = 0; i < res.length; ++i) { - const v = res[i]; - if (v.status === 'fulfilled') { - this.handleFlagChange(null, null, keys[i], v.value); + if (tmp = document.getElementById(`conversation_${this.roomId}`)) { + tmp.classList.add('active'); + } + if (mega.ui.mInfoPanel) { + mega.ui.mInfoPanel.hide(); + } +}; +ChatRoom.prototype.scrollToChat = function () { + this._scrollToOnUpdate = true; + const { + $chatTreePanePs + } = megaChat; + if ($chatTreePanePs && $chatTreePanePs.length) { + const li = document.querySelector(`ul.conversations-pane li#conversation_${this.roomId}`); + if (li && !verge.inViewport(li, -72)) { + Object.values($chatTreePanePs).forEach(({ + ref + }) => { + if (ref.domNode) { + const wrapOuterHeight = $(ref.domNode).outerHeight(); + const itemOuterHeight = $('li:first', ref.domNode).outerHeight(); + const pos = li.offsetTop; + if (ref.domNode.contains(li)) { + ref.doProgramaticScroll == null || ref.doProgramaticScroll(Math.max(0, pos - wrapOuterHeight / 2 + itemOuterHeight), true); } } }); - this.interval = setInterval(() => { - if (!$.dialog) { - this._checkAndShowStep(); - } - }, 10000); - this.initListeners(); + this._scrollToOnUpdate = false; } } - initListeners() { - this.flagMap.addChangeListener((...args) => this.handleFlagChange(...args)); - this.megaChat.chatUIFlags.addChangeListener(SoonFc(200, () => { - if (this.megaChat.chatUIFlags.convPanelCollapse && $.dialog === 'onboardingDialog') { - closeDialog(); - } - this._checkAndShowStep(); - })); - this.megaChat.addChangeListener(() => { - const room = this.megaChat.getCurrentRoom(); - if (!room) { - return; - } - this.checkAndShowStep(); - }); - } - checkAndShowStep() { - this._checkAndShowStep(); - } - _shouldSkipShow() { - if (!M.chat || !mega.ui.onboarding || $.dialog || loadingDialog.active || u_type < 3 || is_mobile || $.msgDialog) { - return true; +}; +ChatRoom.prototype.isActive = function () { + return document.hasFocus() && this.isCurrentlyActive; +}; +ChatRoom.prototype.setActive = function () { + loadSubPage(this.getRoomUrl()); +}; +ChatRoom.prototype.isLoading = function () { + const mb = this.messagesBuff; + return mb.messagesHistoryIsLoading() || mb.isDecrypting; +}; +ChatRoom.prototype.getRoomUrl = function (getRawLink) { + const self = this; + if (self.type === "private") { + const participants = self.getParticipantsExceptMe(); + const contact = M.u[participants[0] || u_handle]; + if (contact) { + return `fm/chat/p/${ contact.u}`; } - this.$topRightMenu = this.$topRightMenu || $('.top-menu-popup', '#topmenu'); - if (!this.$topRightMenu.hasClass('o-hidden')) { - return true; + } else if (!getRawLink && is_chatlink && self.type === "public" && self.publicChatHandle && self.publicChatKey) { + return `chat/${ self.publicChatHandle }#${ self.publicChatKey}`; + } else if (self.type === "public") { + return `fm/chat/c/${ self.roomId}`; + } else if (self.type === "group" || self.type === "public") { + return `fm/chat/g/${ self.roomId}`; + } else { + throw new Error("Can't get room url for unknown room type."); + } +}; +ChatRoom.prototype.activateWindow = function () { + const self = this; + loadSubPage(self.getRoomUrl()); +}; +ChatRoom.prototype.hide = function () { + let _tmp; + if (d) { + this.logger.debug(' ---- hide', this.isCurrentlyActive); + } + this.isCurrentlyActive = false; + this.lastShownInUI = Date.now(); + if (this.megaChat.currentlyOpenedChat === this.roomId) { + this.megaChat.currentlyOpenedChat = null; + } + let tmp = this.megaChat.rootDOMNode.querySelector(`.conversation-panel[data-room-id="${this.chatId}"]`); + (_tmp = tmp) == null || _tmp.classList.add('hidden'); + if (tmp = document.getElementById(`conversation_${this.roomId}`)) { + tmp.classList.remove('active'); + } + this.trigger('onChatHidden', this.isCurrentlyActive); +}; +ChatRoom.prototype.appendMessage = function (message) { + const self = this; + if (message.deleted) { + return false; + } + if (self.shownMessages[message.messageId]) { + return false; + } + if (!message.orderValue) { + const mb = self.messagesBuff; + if (mb.messages.length > 0) { + const prevMsg = mb.messages.getItem(mb.messages.length - 1); + if (!prevMsg) { + self.logger.error("self.messages got out of sync...maybe there are some previous JS exceptions that caused that? note that messages may be displayed OUT OF ORDER in the UI."); + } else { + let nextVal = prevMsg.orderValue + 0.1; + if (!prevMsg.sent) { + const cid = megaChat.plugins.chatdIntegration.chatd.chatIdMessages[self.chatIdBin]; + if (cid && cid.highnum) { + nextVal = ++cid.highnum; + } + } + message.orderValue = nextVal; + } } - this.$topAccDropdown = this.$topAccDropdown || $('.js-dropdown-account', '#topmenu'); - if (this.$topAccDropdown.hasClass('show')) { - return true; + } + message.source = Message.SOURCE.SENT; + self.trigger('onMessageAppended', message); + self.messagesBuff.messages.push(message); + self.shownMessages[message.messageId] = true; +}; +ChatRoom.prototype.getNavElement = function () { + const self = this; + return $(`.nw-conversations-item[data-room-id="${ self.chatId }"]`); +}; +ChatRoom.prototype.sendMessage = function (message) { + const self = this; + const {megaChat} = this; + const messageId = megaChat.generateTempMessageId(self.roomId, message); + const msgObject = new Message(self, self.messagesBuff, { + messageId, + 'userId': u_handle, + message, + 'textContents': message, + 'delay': unixtime(), + 'sent': Message.STATE.NOT_SENT + }); + self.trigger('onSendMessage'); + self.appendMessage(msgObject); + return self._sendMessageToTransport(msgObject).then(internalId => { + if (!internalId) { + this.logger.warn(`Got unexpected(?) 'sendingnum'...`, internalId); } - this.$topNotifDropdown = this.$topNotifDropdown || $('.js-dropdown-notification', '#topmenu'); - if (this.$topNotifDropdown.hasClass('show')) { - return true; + msgObject.internalId = internalId; + msgObject.orderValue = internalId; + return internalId || -0xBADF; + }).catch(ex => { + this.logger.error(`sendMessage failed..`, msgObject, ex); + }); +}; +ChatRoom.prototype._sendMessageToTransport = function (messageObject) { + const self = this; + const {megaChat} = this; + megaChat.trigger('onPreBeforeSendMessage', messageObject); + megaChat.trigger('onBeforeSendMessage', messageObject); + megaChat.trigger('onPostBeforeSendMessage', messageObject); + return megaChat.plugins.chatdIntegration.sendMessage(self, messageObject); +}; +ChatRoom.prototype._sendNodes = async function (nodeids, users) { + const u = this.type === 'public' ? [strongvelope.COMMANDER] : users; + const promises = nodeids.map(nodeId => asyncApiReq({ + a: 'mcga', + n: [nodeId], + u, + id: this.chatId, + v: Chatd.VERSION + })); + const res = await Promise.allSettled(promises); + const sent = []; + for (let i = res.length; i--;) { + if (res[i].status === 'fulfilled') { + sent.push(nodeids[i]); } - this.$searchPanel = this.$searchPanel || $('.search-panel', '.conversationsApp'); - return this.$searchPanel.hasClass('expanded'); } - _checkAndShowStep() { - if (this._shouldSkipShow()) { - return; - } - const { - sections - } = mega.ui.onboarding; - if (!sections) { - return; + if (!sent.length) { + throw ENOENT; + } + return sent; +}; +ChatRoom.prototype.attachNodes = async function (nodes, names) { + if (!Array.isArray(nodes)) { + nodes = [nodes]; + } + const handles = new Set(); + for (let i = nodes.length; i--;) { + const n = nodes[i]; + const h = String(crypto_keyok(n) && n.h || n); + if (!M.getNodeByHandle(h)) { + handles.add(h); } - const { - chat: obChat - } = sections; - if (!obChat) { - return; + } + if (handles.size) { + await dbfetch.acquire([...handles]); + } + return this._attachNodes(nodes, names); +}; +ChatRoom.prototype._attachNodes = mutex('chatroom-attach-nodes', function _(resolve, reject, nodes, names) { + let i; + let step = 0; + const users = []; + const self = this; + let result = null; + let copy = Object.create(null); + let send = Object.create(null); + let link = Object.create(null); + let nmap = Object.create(null); + const members = self.getParticipantsExceptMe(); + const sendMessage = nodes => { + return new Promise(resolve => { + for (let i = nodes.length; i--;) { + const n = nmap[nodes[i]] || M.getNodeByHandle(nodes[i]); + console.assert(n.h, `Node not found... ${nodes[i]}`); + if (n.h) { + const name = names && (names[n.hash] || names[n.h]) || n.name; + this.sendMessage(Message.MANAGEMENT_MESSAGE_TYPES.MANAGEMENT + Message.MANAGEMENT_MESSAGE_TYPES.ATTACHMENT + JSON.stringify([{ + h: n.h, + k: n.k, + t: n.t, + s: n.s, + fa: n.fa, + ts: n.ts, + hash: n.hash, + name, + des: n.des + }])); + } + } + resolve(); + }); + }; + const attach = nodes => { + console.assert(this.type === 'public' || users.length || this.isNote, 'No users to send to?!'); + return this.isNote ? sendMessage(nodes) : this._sendNodes(nodes, users).then(res => sendMessage(res)); + }; + const done = function () { + if (--step < 1) { + nmap = null; + resolve(result); } - if (this.state[OBV4_FLAGS.CHAT]) { - return; + }; + const fail = function (ex) { + if (ex === EBLOCKED) { + result = ex; + } else if (ex === ENOENT) { + result = result || ex; + } else if (d) { + _.logger.error(ex); } - this.showDefaultNextStep(obChat); + done(); + }; + if (d && !_.logger) { + _.logger = new MegaLogger('attachNodes', {}, self.logger); } - showDefaultNextStep(obChat) { - const nextIdx = obChat.searchNextOpenStep(); - if (nextIdx !== false && (!this.$obDialog || !this.$obDialog.is(':visible')) && (this.obToggleDrawn || $('.conversations-category', '.conversationsApp').length)) { - this.obToggleDrawn = true; - if (obChat.steps && obChat.steps[nextIdx] && obChat.steps[nextIdx].isComplete) { - return; - } - obChat.startNextOpenSteps(nextIdx); - this.$obDialog = this.$obDialog || $('#ob-dialog'); + for (i = members.length; i--;) { + const usr = M.getUserByHandle(members[i]); + if (usr.u) { + users.push(usr.u); } } - handleFlagChange(...args) { - if (args.length >= 4 && typeof args[2] === 'string' && typeof args[3] === 'number' && this.state.hasOwnProperty(args[2])) { - if (d) { - console.debug(`Chat onboarding flag ${args[2]}: ${this.state[args[2]]} -> ${args[3]}`); - } - this.state[args[2]] = args[3]; - if (args[2] === OBV4_FLAGS.CHAT && args[3] === 1 && this.interval) { - clearInterval(this.interval); - delete this.interval; + for (i = nodes.length; i--;) { + const h = nodes[i]; + const n = crypto_keyok(h) ? h : M.getNodeByHandle(h); + if (n.t) { + link[n.h] = 1; + continue; + } + if (n.hash) { + nmap[n.hash] = n; + if (names && names[n.h]) { + names[n.hash] = names[n.h]; } } - } - destroy() { - if (this.interval) { - clearInterval(this.interval); - delete this.interval; + let op = send; + if (n.u !== u_handle || M.getNodeRoot(n.h) === 'shares') { + op = copy; } + op[n.h] = 1; + nmap[n.h] = n; } -}, (0,applyDecoratedDescriptor.A)(_class.prototype, "checkAndShowStep", [_dec], Object.getOwnPropertyDescriptor(_class.prototype, "checkAndShowStep"), _class.prototype), _class); - -// EXTERNAL MODULE: ./js/chat/ui/meetings/call.jsx + 11 modules -const call = REQ_(3); -;// ./js/chat/chat.jsx - - - -REQ_(623); -REQ_(553); -REQ_(269); - - - - -const EMOJI_DATASET_VERSION = 5; -const CHAT_ONHISTDECR_RECNT = "onHistoryDecrypted.recent"; -const LOAD_ORIGINALS = { - 'image/gif': 25e6, - 'image/png': 2e5, - 'image/webp': 2e5 -}; -const CHATUIFLAGS_MAPPING = { - 'convPanelCollapse': 'cPC' -}; -function Chat() { - const self = this; - this.is_initialized = false; - this.logger = MegaLogger.getLogger("chat"); - this.mbListeners = []; - this.chats = new MegaDataMap(); - this.scheduledMeetings = new MegaDataMap(); - this.chatUIFlags = new MegaDataMap(); - this.$chatTreePanePs = []; - this.initChatUIFlagsManagement(); - this.currentlyOpenedChat = null; - this.currentlyOpenedView = null; - this.lastOpenedChat = null; - this.archivedChatsCount = 0; - this.FORCE_EMAIL_LOADING = localStorage.fel; - this.WITH_SELF_NOTE = mega.flags.ff_n2s || localStorage.withSelfNote; - this._imageLoadCache = Object.create(null); - this._imagesToBeLoaded = Object.create(null); - this._imageAttributeCache = Object.create(null); - this._queuedMccPackets = []; - this._queuedMcsmPackets = {}; - this._queuedMessageUpdates = []; - this._queuedChatRoomEvents = Object.create(null); - this.handleToId = Object.create(null); - this.publicChatKeys = Object.create(null); - this.SOUNDS = { - ALERT: 'alert_info_message', - INCOMING_MSG: 'incoming_chat_message', - INCOMING_CALL: 'incoming_voice_video_call', - CALL_JOIN: 'user_join_call', - CALL_LEFT: 'user_left_call', - CALL_END: 'end_call', - CALL_JOIN_WAITING: 'user_join_waiting', - RECONNECT: 'reconnecting', - SPEAKER_TEST: 'test_speaker' - }; - this.options = { - 'delaySendMessageIfRoomNotAvailableTimeout': 3000, - 'plugins': { - 'chatdIntegration': ChatdIntegration, - 'callManager2': CallManager2, - 'urlFilter': UrlFilter, - 'emoticonShortcutsFilter': EmoticonShortcutsFilter, - 'emoticonsFilter': EmoticonsFilter, - 'callFeedback': CallFeedback, - 'presencedIntegration': PresencedIntegration, - 'persistedTypeArea': PersistedTypeArea, - 'btRtfFilter': BacktickRtfFilter, - 'rtfFilter': RtfFilter, - 'richpreviewsFilter': RichpreviewsFilter, - 'chatToastIntegration': ChatToastIntegration, - 'chatStats': ChatStats, - 'geoLocationLinks': GeoLocationLinks, - meetingsManager, - 'chatOnboarding': ChatOnboarding, - 'userHelper': ChatUserHelper - }, - 'chatNotificationOptions': { - 'textMessages': { - 'incoming-chat-message': { - title: l.notif_title_incoming_msg, - 'icon' (notificationObj) { - return notificationObj.options.icon; - }, - 'body' (notificationObj, params) { - if (params.type === 'private') { - return l.notif_body_incoming_msg.replace('%s', params.from); - } - return l.notif_body_incoming_msg_group.replace('%1', params.from).replace('%2', params.roomTitle); - } - }, - 'incoming-voice-video-call': { - 'title': l[17878] || "Incoming call", - 'icon' (notificationObj) { - return notificationObj.options.icon; - }, - 'body' (notificationObj, params) { - return l[5893].replace('[X]', params.from); - } - }, - 'screen-share-error': { - title: l.screenshare_failed_notif || 'You are no longer sharing your screen', - icon: notificationObj => { - return notificationObj.options.icon; - }, - body: '' - }, - 'upcoming-scheduled-occurrence': { - title: ({ - options - }) => { - return options.meeting.title; - }, - icon: `${staticpath}/images/mega/mega-icon.svg`, - body: l.notif_body_scheduled_upcoming - }, - 'starting-scheduled-occurrence': { - title: ({ - options - }) => { - return options.meeting.title; - }, - icon: `${staticpath}/images/mega/mega-icon.svg`, - body: l.notif_body_scheduled_starting - } - }, - sounds: Object.values(this.SOUNDS) - }, - 'chatStoreOptions': { - 'autoPurgeMaxMessagesPerRoom': 1024 - } - }; - this.SOUNDS.buffers = Object.create(null); - this.plugins = {}; - self.filePicker = null; - self._chatsAwaitingAps = {}; - MegaDataObject.call(this, { - "currentlyOpenedChat": null, - "activeCall": null, - 'routingSection': null, - 'routingSubSection': null, - 'routingParams': null - }); - this.routing = new ChatRouting(this); - Object.defineProperty(this, 'hasSupportForCalls', { - get () { - return typeof SfuClient !== 'undefined' && typeof TransformStream !== 'undefined' && window.RTCRtpSender && !!RTCRtpSender.prototype.createEncodedStreams; - } - }); - this.minuteClockInterval = setInterval(() => this._syncChats(), 6e4); - return this; -} -inherits(Chat, MegaDataObject); -Object.defineProperty(Chat, 'mcf', { - value: Object.create(null) -}); -Object.defineProperty(Chat, 'mcsm', { - value: Object.create(null) -}); -Chat.prototype.init = promisify(function (resolve, reject) { - const self = this; - if (self.is_initialized) { - self.destroy(); - } - if (d) { - console.time('megachat:plugins:init'); - } - self.plugins = Object.create(null); - self.plugins.chatNotifications = new ChatNotifications(self, self.options.chatNotificationOptions); - self.plugins.chatNotifications.notifications.rebind('onAfterNotificationCreated.megaChat', () => { - self.updateSectionUnreadCount(); - }); - Object.keys(self.options.plugins).forEach(plugin => { - self.plugins[plugin] = new self.options.plugins[plugin](self); - }); + copy = Object.keys(copy); + send = Object.keys(send); + link = Object.keys(link); if (d) { - console.timeEnd('megachat:plugins:init'); + _.logger.debug('copy:%d, send:%d, link:%d', copy.length, send.length, link.length, copy, send, link); } - $(document.body); - if (!is_chatlink) { - $(mega.ui.header.setStatus).rebind('mousedown.megachat', '.sub-menu.status button', function () { - const presence = $(this).data("presence"); - self._myPresence = presence; - const targetPresence = PresencedIntegration.cssClassToPresence(presence); - self.plugins.presencedIntegration.setPresence(targetPresence); - if (targetPresence !== UserPresence.PRESENCE.OFFLINE) { - Object.keys(self.plugins.chatdIntegration.chatd.shards).forEach(k => { - const v = self.plugins.chatdIntegration.chatd.shards[k]; - v.connectionRetryManager.requiresConnection(); - }); + if (link.length) { + ++step; + Promise.resolve(mega.fileRequestCommon.storage.isDropExist(link)).then(res => { + if (res.length) { + return mega.fileRequest.showRemoveWarning(res); } - }); + }).then(() => { + const createLink = h => M.createPublicLink(h).then(({ + link + }) => this.sendMessage(link)); + return Promise.all(link.map(createLink)); + }).then(done).catch(fail); } - self.$container = $('.fm-chat-block'); - if (M.chat && !is_chatlink) { - $('.activity-status-block, .activity-status').removeClass('hidden'); - $('.js-dropdown-account .status-dropdown').removeClass('hidden'); + if (send.length) { + step++; + attach(send).then(done).catch(fail); } - if (is_chatlink) { - const { - ph, - key - } = is_chatlink; - Chat.mcf[ph] = key; - this.publicChatKeys[ph] = key; + if (copy.length) { + step++; + this._copyNodesToAttach(copy, nmap).then(res => attach(res)).then(done).catch(fail); } - const promises = []; - const rooms = Object.keys(Chat.mcf); - for (let i = rooms.length; i--;) { - const roomId = rooms[i]; - const room = Chat.mcf[roomId]; - if (!this.publicChatKeys[rooms[i]]) { - promises.push(self.plugins.chatdIntegration.openChat(room, true)); + if (!step) { + if (d) { + _.logger.warn('Nothing to do here...'); } - delete Chat.mcf[rooms[i]]; + queueMicrotask(done); } - Promise.allSettled(promises).then(res => { - const pub = Object.keys(this.publicChatKeys); - return Promise.allSettled([res].concat(pub.map(pch => { - return this.plugins.chatdIntegration.openChat(pch, true); - }))); - }).then(res => { - res = res[0].value.concat(res.slice(1)); - this.logger.info('chats settled...', res); - if (is_mobile) { - return; - } - if (is_chatlink) { - const start = document.getElementById('startholder'); - this.flyoutStartHolder = start.querySelector('.flyout-holder') || mCreateElement('div', { - class: 'flyout-holder' - }, start); - } - const selector = is_chatlink ? '.chat-links-preview > .chat-app-container' : '.section.conversations'; - const rootDOMNode = this.rootDOMNode = document.querySelector(selector); - const $$root = this.$$root = (0,ReactDOM_.createRoot)(rootDOMNode); - $$root.render(REaCt().createElement(conversations.Ay.ConversationsApp, { - megaChat: this, - routingSection: this.routingSection, - routingSubSection: this.routingSubSection, - routingParams: this.routingParams - })); - this.onChatsHistoryReady().then(() => { - const room = this.getCurrentRoom(); - if (room) { - room.scrollToChat(); - } - return room; - }).dump('on-chat-history-loaded'); - this.is_initialized = true; - this.registerUploadListeners(); - this.trigger('onInit'); - mBroadcaster.sendMessage('chat_initialized'); - setInterval(this.removeMessagesByRetentionTime.bind(this, null), 2e4); - this.autoJoinIfNeeded(); - const scheduledMeetings = Object.values(Chat.mcsm); - if (scheduledMeetings && scheduledMeetings.length) { - for (let i = scheduledMeetings.length; i--;) { - const scheduledMeeting = scheduledMeetings[i]; - this.plugins.meetingsManager.attachMeeting(scheduledMeeting); - delete Chat.mcsm[scheduledMeeting.id]; - } - } - if (notify) { - notify.countAndShowNewNotifications(); - } - return true; - }).then(resolve).catch(reject); }); -Chat.prototype.showUpgradeDialog = function () { - return is_extension ? msgDialog('warningb', l[1900], l[8841]) : msgDialog('confirmation', l[1900], l[8840], '', cb => cb && location.reload()); -}; -Chat.prototype._syncChats = function () { - if (!this.is_initialized) { - return; - } - this.plugins.meetingsManager.checkForNotifications(); +ChatRoom.prototype._copyNodesToAttach = async function (copy, nmap) { const { - chats, - logger - } = this; - if (chats && chats.length) { - chats.forEach(({ - chatId, - scheduledMeeting - }) => { - const dnd = pushNotificationSettings.getDnd(chatId); - if (dnd && dnd < unixtime()) { - pushNotificationSettings.disableDnd(chatId); - if (logger) { - logger.debug(`Chat.prototype._syncDnd chatId=${chatId}`); - } - } - const { - isUpcoming, - chatRoom, - id - } = scheduledMeeting || {}; - if (isUpcoming) { - scheduledMeeting.setNextOccurrence(); - chatRoom.trackDataChange(); - if (logger) { - logger.debug(`Chat.prototype.__syncScheduledMeetings id=${id} chatId=${chatId}`); + h: target + } = await M.myChatFilesFolder.get(true); + if (!M.c[target]) { + await dbfetch.get(target); + } + const dir = Object.keys(M.c[target] || {}); + const rem = []; + for (let i = copy.length; i--;) { + const n = nmap[copy[i]] || M.getNodeByHandle(copy[i]); + console.assert(n.h, `Node not found.. ${copy[i]}`); + for (let y = dir.length; y--;) { + const b = M.getNodeByHandle(dir[y]); + if (n.h === b.h || b.hash === n.hash) { + if (d) { + this.logger.info('deduplication %s:%s', n.h, b.h, [n], [b]); } + rem.push(n.h); + copy.splice(i, 1); + break; } + } + } + let res = []; + if (copy.length) { + res = await M.copyNodes(copy, target, false, false, { + targetChatId: this.chatId }); + } else if (d) { + this.logger.info('No new nodes to copy.', rem); } -}; -Chat.prototype.loadChatUIFlagsFromConfig = function (val) { - let hadChanged = false; - let flags = val || mega.config.get("cUIF"); - if (flags) { - if (typeof flags !== 'object') { - flags = {}; + assert(Array.isArray(res), `Unexpected response, ${res && res.message || res}`, res); + const [h] = res; + res = [...rem, ...res]; + assert(res.length, 'Unexpected condition... nothing to attach ?!'); + for (let i = res.length; i--;) { + const n = nmap[res[i]] || M.getNodeByHandle(res[i]); + if (n.fv) { + if (d) { + this.logger.info('Skipping file-version %s', n.h, n); + } + res.splice(i, 1); } - Object.keys(CHATUIFLAGS_MAPPING).forEach(k => { - const v = flags[CHATUIFLAGS_MAPPING[k]]; - hadChanged = v !== undefined && this.chatUIFlags.set(k, v) !== false || hadChanged; - }); } - return hadChanged; + if (h && !res.length) { + if (d) { + this.logger.info('Adding nothing but a file-version?..', h); + } + res = [h]; + } + return res; }; -Chat.prototype.cleanup = function (clean) { - const room = this.getCurrentRoom(); - if (room) { - room.hide(); +ChatRoom.prototype.onUploadStart = function (data) { + const self = this; + if (d) { + self.logger.debug('onUploadStart', data); } - M.chat = false; - this.routingParams = null; - this.routingSection = null; - this.routingSubSection = null; - if (clean) { - M.currentdirid = page = false; +}; +ChatRoom.prototype.uploadFromComputer = function () { + this.scrolledToBottom = true; + $('#fileselect1').trigger('click'); +}; +ChatRoom.prototype.attachContacts = function (ids) { + for (let i = 0; i < ids.length; i++) { + const nodeId = ids[i]; + const node = M.u[nodeId]; + this.sendMessage(Message.MANAGEMENT_MESSAGE_TYPES.MANAGEMENT + Message.MANAGEMENT_MESSAGE_TYPES.CONTACT + JSON.stringify([{ + u: node.u, + email: node.m, + name: node.name || node.m + }])); } }; -Chat.prototype.initChatUIFlagsManagement = function () { +ChatRoom.prototype.getMessageById = function (messageId) { const self = this; - self.loadChatUIFlagsFromConfig(); - this.chatUIFlags.addChangeListener((hashmap, extraArg) => { - const flags = mega.config.get("cUIF") || {}; - let hadChanged = false; - let hadLocalChanged = false; - Object.keys(CHATUIFLAGS_MAPPING).forEach((k) => { - if (flags[CHATUIFLAGS_MAPPING[k]] !== self.chatUIFlags[k]) { - if (extraArg === 0xDEAD) { - self.chatUIFlags._data[k] = flags[CHATUIFLAGS_MAPPING[k]]; - hadLocalChanged = true; - } else { - flags[CHATUIFLAGS_MAPPING[k]] = self.chatUIFlags[k]; - hadChanged = true; - } - } - }); - if (hadLocalChanged) { - if (extraArg !== 0xDEAD) { - self.chatUIFlags.trackDataChange(0xDEAD); - } - $.tresizer(); - } - if (extraArg === 0xDEAD) { - return; - } - if (hadChanged) { - mega.config.set("cUIF", flags); - } - }); - this.mbListeners.push(mBroadcaster.addListener('fmconfig:cUIF', tryCatch(v => { - if (self.loadChatUIFlagsFromConfig(v)) { - self.chatUIFlags.trackDataChange(0xDEAD); + const msgs = self.messagesBuff.messages; + const msgKeys = msgs.keys(); + for (let i = 0; i < msgKeys.length; i++) { + const k = msgKeys[i]; + const v = msgs[k]; + if (v && v.messageId === messageId) { + return v; } - })), mBroadcaster.addListener('statechange', state => { - this.trigger('viewstateChange', state); - })); + } + return false; }; -Chat.prototype.unregisterUploadListeners = function (destroy) { - 'use strict'; - +ChatRoom.prototype.hasMessages = function (userMessagesOnly = false) { + return this.messagesBuff.messages.some(m => !userMessagesOnly || m.messageHtml); +}; +ChatRoom.prototype.renderContactTree = function () { const self = this; - mBroadcaster.removeListener(self._uplDone); - mBroadcaster.removeListener(self._uplError); - mBroadcaster.removeListener(self._uplAbort); - mBroadcaster.removeListener(self._uplFAError); - mBroadcaster.removeListener(self._uplFAReady); - if (destroy) { - mBroadcaster.removeListener(self._uplStart); + const $navElement = self.getNavElement(); + const $count = $('.nw-conversations-unread', $navElement); + const count = self.messagesBuff.getUnreadCount(); + if (count > 0) { + $count.text(count > 9 ? "9+" : count); + $navElement.addClass("unread"); + } else if (count === 0) { + $count.text(""); + $navElement.removeClass("unread"); } - delete self._uplError; + $navElement.data('chatroom', self); }; -Chat.prototype.registerUploadListeners = function () { +ChatRoom.prototype.getUnreadCount = function () { const self = this; - const logger = d && MegaLogger.getLogger('chatUploadListener', false, self.logger); - const ufo = Object.create(null); - self.unregisterUploadListeners(true); - const forEachChat = function (chats, callback) { - let result = 0; - if (!Array.isArray(chats)) { - chats = [chats]; + return self.messagesBuff.getUnreadCount(); +}; +ChatRoom.prototype.recover = function () { + const self = this; + self.callRequest = null; + if (self.state !== ChatRoom.STATE.LEFT) { + self.membersLoaded = false; + self.setState(ChatRoom.STATE.JOINING, true); + self.megaChat.trigger("onRoomCreated", [self]); + return MegaPromise.resolve(); + } else { + return MegaPromise.reject(); + } +}; +ChatRoom.prototype.showMissingUnifiedKeyDialog = function () { + return msgDialog(`warningb:!^${l.msg_dlg_cancel}!${l[23433]}`, null, l[200], l.chat_key_failed_dlg_text, reload => reload ? M.reload() : null, 1); +}; +ChatRoom.prototype.hasInvalidKeys = function () { + if (!is_chatlink && this.type === 'public') { + const { + unifiedKey + } = this.protocolHandler || {}; + if (!unifiedKey || unifiedKey && unifiedKey.length !== 16 || !this.ck || this.ck && this.ck.length !== 32) { + console.error('Error instantiating room/call -- missing `unifiedKey`/malformed `ck` for public chat.'); + const { + owner, + actors + } = mBroadcaster.crossTab; + eventlog(99751, JSON.stringify([1, buildVersion.website || 'dev', String(this.chatId).length | 0, this.type | 0, this.isMeeting | 0, typeof unifiedKey, String(unifiedKey || '').length | 0, typeof this.ck, String(this.ck).length | 0, !!owner | 0, Object(actors).length | 0])); + return true; } - for (let i = chats.length; i--;) { - const room = self.getRoomFromUrlHash(chats[i]); - if (room) { - callback(room, ++result); - } - } - return result; + } + return false; +}; +ChatRoom.prototype.joinCall = ChatRoom._fnRequireParticipantKeys(function (audio, video, callId) { + if (!megaChat.hasSupportForCalls || this.activeCallIds.length === 0 || this.meetingsLoading) { + return; + } + if (this.hasInvalidKeys()) { + return this.showMissingUnifiedKeyDialog(); + } + this.meetingsLoading = { + title: l.joining, + audio, + video }; - const lookupPendingUpload = function (id) { - console.assert((id | 0) > 0 || String(id).length === 8, 'Invalid lookupPendingUpload arguments...'); - for (const uid in ulmanager.ulEventData) { - if (ulmanager.ulEventData[uid].faid === id || ulmanager.ulEventData[uid].h === id) { - return uid; - } - } + callId = callId || this.activeCallIds.keys()[0]; + return asyncApiReq({ + 'a': 'mcmj', + 'cid': this.chatId, + "mid": callId + }).then(r => { + this.startOrJoinCall(callId, r.url, audio, video, r.organiser); + }); +}); +ChatRoom.prototype.startOrJoinCall = function (callId, url, audio, video, organiser) { + tryCatch(() => { + const call = this.call = megaChat.activeCall = megaChat.plugins.callManager2.createCall(this, callId, this.protocolHandler.chatMode === strongvelope.CHAT_MODE.PUBLIC && str_to_ab(this.protocolHandler.unifiedKey)); + call.setOrganiser(organiser); + return call.connect(url, audio, video); + }, ex => { + let _this$call; + (_this$call = this.call) == null || _this$call.destroy(); + this.call = megaChat.activeCall = null; + this.meetingsLoading = false; + console.error('Failed to start/join call:', ex); + })(); +}; +ChatRoom.prototype.rejectCall = function (callId) { + if (this.activeCallIds.length === 0) { + return; + } + callId = callId || this.activeCallIds.keys()[0]; + if (this.type === "private") { + return asyncApiReq({ + 'a': 'mcme', + 'cid': this.chatId, + 'mid': callId + }); + } + const shard = this.chatd.shards[this.chatShard]; + if (shard) { + shard.sendCallReject(base64urldecode(this.chatId), base64urldecode(callId)); + } + return Promise.resolve(); +}; +ChatRoom.prototype.ringUser = function (userId, callId, callstate) { + assert(userId, 'Missing user handle.'); + assert(callId, 'Missing chat handle.'); + assert(this.type !== 'private', 'Unexpected chat type.'); + const shard = this.chatd.shards[this.chatShard]; + if (shard) { + api.req({ + a: 'mcru', + u: userId, + cid: this.chatId + }).then(() => shard.ringUser(this.chatIdBin, base64urldecode(userId), base64urldecode(callId), callstate)).catch(dump); + } +}; +ChatRoom.prototype.endCallForAll = function (callId) { + if (this.activeCallIds.length && this.type !== 'private') { + callId = callId || this.activeCallIds.keys()[0]; + asyncApiReq({ + 'a': 'mcme', + 'cid': this.chatId, + 'mid': callId + }); + eventlog(99761, JSON.stringify([this.chatId, callId, this.isMeeting | 0])); + } +}; +ChatRoom.prototype.startAudioCall = function (scheduled) { + return this.startCall(true, false, scheduled); +}; +ChatRoom.prototype.startVideoCall = function (scheduled) { + return this.startCall(true, true, scheduled); +}; +ChatRoom.prototype.startCall = ChatRoom._fnRequireParticipantKeys(function (audio, video, scheduled) { + if (!megaChat.hasSupportForCalls || this.meetingsLoading) { + return; + } + if (this.activeCallIds.length > 0) { + this.joinCall(this.activeCallIds.keys()[0]); + return; + } + if (this.hasInvalidKeys()) { + return this.showMissingUnifiedKeyDialog(); + } + this.meetingsLoading = { + title: l.starting, + audio, + video }; - const unregisterListeners = function () { - if (!$.len(ulmanager.ulEventData)) { - self.unregisterUploadListeners(); - if (d) { - logger.warn('Revoked upload listeners.'); - } - } + const opts = { + a: 'mcms', + cid: this.chatId, + sm: scheduled && this.scheduledMeeting && this.scheduledMeeting.id }; - const onUploadComplete = function (ul) { - if (ulmanager.ulEventData[ul && ul.uid]) { - forEachChat(ul.chat, (room) => { - if (d) { - logger.debug('Attaching node[%s] to chat room[%s]...', ul.h, room.chatId, ul.uid, ul, M.d[ul.h]); - } - room.attachNodes([ul.h]).catch(dump); - }); - delete ulmanager.ulEventData[ul.uid]; - unregisterListeners(); - } else if (d) { - logger.warn('could not complete upload...', ul); + if (localStorage.sfuId) { + opts.sfu = parseInt(localStorage.sfuId, 10); + } + return asyncApiReq(opts).then(r => { + this.startOrJoinCall(r.callId, r.sfu, audio, video, r.organiser); + }).catch(ex => { + this.meetingsLoading = false; + this.logger.error(`Failed to start call: ${ex}`); + }); +}); +ChatRoom.prototype.subscribeForCallEvents = function () { + const callMgr = megaChat.plugins.callManager2; + this.rebind("onChatdPeerJoinedCall.callManager", (e, data) => { + if (!this.activeCallIds.exists(data.callId)) { + this.activeCallIds.set(data.callId, []); } - }; - const onUploadCompletion = function (uid, handle, faid, chat) { - if (!chat) { - if (d > 1) { - logger.debug('ignoring upload:completion that is unrelated to chat.', arguments); + this.activeCallIds.set(data.callId, [...this.activeCallIds[data.callId], ...data.participants]); + const parts = data.participants; + for (let i = 0; i < parts.length; i++) { + if (this.type === "private" || parts[i] === u_handle && this.ringingCalls.exists(data.callId)) { + this.ringingCalls.remove(data.callId); + callMgr.trigger("onRingingStopped", { + callId: data.callId, + chatRoom: this + }); } + } + megaChat.updateSectionUnreadCount(); + this.callParticipantsUpdated(); + }); + this.rebind("onChatdPeerLeftCall.callManager", (e, data) => { + if (!this.activeCallIds[data.callId]) { return; } - const n = M.getNodeByHandle(handle); - const ul = ulmanager.ulEventData[uid] || false; - if (d) { - logger.info('upload:completion', uid, handle, faid, ul, n); + const parts = data.participants; + for (let i = 0; i < parts.length; i++) { + array.remove(this.activeCallIds[data.callId], parts[i], true); + if (parts[i] === u_handle && this.ringingCalls.exists(data.callId)) { + this.ringingCalls.remove(data.callId); + callMgr.trigger("onRingingStopped", { + callId: data.callId, + chatRoom: this + }); + } } - if (!ul) { + this.callParticipantsUpdated(); + }); + this.rebind("onCallLeft.callManager", (e, data) => { + console.warn("onCallLeft:", JSON.stringify(data)); + const { + call + } = this; + if (!call || call.callId !== data.callId) { if (d) { - logger.error('Upload event data store missing...', uid, n, ul); - } - } else { - ul.h = handle; - if (ul.efa && !n) { - if (d) { - logger.error('Invalid state, efa set on deduplication?', ul.efa, ul); - } - ul.efa = 0; - } else if (ufo[faid]) { - if (d) { - logger.info(`Recovering fa:error state for ${faid}`, ufo[faid], ul.efa); - } - ul.efa = Math.max(0, ul.efa - ufo[faid]) | 0; - } - if (ul.efa && (!n.fa || String(n.fa).split('/').length < ul.efa)) { - console.assert(!ul.faid || ul.faid === faid, `${faid} != ${ul.faid}`); - ul.faid = faid; - if (d) { - logger.info('Waiting for file attribute to arrive.', handle, ul.efa, n.fa, n.name, ul, [n]); - } - } else { - onUploadComplete(ul); + console.warn("... no active call or event not for it"); } + return; } - }; - const onUploadError = function (uid, error) { - const ul = ulmanager.ulEventData[uid]; + this.meetingsLoading = false; + call.hangUp(data.reason); + megaChat.activeCall = this.call = null; + }); + this.rebind("onChatdCallEnd.callManager", (e, data) => { if (d) { - logger.debug(error === -0xDEADBEEF ? 'upload:abort' : 'upload.error', uid, error, [ul]); + console.warn("onChatdCallEnd:", JSON.stringify(data)); } - if (ul) { - delete ulmanager.ulEventData[uid]; - unregisterListeners(); - } else if (d) { - logger.warn(`No upload association for #${uid}`, error); + this.meetingsLoading = false; + this.activeCallIds.remove(data.callId); + if (this.callUserLimited) { + this.callUserLimited.abort(); } - }; - const onAttributeReady = function (handle, fa) { - delay(`chat:fa-ready:${ handle}`, () => { - const uid = lookupPendingUpload(handle); - const ul = ulmanager.ulEventData[uid] || false; - if (d) { - logger.debug('fa:ready', handle, fa, ul.efa, uid, ul); - } - if (ul.h && String(fa).split('/').length >= ul.efa) { - onUploadComplete(ul); - } else if (d) { - logger.debug('Not enough file attributes yet, holding...', handle, fa, ul.efa, ul); - } - }); - }; - const onAttributeError = function (faid, error, onStorageAPIError, nFAiled) { - const uid = lookupPendingUpload(faid); - const ul = ulmanager.ulEventData[uid] || false; - if (d) { - logger.debug('fa:error', faid, error, onStorageAPIError, uid, ul, nFAiled, ul.efa); + this.callUserLimited = false; + this.stopRinging(data.callId); + this.callParticipantsUpdated(); + megaChat.updateSectionUnreadCount(); + }); + this.rebind('onCallState.callManager', function (e, data) { + const ac = this.activeCallIds[data.callId]; + console.assert(ac, `unknown call: ${data.callId}`); + if (ac) { + callMgr.onCallState(data, this); + this.callParticipantsUpdated(); } - if (ul) { - ul.efa = Math.max(0, ul.efa - nFAiled) | 0; - if (ul.h) { - const n = M.getNodeByHandle(ul.h); - if (!ul.efa || n.fa && String(n.fa).split('/').length >= ul.efa) { - onUploadComplete(ul); - } - } - } else { - if (d) { - logger.warn(`No upload association for ${faid} (yet?)`, nFAiled, error); - } - ufo[faid] = (ufo[faid] | 0) + nFAiled; + }); + this.rebind('onRoomDisconnected.callManager', function () { + this.activeCallIds.clear(); + megaChat.updateSectionUnreadCount(); + if (navigator.onLine) { + return; } - }; - const registerLocalListeners = function () { - self._uplError = mBroadcaster.addListener('upload:error', onUploadError); - self._uplAbort = mBroadcaster.addListener('upload:abort', onUploadError); - self._uplFAReady = mBroadcaster.addListener('fa:ready', onAttributeReady); - self._uplFAError = mBroadcaster.addListener('fa:error', onAttributeError); - self._uplDone = mBroadcaster.addListener('upload:completion', onUploadCompletion); - }; - self._uplStart = mBroadcaster.addListener('upload:start', (data) => { - if (d) { - logger.info('onUploadStart', [data]); + if (this.call) { + this.trigger('ChatDisconnected', this); } - const notify = function (room) { - room.onUploadStart(data); - }; - for (const k in data) { - const chats = data[k].chat; - if (chats && forEachChat(chats, notify) && !self._uplError) { - registerLocalListeners(); - } + this.callParticipantsUpdated(); + }); + this.rebind('onStateChange.callManager', function (e, oldState, newState) { + if (newState === ChatRoom.STATE.LEFT && this.call) { + this.call.hangUp(SfuClient.TermCode.kLeavingRoom); } }); -}; -Chat.prototype.getRoomFromUrlHash = function (urlHash) { - if (urlHash.indexOf("#") === 0) { - urlHash = urlHash.subtr(1, urlHash.length); - } - if (urlHash.indexOf("chat/g/") > -1 || urlHash.indexOf("chat/c/") > -1) { - var foundRoom = null; - urlHash = urlHash.replace("chat/g/", "").replace("chat/c/", ""); - megaChat.chats.forEach((room) => { - if (!foundRoom && room.chatId === urlHash) { - foundRoom = room; - } - }); - return foundRoom; - } else if (urlHash.indexOf("chat/p/") > -1) { - const contactHash = urlHash.replace("chat/p/", ""); - if (!contactHash) { + this.rebind('onCallPeerLeft.callManager', (e, data) => { + const { + call + } = this; + if (!call || call.isDestroyed || call.hasOtherParticipant() || SfuClient.isTermCodeRetriable(data.reason)) { return; } - const chatRoom = this.getPrivateRoom(contactHash); - return chatRoom; - } else if (urlHash.indexOf("chat/") > -1 && urlHash[13] === "#") { - var foundRoom = null; - const pubHandle = urlHash.replace("chat/", "").split("#")[0]; - urlHash = urlHash.replace("chat/g/", ""); - const chatIds = megaChat.chats.keys(); - for (let i = 0; i < chatIds.length; i++) { - const cid = chatIds[i]; - const room = megaChat.chats[cid]; - if (room.publicChatHandle === pubHandle) { - foundRoom = room; - break; - } - } - return foundRoom; - } else { - return null; - } -}; -Chat.prototype.updateSectionUnreadCount = SoonFc(function () { - if (!this.is_initialized) { - return; - } - let unreadCount = 0; - const notificationsCount = { - unreadChats: 0, - unreadMeetings: 0, - unreadUpcoming: 0, - chatsCall: false, - meetingCall: false - }; - let havePendingCall = false; - let haveAdHocMessage = false; - this.chats.forEach(chatRoom => { - if (chatRoom.isArchived() || chatRoom.state === ChatRoom.STATE.LEFT) { - return; + if (this.type === 'private') { + return this.trigger('onCallLeft', { + callId: call.callId + }); } - const unreads = parseInt(chatRoom.messagesBuff.getUnreadCount(), 10); - unreadCount += unreads; - if (unreads) { - notificationsCount[chatRoom.isMeeting ? 'unreadMeetings' : 'unreadChats'] += unreads; - if (chatRoom.scheduledMeeting && chatRoom.scheduledMeeting.isUpcoming) { - notificationsCount.unreadUpcoming += unreads; + setTimeout(() => { + if (this.call === call) { + this.call.initCallTimeout(); } - } - if (chatRoom.havePendingCall() && chatRoom.uniqueCallParts && !chatRoom.uniqueCallParts[u_handle]) { - havePendingCall = true; - if (chatRoom.isMeeting) { - notificationsCount.meetingCall = true; - if (!chatRoom.scheduledMeeting && unreads) { - haveAdHocMessage = true; - } - } else { - notificationsCount.chatsCall = true; + }, 3000); + }); + this.rebind('onMeAdded', (e, addedBy) => { + if (this.activeCallIds.length > 0) { + const callId = this.activeCallIds.keys()[0]; + if (this.ringingCalls.exists(callId)) { + return; } + this.ringingCalls.set(callId, addedBy); + this.megaChat.trigger('onIncomingCall', [this, callId, addedBy, callMgr]); + this.fakedLocalRing = true; + setTimeout(() => { + delete this.fakedLocalRing; + if (this.ringingCalls.exists(callId)) { + callMgr.trigger("onRingingStopped", { + callId, + chatRoom: this + }); + } + }, 30e3); } }); - unreadCount = unreadCount > 9 ? "9+" : unreadCount; - if (!is_chatlink && mega.ui.header) { - if (notificationsCount.unreadChats || notificationsCount.unreadUpcoming || haveAdHocMessage) { - mega.ui.header.chatsButton.addClass('decorated'); - } else { - mega.ui.header.chatsButton.removeClass('decorated'); - } - const phoneIcon = mega.ui.header.domNode.querySelector('.top-chats-call'); - if (phoneIcon) { - phoneIcon.classList[havePendingCall ? 'remove' : 'add']('hidden'); +}; +ChatRoom.prototype.stateIsLeftOrLeaving = function () { + return this.state == ChatRoom.STATE.LEFT || this.state == ChatRoom.STATE.LEAVING || (!is_chatlink && this.state === ChatRoom.STATE.READY && this.membersSetFromApi && !this.membersSetFromApi.members.hasOwnProperty(u_handle) || is_chatlink && !this.members.hasOwnProperty(u_handle)); +}; +ChatRoom.prototype._clearChatMessagesFromChatd = function () { + this.chatd.shards[this.chatShard].retention(base64urldecode(this.chatId), 1); +}; +ChatRoom.prototype.isReadOnly = function () { + if (this.type === "private") { + const members = this.getParticipantsExceptMe(); + if (members[0] && !Object(M.u[members[0]]).c) { + return true; } } - if (this._lastUnreadCount !== unreadCount) { - this._lastUnreadCount = unreadCount; - notify.updateNotificationIndicator(); - } - if (!this._lastNotifications || !shallowEqual(this._lastNotifications, notificationsCount)) { - this._lastNotifications = notificationsCount; - megaChat.trigger('onUnreadCountUpdate', notificationsCount); + return this.members && this.members[u_handle] <= 0 || !this.members.hasOwnProperty(u_handle) || this.privateReadOnlyChat || this.state === ChatRoom.STATE.LEAVING || this.state === ChatRoom.STATE.LEFT; +}; +ChatRoom.prototype.iAmOperator = function () { + return this.type === 'private' || this.members && this.members[u_handle] === ChatRoom.MembersSet.PRIVILEGE_STATE.OPERATOR; +}; +ChatRoom.prototype.iAmReadOnly = function () { + return this.type !== 'private' && this.members && this.members[u_handle] === ChatRoom.MembersSet.PRIVILEGE_STATE.READONLY; +}; +ChatRoom.prototype.iAmWaitingRoomPeer = function () { + return this.options.w && !this.iAmOperator(); +}; +ChatRoom.prototype.didInteraction = function (user_handle, ts) { + const self = this; + const newTs = ts || unixtime(); + if (user_handle === u_handle) { + Object.keys(self.members).forEach((user_handle) => { + const contact = M.u[user_handle]; + if (contact && user_handle !== u_handle && contact.c === 1) { + setLastInteractionWith(contact.u, `1:${ newTs}`); + } + }); + } else { + const contact = M.u[user_handle]; + if (contact && user_handle !== u_handle && contact.c === 1) { + setLastInteractionWith(contact.u, `1:${ newTs}`); + } } -}, 100); -Chat.prototype.dropAllDatabases = promisify(function (resolve, reject) { - const chatd = this.plugins.chatdIntegration.chatd || false; - const promises = []; - if (chatd.chatdPersist) { - promises.push(chatd.chatdPersist.drop()); +}; +ChatRoom.prototype.retrieveAllHistory = function () { + const self = this; + self.messagesBuff.retrieveChatHistory().done(() => { + if (self.messagesBuff.haveMoreHistory()) { + self.retrieveAllHistory(); + } + }); +}; +ChatRoom.prototype.seedRoomKeys = async function (keys) { + assert(Array.isArray(keys) && keys.length, `Invalid keys parameter for seedRoomKeys.`, keys); + if (d > 2) { + this.logger.warn('Seeding room keys...', keys); } - if ('messagesQueueKvStorage' in chatd) { - promises.push(chatd.messagesQueueKvStorage.destroy()); + const promises = [ChatdIntegration._ensureKeysAreLoaded(keys, undefined, this.publicChatHandle)]; + if (!this.protocolHandler) { + promises.push(ChatdIntegration._waitForProtocolHandler(this)); } - if (Reactions.ready) { - promises.push(Reactions._db.destroy()); + if (!this.notDecryptedKeys) { + this.notDecryptedKeys = Object.create(null); } - if (PersistedTypeArea.ready) { - promises.push(PersistedTypeArea._db.destroy()); + for (let i = keys.length; i--;) { + const { + key, + keyid, + keylen, + userId + } = keys[i]; + this.notDecryptedKeys[`${userId}-${keyid}`] = { + userId, + keyid, + keylen, + key + }; } - Promise.allSettled(promises).then(resolve).catch(reject); -}); -Chat.prototype.destroy = function (isLogout) { - if (!this.is_initialized) { - return; - } - this.isLoggingOut = isLogout; - for (let i = 0; i < this.mbListeners.length; i++) { - mBroadcaster.removeListener(this.mbListeners[i]); - } - this.unregisterUploadListeners(true); - this.trigger('onDestroy', [isLogout]); - tryCatch(() => this.$$root.unmount())(); - this.chats.forEach((chatRoom, chatId) => { - if (!isLogout) { - chatRoom.destroy(false, true); + const promise = this._keysAreSeeding = Promise.all(promises).then(() => { + const res = this.protocolHandler.seedKeys(keys); + for (let i = res.length; i--;) { + delete this.notDecryptedKeys[res[i]]; + } + return res; + }).catch(ex => { + this.logger.error('Failed to seed room keys!', ex, keys); + throw ex; + }).finally(() => { + if (promise === this._keysAreSeeding) { + delete this._keysAreSeeding; } - this.chats.remove(chatId); }); - this.is_initialized = false; - if (this.plugins.chatdIntegration && this.plugins.chatdIntegration.chatd && this.plugins.chatdIntegration.chatd.shards) { - const { - shards - } = this.plugins.chatdIntegration.chatd; - Object.keys(shards).forEach(shard => shards[shard].connectionRetryManager.options.functions.forceDisconnect()); - } - for (const pluginName in this.plugins) { - const plugin = this.plugins[pluginName]; - if (plugin.destroy) { - plugin.destroy(); + return promise; +}; +ChatRoom.prototype.truncate = function () { + const self = this; + const chatMessages = self.messagesBuff.messages; + if (chatMessages.length > 0) { + let lastChatMessageId = null; + let i = chatMessages.length - 1; + while (lastChatMessageId == null && i >= 0) { + const message = chatMessages.getItem(i); + if (message instanceof Message && message.dialogType !== "truncated") { + lastChatMessageId = message.messageId; + } + i--; + } + if (lastChatMessageId) { + asyncApiReq({ + a: 'mct', + id: self.chatId, + m: lastChatMessageId, + v: Chatd.VERSION + }).catch(ex => { + if (ex === -2) { + msgDialog('warninga', l[135], l[8880]); + } + }); } - } - if (this.minuteClockInterval) { - clearInterval(this.minuteClockInterval); - } - if (megaChat.flyoutStartHolder && mega.ui.flyout) { - mega.ui.flyout.reinit(megaChat.flyoutStartHolder); - delete megaChat.flyoutStartHolder; } }; -Chat.prototype.getContacts = function () { - const results = []; - M.u.forEach((k, v) => { - if (v.c == 1 || v.c == 2) { - results.push(v); - } +ChatRoom.prototype.getActiveCalls = function () { + return this.activeCallIds.map((parts, id) => { + return parts.indexOf(u_handle) > -1 ? id : undefined; }); - return results; }; -Chat.prototype.userPresenceToCssClass = function (presence) { - if (presence === UserPresence.PRESENCE.ONLINE) { - return 'online'; - } else if (presence === UserPresence.PRESENCE.AWAY) { - return 'away'; - } else if (presence === UserPresence.PRESENCE.DND) { - return 'busy'; - } else if (presence === UserPresence.PRESENCE.OFFLINE) { - return 'offline'; - } else { - return 'black'; +ChatRoom.prototype.haveActiveCall = function () { + return this.getActiveCalls().length > 0; +}; +ChatRoom.prototype.haveActiveOnHoldCall = function () { + const activeCallIds = this.getActiveCalls(); + for (let i = 0; i < activeCallIds.length; i++) { + const call = megaChat.plugins.callManager2.calls[`${this.chatId }_${ activeCallIds[i]}`]; + if (call && call.av & SfuClient.Av.onHold) { + return true; + } + } + return false; +}; +ChatRoom.prototype.havePendingGroupCall = function () { + if (this.type !== "group" && this.type !== "public") { + return false; } + return this.activeCallIds.length > 0; }; -Chat.prototype._renderMyStatus = function () { +ChatRoom.prototype.havePendingCall = function () { + return this.activeCallIds.length > 0; +}; +ChatRoom.prototype.getActiveCallMessageId = function (ignoreActive) { const self = this; - if (!self.is_initialized) { - return; + if (!ignoreActive && !self.havePendingCall() && !self.haveActiveCall()) { + return false; } - if (typeof megaChat.userPresence === 'undefined') { - return; + const msgs = self.messagesBuff.messages; + for (let i = msgs.length - 1; i >= 0; i--) { + const msg = msgs.getItem(i); + if (msg.dialogType === "remoteCallEnded") { + return false; + } + if (msg.dialogType === "remoteCallStarted") { + return msg.messageId; + } } - const $status = $('.activity-status-block .activity-status', 'body'); - $('.top-user-status-popup .dropdown-item').removeClass("active"); - $status.removeClass('online').removeClass('away').removeClass('busy').removeClass('offline').removeClass('black'); - const actualPresence = self.plugins.presencedIntegration.getMyPresenceSetting(); - const userPresenceConRetMan = megaChat.userPresence.connectionRetryManager; - const presence = self.plugins.presencedIntegration.getMyPresence(); - let cssClass = PresencedIntegration.presenceToCssClass(presence); - if (userPresenceConRetMan.getConnectionState() !== ConnectionRetryManager.CONNECTION_STATE.CONNECTED) { - cssClass = "offline"; +}; +ChatRoom.prototype.stopRinging = function (callId) { + if (this.ringingCalls.exists(callId)) { + this.ringingCalls.remove(callId); } - const $activityStatus = $('.activity-text', '.js-topbar'); - if (actualPresence === UserPresence.PRESENCE.ONLINE) { - $('.top-user-status-popup .dropdown-item[data-presence="chat"]').addClass("active"); - $activityStatus.text(l[5923]); - } else if (actualPresence === UserPresence.PRESENCE.AWAY) { - $('.top-user-status-popup .dropdown-item[data-presence="away"]').addClass("active"); - $activityStatus.text(l[5924]); - } else if (actualPresence === UserPresence.PRESENCE.DND) { - $('.top-user-status-popup .dropdown-item[data-presence="dnd"]').addClass("active"); - $activityStatus.text(l[5925]); - } else if (actualPresence === UserPresence.PRESENCE.OFFLINE) { - $('.top-user-status-popup .dropdown-item[data-presence="unavailable"]').addClass("active"); - $activityStatus.text(l[5926]); - } else { - $('.top-user-status-popup .dropdown-item[data-presence="unavailable"]').addClass("active"); - $activityStatus.text(l[5926]); + megaChat.plugins.callManager2.trigger("onRingingStopped", { + callId, + chatRoom: this + }); +}; +ChatRoom.prototype.callParticipantsUpdated = function () { + const self = this; + let msgId = self.getActiveCallMessageId(); + if (!msgId) { + msgId = self.getActiveCallMessageId(true); } - $status.addClass(cssClass); - if (userPresenceConRetMan.getConnectionState() === ConnectionRetryManager.CONNECTION_STATE.CONNECTING) { - $status.parent().addClass("fadeinout"); - } else { - $status.parent().removeClass("fadeinout"); + const callParts = self.getCallParticipants() || []; + self.uniqueCallParts = {}; + for (let i = 0; i < callParts.length; i++) { + self.uniqueCallParts[callParts[i]] = true; + } + if (this.callUserLimited && this.canJoinLimitedCall()) { + this.callUserLimited.abort(); + this.callUserLimited = false; } + const msg = self.messagesBuff.getMessageById(msgId); + msg && msg.wrappedChatDialogMessage && msg.wrappedChatDialogMessage.trackDataChange(); + self.trackDataChange(); }; -Chat.prototype.renderMyStatus = SoonFc(Chat.prototype._renderMyStatus, 100); -Chat.prototype.openChat = function (userHandles, type, chatId, chatShard, chatdUrl, setAsActive, chatHandle, publicChatKey, ck, isMeeting, mcoFlags, organiser) { +ChatRoom.prototype.onPublicChatRoomInitialized = function () { const self = this; - let room = false; - type = type || "private"; - setAsActive = setAsActive === true; - let roomId = chatId; - if (!publicChatKey && chatHandle && self.publicChatKeys[chatHandle]) { - if (type !== "public") { - console.error("this should never happen.", type); - type = "public"; - } - publicChatKey = self.publicChatKeys[chatHandle]; - } - const $promise = new MegaPromise(); - if (type === "private") { - this.initContacts(userHandles, 2); - roomId = userHandles.length > 1 ? array.one(userHandles, u_handle) : u_handle; - if (self.chats[roomId]) { - $promise.resolve(roomId, self.chats[roomId]); - return [roomId, self.chats[roomId], $promise]; - } - } else { - assert(roomId, 'Tried to create a group chat, without passing the chatId.'); - roomId = chatId; + if (self.type !== "public" || !localStorage.autoJoinOnLoginChat) { + return; } - if (type === "group" || type === "public") { - if (d) { - console.time(`openchat:${ chatId }.${ type}`); - } - const newUsers = this.initContacts(userHandles); - if (newUsers.length) { - const chats = self.chats._data; - if (d) { - console.debug('openchat:%s.%s: processing %s new users...', chatId, type, newUsers.length); - } - for (const k in chats) { - const chatRoom = self.chats[k]; - const participants = array.to.object(chatRoom.getParticipantsExceptMe()); - for (let j = newUsers.length; j--;) { - const u = newUsers[j]; - if (participants[u]) { - chatRoom.trackDataChange(); - break; - } + const autoLoginChatInfo = tryCatch(JSON.parse.bind(JSON))(localStorage.autoJoinOnLoginChat) || false; + if (autoLoginChatInfo[0] === self.publicChatHandle) { + localStorage.removeItem("autoJoinOnLoginChat"); + if (unixtime() - 7200 < autoLoginChatInfo[1]) { + const doJoinEventually = function (state) { + if (state === ChatRoom.STATE.READY) { + self.joinViaPublicHandle(); + self.unbind(`onStateChange.${ self.publicChatHandle}`); } - } - self.renderMyStatus(); - } - if (d) { - console.timeEnd(`openchat:${ chatId }.${ type}`); - } - if (type === "group") { - ChatdIntegration._ensureKeysAreLoaded([], userHandles, chatHandle).catch(dump); + }; + self.rebind(`onStateChange.${ self.publicChatHandle}`, (e, oldState, newState) => { + doJoinEventually(newState); + }); + doJoinEventually(self.state); } - ChatdIntegration._ensureContactExists(userHandles, chatHandle); } - if (self.chats[roomId]) { - room = self.chats[roomId]; - if (setAsActive) { - room.show(); - } - $promise.resolve(roomId, room); - return [roomId, room, $promise]; - } - if (setAsActive && self.currentlyOpenedChat && self.currentlyOpenedChat !== roomId) { - self.hideChat(self.currentlyOpenedChat); - self.currentlyOpenedChat = null; - } - room = new ChatRoom(self, roomId, type, userHandles, unixtime(), undefined, chatId, chatShard, chatdUrl, null, chatHandle, publicChatKey, ck, isMeeting, 0, mcoFlags, organiser); - self.chats.set(room.roomId, room); - if (setAsActive && !self.currentlyOpenedChat || self.currentlyOpenedChat === room.roomId) { - room.setActive(); - } - room.showAfterCreation = setAsActive !== false; - return [roomId, room, new Promise((resolve, reject) => { - this.trigger('onRoomInitialized', [room, resolve, reject]); - room.setState(ChatRoom.STATE.JOINING); - const q = this._queuedChatRoomEvents[chatId]; - if (q) { - delete this._queuedChatRoomEvents[chatId]; - for (let i = 0; i < q.length; ++i) { - const [event, data] = q[i]; - if (d) { - this.logger.debug(`Dispatching deferred event '${event}'`, data); - } - room.trigger(event, data); - } - q.timer.abort(); - } - this.processQueuedMcsmPackets(); - })]; }; -Chat.prototype.initContacts = function (userHandles, c) { - const newUsers = []; - for (let i = userHandles.length; i--;) { - const u = userHandles[i]; - const e = u in M.u; - M.addUser(e ? { - u - } : { - u, - c - }, e || !newUsers.push(u)); +ChatRoom.prototype.isUIMounted = function () { + return this._uiIsMounted; +}; +ChatRoom.prototype.attachSearch = function () { + this.activeSearches++; +}; +ChatRoom.prototype.detachSearch = function () { + if (--this.activeSearches === 0) { + this.messagesBuff.detachMessages(); } - return newUsers; + this.activeSearches = Math.max(this.activeSearches, 0); + this.trackDataChange(); }; -Chat.prototype.smartOpenChat = function (...args) { +ChatRoom.prototype.scrollToMessageId = function (msgId, index, retryActive) { const self = this; - if (typeof args[0] === 'string') { - args[0] = [u_handle, args[0]]; - if (args.length < 2) { - args.push('private'); - } + if (!self.isCurrentlyActive && !retryActive) { + tSleep(1.5).then(() => { + self.scrollToMessageId(msgId, index, true); + }); + return; } - return new Promise((resolve, reject) => { - const waitForReadyState = function (aRoom, aShow) { - const verify = function () { - return aRoom.state === ChatRoom.STATE.READY; - }; - const ready = function () { - if (aShow) { - aRoom.show(); - } - resolve(aRoom); - }; - if (verify()) { - return ready(); - } - const { - roomId - } = aRoom; - createTimeoutPromise(verify, 300, 3e4, false, `waitForReadyState(${roomId})`).then(ready).catch(reject); - }; - const [members, type] = args; - if (members.length === 2 && type === 'private') { - const chatRoom = self.chats[members.every(h => h === members[0]) ? u_handle : array.one(members, u_handle)]; - if (chatRoom) { - if (args[5]) { - chatRoom.show(); - } - return waitForReadyState(chatRoom, args[5]); - } - } - const result = self.openChat.apply(self, args); - if (result instanceof MegaPromise) { - result.then(reject).catch(reject); - } else if (!Array.isArray(result)) { - reject(EINTERNAL); - } else { - const room = result[1]; - const roomId = result[0]; - const promise = result[2]; - if (!(promise instanceof Promise)) { - self.logger.error('Unexpected openChat() response...'); - return reject(EINTERNAL); - } - self.logger.debug('Waiting for chat "%s" to be ready...', roomId, [room]); - promise.then(aRoom => { - const aRoomId = aRoom && aRoom.roomId; - if (aRoomId !== roomId || room && room !== aRoom || !(aRoom instanceof ChatRoom)) { - self.logger.error('Unexpected openChat() procedure...', aRoomId, [aRoom]); - return reject(EINTERNAL); - } - waitForReadyState(aRoom); - }).catch(ex => { - if (ex === EACCESS) { - room.destroy(); - } - reject(ex); + assert(self.isCurrentlyActive, 'chatRoom is not visible'); + self.isScrollingToMessageId = true; + if (!self.$rConversationPanel) { + self.one(`onHistoryPanelComponentDidMount.scrollToMsgId${ msgId}`, () => { + self.scrollToMessageId(msgId, index); + }); + return; + } + const ps = self.$rConversationPanel.messagesListScrollable; + assert(ps); + const msgObj = self.messagesBuff.getMessageById(msgId); + if (msgObj) { + const elem = $(`.${ msgId }.message.body`)[0]; + self.scrolledToBottom = false; + ps.scrollToElement(elem, true); + self.$rConversationPanel.lastScrollPosition = undefined; + self.isScrollingToMessageId = false; + } else if (self.messagesBuff.isRetrievingHistory) { + self.one(`onHistoryDecrypted.scrollToMsgId${ msgId}`, () => { + self.one(`onComponentDidUpdate.scrollToMsgId${ msgId}`, () => { + self.scrollToMessageId(msgId, index); }); - } - }); -}; -Chat.prototype.hideAllChats = function () { - const self = this; - self.chats.forEach(chatRoom => { - if (chatRoom.isCurrentlyActive) { - chatRoom.hide(); - } - }); -}; -Chat.prototype.retrieveSharedFilesHistory = async function (len = 47, chatRoom = null) { - chatRoom = len instanceof ChatRoom ? len : chatRoom || this.getCurrentRoom(); - return chatRoom.messagesBuff.retrieveSharedFilesHistory(len); -}; -Chat.prototype.getCurrentRoom = function () { - return this.chats[this.currentlyOpenedChat]; -}; -Chat.prototype.getCurrentMeeting = function () { - const chatRoom = this.getCurrentRoom(); - return chatRoom && chatRoom.scheduledMeeting || null; -}; -Chat.prototype.getCurrentRoomJid = function () { - return this.currentlyOpenedChat; -}; -Chat.prototype.hideChat = function (roomJid) { - const self = this; - const room = self.chats[roomJid]; - if (room) { - room.hide(); + }); + } else if (self.messagesBuff.haveMoreHistory()) { + self.messagesBuff.retrieveChatHistory(!index || index <= 0 ? undefined : index); + ps.doProgramaticScroll(0, true); + self.one(`onHistoryDecrypted.scrollToMsgId${ msgId}`, () => { + self.one(`onComponentDidUpdate.scrollToMsgId${ msgId}`, () => { + self.scrollToMessageId(msgId); + }); + }); } else { - self.logger.warn("Room not found: ", roomJid); + self.isScrollingToMessageId = false; } }; -Chat.prototype.sendMessage = function (roomJid, val) { - const fail = ex => { - this.logger.error(`sendMessage(${roomJid}) failed.`, ex); +ChatRoom.prototype.setMcoFlags = function (flags) { + const req = { + a: 'mco', + cid: this.chatId, + ...flags }; - if (!this.chats[roomJid]) { - this.logger.warn("Queueing message for room: ", roomJid, val); - const timeout = this.options.delaySendMessageIfRoomNotAvailableTimeout; - return createTimeoutPromise(() => !!this.chats[roomJid], 500, timeout).then(() => { - return this.chats[roomJid].sendMessage(val); - }).catch(fail); + asyncApiReq(req).dump('roomSetCallFlags'); +}; +ChatRoom.prototype.toggleOpenInvite = function () { + if (this.type === 'private' || !this.iAmOperator()) { + return; } - return this.chats[roomJid].sendMessage(val).catch(fail); + this.setMcoFlags({ + [MCO_FLAGS.OPEN_INVITE]: Math.abs(this.options[MCO_FLAGS.OPEN_INVITE] - 1) + }); }; -Chat.prototype.processNewUser = function (u, isNewChat) { - const self = this; - if (self.plugins.presencedIntegration) { - const user = M.u[u] || false; - if (user.c === 1) { - self.plugins.presencedIntegration.addContact(u, isNewChat); - } +ChatRoom.prototype.toggleWaitingRoom = function () { + if (this.type === 'private' || !this.iAmOperator()) { + return; } - self.chats.forEach((chatRoom) => { - if (chatRoom.getParticipantsExceptMe().indexOf(u) > -1) { - chatRoom.trackDataChange(); - } + this.setMcoFlags({ + [MCO_FLAGS.WAITING_ROOM]: Math.abs(this.options[MCO_FLAGS.WAITING_ROOM] - 1) }); - self.renderMyStatus(); }; -Chat.prototype.processRemovedUser = function (u) { - const self = this; - if (self.plugins.presencedIntegration) { - self.plugins.presencedIntegration.removeContact(u); +ChatRoom.prototype.exportToFile = function () { + if (this.messagesBuff.messages.length === 0 || this.exportIo) { + return; } - self.chats.forEach((chatRoom) => { - if (chatRoom.getParticipantsExceptMe().indexOf(u) > -1) { - chatRoom.trackDataChange(); + loadingDialog.show('chat_export'); + eventlog(99874); + this._exportChat().then(() => { + eventlog(99875, JSON.stringify([1])); + }).catch(ex => { + if (d) { + console.warn('Chat export: ', ex); + } + const report = [String(ex && ex.message || ex).replace(/\s+/g, '').substring(0, 64)]; + report.unshift(report[0] === 'Aborted' ? 1 : 0); + if (!report[0]) { + msgDialog('error', '', l.export_chat_failed, '', undefined, 1); } + eventlog(99875, JSON.stringify(report)); + }).finally(() => { + loadingDialog.hide('chat_export'); + this.isScrollingToMessageId = false; + onIdle(() => this.messagesBuff.detachMessages()); }); - self.renderMyStatus(); }; -Chat.prototype.refreshConversations = function () { - const self = this; - if (!u_type && !self.$container && !megaChatIsReady) { - $('.fm-chat-block').hide(); - return false; - } - $('.section.conversations .fm-chat-is-loading').addClass('hidden'); - if (self.$container.parent('.section.conversations .fm-right-files-block').length == 0) { - $('.section.conversations .fm-right-files-block').append(self.$container); - } - self.$leftPane = self.$leftPane || $('.conversationsApp .fm-left-panel'); - if (is_chatlink || megaChat._joinDialogIsShown) { - self.$leftPane.addClass('hidden'); - } else { - self.$leftPane.removeClass('hidden'); - } -}; -Chat.prototype.navigate = function megaChatNavigate(location, event, isLandingPage) { - return new Promise((resolve, reject) => { - this.routing.route(resolve, reject, location, event, isLandingPage); - }); -}; -if (is_mobile) { - Chat.prototype.navigate = function (location, event, isLandingPage) { - if (d) { - this.logger.warn('mobile-nop navigate(%s)', location, event, isLandingPage); - } - if (is_chatlink) { - mobile.chatlink.show(is_chatlink.ph, is_chatlink.key); - } else { - loadSubPage('fm', event); - } - return Promise.resolve(); - }; -} -Chat.prototype.renderListing = async function megaChatRenderListing(location, isInitial) { - if (!isInitial && !M.chat) { - console.debug('renderListing: Not in chat.'); - throw EACCESS; - } - M.hideEmptyGrids(); - this.refreshConversations(); - this.hideAllChats(); - if (!is_chatlink && mega.ui.flyout && (mega.ui.flyout.name.startsWith('contact') || mega.ui.flyout.name === 'chat')) { - mega.ui.flyout.hide(); +ChatRoom.prototype._exportChat = async function () { + this.isScrollingToMessageId = true; + while (this.messagesBuff.haveMoreHistory()) { + await this.messagesBuff.retrieveChatHistory(100); } - $('.files-grid-view').addClass('hidden'); - $('.fm-blocks-view').addClass('hidden'); - $('.fm-chat-block').addClass('hidden'); - $('.fm-right-files-block').addClass('hidden'); - $('.fm-right-files-block.in-chat').removeClass('hidden'); - $('.nw-conversations-item').removeClass('selected'); - $('.fm-empty-conversations').removeClass('hidden'); - M.onSectionUIOpen('conversations'); - let room; - if (!location && this.chats.length) { - const valid = room => room && room._leaving !== true && !room.isNote && room.isDisplayable() && room; - room = valid(this.chats[this.lastOpenedChat]); - if (!room) { - let idx = 0; - const rooms = Object.values(this.chats).filter(r => this.currentlyOpenedView === null || r.isMeeting === !!this.currentlyOpenedView).sort(M.sortObjFn('lastActivity', -1)); - do { - room = valid(rooms[idx]); - } while (!room && ++idx < rooms.length); - } - if (room) { - location = room.getRoomUrl(); + await Promise.allSettled([this.messagesBuff.isDecrypting || Promise.resolve(), this.messagesBuff.$sharedFilesLoading || Promise.resolve(), this.messagesBuff.$isDecryptingSharedFiles || Promise.resolve()]); + do { + await this.messagesBuff.retrieveSharedFilesHistory(100); + } while (this.messagesBuff.haveMoreSharedFiles); + let withMedia = !!M.v.length; + if (withMedia) { + withMedia = await asyncMsgDialog(`*confirmation:!^${l.export_chat_media_dlg_conf}!${l.export_chat_media_dlg_rej}`, '', l.export_chat_media_dlg_title, l.export_chat_media_dlg_text); + if (withMedia === null) { + throw new Error('Aborted'); } } - if (location) { - $('.fm-empty-conversations').addClass('hidden'); - return this.navigate(location, undefined, isInitial).catch(ex => { - if (d) { - this.logger.warn('Failed to navigate to %s...', location, room, ex); + let { + attachNodes, + stringNodes + } = this.messagesBuff.getExportContent(withMedia); + stringNodes = stringNodes.join('\n'); + const basename = M.getSafeName(this.getRoomTitle()); + const zname = l.export_chat_zip_file.replace('%s', basename); + const bufferName = l.export_chat_text_file.replace('%s', basename); + if (attachNodes.length) { + const p = []; + const n = []; + let s = 0; + for (const node of attachNodes) { + s += node.s; + if (node.ph) { + p.push(node.ph); + } else { + n.push(node.h); } - if (!room) { - return this.renderListing(null); + } + const res = await asyncApiReq({ + a: 'qbq', + s, + n, + p + }); + if (res === 1 || res === 2) { + const fallback = await asyncMsgDialog('confirmation', '', l.export_chat_media_obq_title, l.export_chat_media_obq_text); + if (fallback) { + return M.saveAs(stringNodes, bufferName); } - onIdle(() => { - room.destroy(); + } else if (res === 0) { + await M.require('clientzip_js'); + const data = new TextEncoder().encode(stringNodes); + const dl = { + size: data.byteLength + s, + n: bufferName, + t: unixtime(), + id: this.chatId, + p: '', + io: Object.create(null), + writer: Object.create(null), + offset: 0, + zname + }; + const io = await (0,_utils_jsx0__ .O1)(dl); + const t = new Date((this.lastActivity || this.ctime) * 1000); + let failedCount = 0; + const src = (0,_utils_jsx0__ .VV)(attachNodes, size => { + failedCount++; + dl.done += size; }); - throw ex; - }); + src.unshift({ + name: bufferName, + lastModified: t, + input: data.buffer + }); + dl.done = 0; + const reader = clientZip.downloadZip(src).body.getReader(); + dl.nextChunk = async () => { + const read = await reader.read().catch(dump); + if (!read) { + reader.cancel().catch(ex => { + if (ex !== EOVERQUOTA) { + msgDialog('error', '', l.export_chat_failed, ex < 0 ? api_strerror(ex) : ex, undefined, 1); + } + }); + io.abort(); + delete this.exportIo; + loadingDialog.hideProgress(); + return; + } + if (read.done) { + loadingDialog.hideProgress(); + io.download(zname); + delete this.exportIo; + if (failedCount) { + msgDialog('error', '', l.export_chat_failed, l.export_chat_partial_fail, undefined, 1); + } + } else { + dl.done += read.value.byteLength; + loadingDialog.showProgress(dl.done / dl.size * 100); + io.write(read.value, dl.offset, dl.nextChunk); + dl.offset += read.value.length; + } + }; + io.begin = dl.nextChunk; + io.setCredentials(false, dl.size, zname); + this.exportIo = io; + } else { + throw new Error(`Unexpected qbq response ${res}`); + } + } else { + return M.saveAs(stringNodes, bufferName); } - return ENOENT; }; -Chat.prototype.setAttachments = function (roomId) { - 'use strict'; +ChatRoom.prototype.canJoinLimitedCall = function () { + const callParts = this.getCallParticipants(); + return this.iAmOperator() && callParts.length < CallManager2.CALL_USER_LIMIT || callParts.length < CallManager2.CALL_USER_LIMIT - 1; +}; +window.ChatRoom = ChatRoom; + const __WEBPACK_DEFAULT_EXPORT__ = { + ChatRoom +}; - if (M.chat) { - if (d) { - console.assert(this.chats[roomId] && this.chats[roomId].isCurrentlyActive, 'check this...'); + }, + + 8264 +(_, EXP_, REQ_) { + +"use strict"; + REQ_.d(EXP_, { + LP: () => getUniqueId, + N9: () => timing, + Zz: () => compose, + hG: () => SoonFcWrap, + u9: () => ContactAwareComponent, + w9: () => MegaRenderMixin + }); + + const _babel_runtime_helpers_applyDecoratedDescriptor0__ = REQ_(793); + +let _dec, _dec2, _dec3, _dec4, _dec5, _class; +const INTERSECTION_OBSERVER_AVAILABLE = typeof IntersectionObserver !== 'undefined'; +const RESIZE_OBSERVER_AVAILABLE = typeof ResizeObserver !== 'undefined'; +function shallowEqual(objA, objB) { + if (objA === objB) { + return true; + } + for (var key in objA) { + if (key === "children") { + continue; } - M.v = Object.values(M.chc[roomId] || {}); - if (M.v.length) { - let _this$chats$roomId; - const sv = (_this$chats$roomId = this.chats[roomId]) == null || (_this$chats$roomId = _this$chats$roomId.messagesBuff) == null || (_this$chats$roomId = _this$chats$roomId.sharedFiles) == null ? void 0 : _this$chats$roomId._sortedVals; - if (sv && sv.length === M.v.length) { - M.v.sort((a, b) => sv.indexOf(a.m) - sv.indexOf(b.m)); - } else { - if (d) { - this.logger.info('falling back to order-value sorting.', sv); - } - M.v.sort(M.sortObjFn('co')); - } - for (let i = M.v.length; i--;) { - const n = M.v[i]; - if (!n.revoked && !n.seen) { - n.seen = -1; - if (this._shallLoadImageFor(n)) { - this._enqueueImageLoad(n); + if (objA.hasOwnProperty(key)) { + if (!objB.hasOwnProperty(key)) { + return false; + } else if (objA[key] !== objB[key]) { + if (typeof objA[key] === 'function' && typeof objB[key] === 'function') { + if (objA[key].toString() !== objB[key].toString()) { + return false; } + } else { + return false; } } - if ($.triggerSlideShow) { - delay('chat:refresh-slideshow-on-single-entry', () => { - const { - slideshowid: id - } = window; - if (id && $.triggerSlideShow === id) { - slideshow(id); - } - delete $.triggerSlideShow; - }); - } } - } else if (d) { - console.warn('Not in chat...'); } -}; -Chat.prototype._enqueueMessageUpdate = function (message) { - this._queuedMessageUpdates.push(message); - delay('chat:enqueue-message-updates', () => { - const queue = this._queuedMessageUpdates; - this._queuedMessageUpdates = []; - for (let i = queue.length; i--;) { - queue[i].trackDataChange(); - } - }, 400); -}; -Chat.prototype._shallLoadImageFor = function (n) { - return n && /:[01]\*/.test(n.fa); -}; -Chat.prototype._enqueueImageLoad = function (n) { - 'use strict'; - let cc = previews[n.h] || previews[n.hash]; - if (cc) { - if (cc.poster) { - n.src = cc.poster; - } else { - if (cc.full && n.mime !== 'image/png' && n.mime !== 'image/webp') { - cc = cc.prev || false; - } - if (String(cc.type).startsWith('image/')) { - n.src = cc.src; - } + for (key in objB) { + if (objB.hasOwnProperty(key) && !objA.hasOwnProperty(key)) { + return false; } } - let cached = n.src; - if (this._shallLoadImageFor(n)) { - let load = false; - let dedup = true; - if (this._imageAttributeCache[n.fa]) { - this._imageAttributeCache[n.fa].push(n.ch); - } else { - this._imageAttributeCache[n.fa] = [n.ch]; - load = !cached; + return true; +} +window.shallowEqual = shallowEqual; +const MAX_ALLOWED_DEBOUNCED_UPDATES = 5; +const DEBOUNCED_UPDATE_TIMEOUT = 60; +const REENABLE_UPDATES_AFTER_TIMEOUT = 300; +const MAX_TRACK_CHANGES_RECURSIVE_DEPTH = 1; +let _propertyTrackChangesVars = Object.create(null); +_propertyTrackChangesVars._listenersMap = Object.create(null); +_propertyTrackChangesVars._dataChangedHistory = Object.create(null); +if (window._propertyTrackChangesVars) { + _propertyTrackChangesVars = window._propertyTrackChangesVars; +} else { + window._propertyTrackChangesVars = _propertyTrackChangesVars; +} +window.megaRenderMixinId = window.megaRenderMixinId ? window.megaRenderMixinId : 0; +const FUNCTIONS = ['render', 'shouldComponentUpdate', 'doProgramaticScroll', 'componentDidMount', 'componentDidUpdate', 'componentWillUnmount', 'refreshUI', 'eventuallyInit', 'handleWindowResize', 'focusTypeArea', 'initScrolling', 'updateScroll', 'isActive', 'onMessagesScrollReinitialise', 'specShouldComponentUpdate', 'attachAnimationEvents', 'eventuallyReinitialise', 'reinitialise', 'reinitialised', 'getContentHeight', 'getScrollWidth', 'isAtBottom', 'onResize', 'isComponentEventuallyVisible', 'getCursorPosition', 'getTextareaMaxHeight']; +const localStorageProfileRenderFns = localStorage.profileRenderFns; +if (localStorageProfileRenderFns) { + window.REACT_RENDER_CALLS = {}; +} +let ID_CURRENT = 1; +const DEBUG_THIS = d > 1 ? d : false; +const scheduler = (func, name, debug) => { + const dbug = debug !== false && DEBUG_THIS; + let idnt = null; + let task = null; + const fire = () => { + if (dbug) { + console.warn('Dispatching scheduled task for %s.%s...', idnt, name); } - if (this._imageLoadCache[n.fa]) { - this._imageLoadCache[n.fa].push(n.ch); - } else { - this._imageLoadCache[n.fa] = [n.ch]; - if (load) { - this._imagesToBeLoaded[n.fa] = n; - dedup = false; - } + if (task) { + queueMicrotask(task); + task = null; } - if (dedup) { - cached = true; - } else { - delay('chat:enqueue-image-load', this._doLoadImages.bind(this), 350); + }; + const _scheduler = function () { + if (dbug) { + if (!idnt) { + idnt = name[0] === '(' && this.getReactId && this.getReactId() || this; + } + console.warn('Scheduling task from %s.%s...', idnt, name, [this], !!task); } - } - if (cached) { - this._doneLoadingImage(n.fa); - } -}; -Chat.prototype._doLoadImages = function () { - "use strict"; - - const self = this; - const originals = Object.create(null); - let imagesToBeLoaded = self._imagesToBeLoaded; - self._imagesToBeLoaded = Object.create(null); - const chatImageParser = function (h, data) { - const n = M.chd[(self._imageLoadCache[h] || [])[0]] || false; - if (n && data !== 0xDEAD) { - n.src = mObjectURL([data.buffer || data], 'image/jpeg'); - n.srcBuffer = data; - } else if (d) { - console.warn('Failed to load image for %s', h, n); + if (!task) { + queueMicrotask(fire); } - self._doneLoadingImage(h); - }; - for (const k in imagesToBeLoaded) { - const node = imagesToBeLoaded[k]; - const mime = filemime(node); - if (node.s < LOAD_ORIGINALS[mime]) { - originals[node.fa] = node; - delete imagesToBeLoaded[k]; + let idx = arguments.length; + const args = new Array(idx); + while (idx--) { + args[idx] = arguments[idx]; } - } - const onSuccess = function (ctx, origNodeHandle, data) { - chatImageParser(origNodeHandle, data); - }; - const onError = function (origNodeHandle) { - chatImageParser(origNodeHandle, 0xDEAD); - }; - const loadOriginal = function (n) { - const origFallback = ex => { - const type = String(n.fa).indexOf(':1*') > 0 ? 1 : 0; - if (d) { - console.debug('Failed to load original image on chat.', n.h, n, ex); - } - imagesToBeLoaded[n.fa] = originals[n.fa]; - delete originals[n.fa]; - delay(`ChatRoom[${ self.roomId }]:origFallback${ type}`, () => { - api_getfileattr(imagesToBeLoaded, type, onSuccess, onError); - }); + task = () => { + func.apply(this, args); }; - M.gfsfetch(n.h, 0, -1).then((data) => { - const handler = is_image(n); - if (typeof handler === 'function') { - handler(data, buffer => { - if (buffer) { - chatImageParser(n.fa, buffer); - } else { - origFallback(EFAILED); - } - }); - } else { - chatImageParser(n.fa, data); - } - }).catch(origFallback); }; - if ($.len(originals)) { - Object.values(originals).map(loadOriginal); - } - api_getfileattr(imagesToBeLoaded, 1, onSuccess, onError); - [imagesToBeLoaded, originals].forEach((obj) => { - Object.keys(obj).forEach((handle) => { - self._startedLoadingImage(handle); + if (DEBUG_THIS) { + Object.defineProperty(_scheduler, smbl(name), { + value: func }); - }); - imagesToBeLoaded = Object.create(null); + } + return _scheduler; }; -Chat.prototype._getImageNodes = function (h, src) { - let nodes = this._imageLoadCache[h] || []; - let handles = [].concat(nodes); - for (let i = nodes.length; i--;) { - const n = M.chd[nodes[i]] || false; - if (this._imageAttributeCache[n.fa]) { - handles = handles.concat(this._imageAttributeCache[n.fa]); +const timing = (min, max) => { + return function (target, key, de) { + if (DEBUG_THIS > 2) { + de[key] = de.value; + _timing(de, min, max); + de.value = de[key]; } - } - handles = array.unique(handles); - nodes = handles.map((ch) => { - const n = M.chd[ch] || false; - if (src && n.src) { - Object.assign(src, n); + return de; + }; +}; +const logcall = () => { + return function (target, key, descriptor) { + if (DEBUG_THIS > 3) { + const func = descriptor.value; + descriptor.value = function () { + console.group('[logcall] Entering into %s.%s...', this, key); + const r = func.apply(this, arguments); + console.info('[logcall] Leaving %s.%s...', this, key); + console.groupEnd(); + return r; + }; } - return n; - }); - return nodes; + return descriptor; + }; }; -Chat.prototype._startedLoadingImage = function (h) { - "use strict"; - - const nodes = this._getImageNodes(h); - for (let i = nodes.length; i--;) { - const n = nodes[i]; - if (!n.src && n.seen !== 2) { - let imgNode = document.getElementById(n.ch); - if (imgNode && (imgNode = imgNode.querySelector('img'))) { - imgNode.parentNode.parentNode.classList.add('thumb-loading'); - } +const schedule = (local, debug) => { + return function (target, property, descriptor) { + if (local) { + const func = descriptor.value; + descriptor = { + configurable: true, + get: function _unusedScheduler() { + Object.defineProperty(this, property, { + value: scheduler(func, `(${ property })`, debug) + }); + return this[property]; + } + }; + } else { + descriptor.value = scheduler(descriptor.value, property, debug); } - } + return descriptor; + }; }; -Chat.prototype._doneLoadingImage = function (h) { - const self = this; - const setSource = function (n, img, src) { - const message = n.mo; - img.onload = function () { - img.onload = null; - n.srcWidth = this.naturalWidth; - n.srcHeight = this.naturalHeight; - if (message) { - self._enqueueMessageUpdate(message); - } +const compose = (...funcs) => funcs.reduce((a, b) => (...args) => a(b(...args)), arg => arg); +const replaceAt = (i, o, n) => `${o.slice(0, i)}${n}${o.slice(i + n.length)}`; +const SoonFcWrap = (milliseconds, local) => { + return function (target, propertyKey, descriptor) { + descriptor.value = SoonFc(descriptor.value, !local, milliseconds); + return descriptor; + }; +}; +const rAFWrap = () => { + return function (target, propertyKey, descriptor) { + const old = descriptor.value; + descriptor.value = function () { + return old.apply(this, arguments); }; - img.setAttribute('src', src); + return descriptor; }; - const root = {}; - const nodes = this._getImageNodes(h, root); - const {src} = root; - for (let i = nodes.length; i--;) { - const n = nodes[i]; - let imgNode = document.getElementById(n.ch); - if (imgNode && (imgNode = imgNode.querySelector('img'))) { - const parent = imgNode.parentNode; - const container = parent.parentNode; - if (src) { - container.classList.add('thumb'); - parent.classList.remove('no-thumb'); - } else { - container.classList.add('thumb-failed'); +}; +const trycatcher = () => (t, p, d) => (d.value = tryCatch(d.value)) && d; +const getUniqueId = () => makeUUID().slice(-12); +const MegaRenderMixin = (_dec = logcall(), _dec2 = SoonFcWrap(50, true), _dec3 = logcall(), _dec4 = SoonFcWrap(80, true), _dec5 = SoonFcWrap(350, true), _class = class MegaRenderMixin extends React.Component { + constructor(props) { + super(props); + lazy(this, '__internalReactID', function () { + let key = ''; + let fib = DEBUG_THIS && this._reactInternalFiber; + while (fib) { + let tmp = fib.key; + if (tmp && tmp[0] !== '.' && key.indexOf(tmp) < 0) { + key += `${tmp }/`; + } + if (tmp = fib.memoizedProps) { + if (tmp.contact) { + tmp = tmp.contact.u + (tmp.chatRoom ? `@${ tmp.chatRoom.roomId}` : ''); + } else if (tmp.chatRoom) { + tmp = tmp.chatRoom.roomId; + } else { + tmp = 0; + } + if (tmp && key.indexOf(tmp) < 0) { + key += `${tmp }/`; + } + } + fib = fib._debugOwner; } - n.seen = 2; - container.classList.remove('thumb-loading'); - setSource(n, imgNode, src || window.noThumbURI || ''); + key = key ? `[${ key.substr(0, key.length - 1) }]` : ''; + return `::${ this.constructor.name }[${ `000${ ID_CURRENT++}`.slice(-4) }]${ key}`; + }); + lazy(this, '__internalUniqueID', function () { + return (this.__internalReactID + makeUUID().substr(-12)).replace(/[^a-zA-Z0-9]/g, ''); + }); + Object.defineProperty(this, 'isMounted', { + value: function MegaRenderMixin_isMounted() { + return !!this.__isMounted; + } + }); + if (DEBUG_THIS > 2) { + Object.defineProperty(this, 'safeForceUpdate', { + value: function MegaRenderMixin_safeForceUpdate_debug() { + console.group('%s.safeForceUpdate: mounted:%s, visible:%s', this.getReactId(), this.__isMounted, this.isComponentEventuallyVisible()); + if (this.__isMounted) { + this.forceUpdate(() => { + console.warn('%s.safeForceUpdate finished.', this.getReactId()); + console.groupEnd(); + }); + } + } + }); + Object.keys(this).forEach(k => { + if (this[k] && this[k].apply) { + const orig = this[k]; + this[k] = function () { + let s = performance.now(); + const r = orig.apply(this, arguments); + s = performance.now() - s; + if (s > 30) { + console.error(k, this, "took", s, "ms", 'returned', r); + } + return r; + }; + } + }); } - if (src) { - n.src = src; - if (root.srcBuffer && root.srcBuffer.byteLength) { - n.srcBuffer = root.srcBuffer; + if (DEBUG_THIS) { + if (!megaChat.__components) { + megaChat.__components = new WeakMap(); } - if (n.srcBuffer && !previews[n.h] && is_image3(n)) { - preqs[n.h] = 1; - previewimg(n.h, n.srcBuffer, 'image/jpeg'); - previews[n.h].fromChat = Date.now(); + megaChat.__components.set(this, Object.getPrototypeOf(this)); + } + } + componentWillUnmount() { + if (super.componentWillUnmount) { + super.componentWillUnmount(); + } + this.__isMounted = false; + chatGlobalEventManager.removeEventListener('resize', `megaRenderMixing${ this.getUniqueId()}`); + chatGlobalEventManager.removeEventListener('hashchange', `hc${ this.getUniqueId()}`); + const node = this.findDOMNode(); + if (this.__intersectionObserverInstance) { + if (node) { + this.__intersectionObserverInstance.unobserve(node); } + this.__intersectionObserverInstance.disconnect(); + this.__intersectionObserverInstance = undefined; + } + if (this.onResizeObserved) { + if (!RESIZE_OBSERVER_AVAILABLE) { + $(document.body).unbind(`resize.resObs${ this.getUniqueId()}`); + } else { + this.__resizeObserverInstance.unobserve(node); + this.__resizeObserverInstance.disconnect(); + this.__resizeObserverInstance = undefined; + } + } + const instanceId = this.getUniqueId(); + const listeners = _propertyTrackChangesVars._listenersMap[instanceId]; + if (listeners) { + for (const k in listeners) { + const v = listeners[k]; + v[0].removeChangeListener(v[1]); + } + } + _propertyTrackChangesVars._listenersMap[instanceId] = null; + _propertyTrackChangesVars._dataChangedHistory[instanceId] = null; + if (this._dataStructListeners) { + this._internalDetachRenderCallbacks(); + } + if (this.detachRerenderCallbacks) { + this.detachRerenderCallbacks(); } - delete n.mo; } - if (src) { - mBroadcaster.sendMessage('chat_image_preview'); + getReactId() { + return this.__internalReactID; } -}; -Chat.prototype.onChatsHistoryReady = promisify(function (resolve, reject, timeout) { - if (this.allChatsHadInitialLoadedHistory()) { - return resolve(); + getUniqueId() { + return this.__internalUniqueID; } - let timer = null; - const {chatd} = this.plugins.chatdIntegration; - const eventName = `onMessagesHistoryDone.ochr${ makeid(16)}`; - const ready = () => { - queueMicrotask(resolve); - chatd.off(eventName); - if (timer) { - timer.abort(); - timer = null; + debouncedForceUpdate() { + this.eventuallyUpdate(); + } + componentDidMount() { + if (super.componentDidMount) { + super.componentDidMount(); } - }; - chatd.on(eventName, () => { - if (this.allChatsHadInitialLoadedHistory()) { - ready(); + this.__isMounted = true; + this._wasRendered = true; + if (this.props.requiresUpdateOnResize || this.requiresUpdateOnResize || !this.props.skipQueuedUpdatesOnResize) { + chatGlobalEventManager.addEventListener('resize', `megaRenderMixing${ this.getUniqueId()}`, () => this.onResizeDoUpdate()); + } + chatGlobalEventManager.addEventListener('hashchange', `hc${ this.getUniqueId()}`, () => this.onResizeDoUpdate()); + if (this.props) { + this._recurseAddListenersIfNeeded("p", this.props); + } + if (this.state) { + this._recurseAddListenersIfNeeded("s", this.state); + } + const node = this.findDOMNode(); + if (INTERSECTION_OBSERVER_AVAILABLE && !this.customIsEventuallyVisible && node && node.nodeType) { + this.__intersectionVisibility = false; + onIdle(() => { + this.__intersectionObserverInstance = new IntersectionObserver(entries => { + const entry = entries.pop(); + if (entry.intersectionRatio < 0.2 && !entry.isIntersecting) { + this.__intersectionVisibility = false; + } else { + this.__intersectionVisibility = true; + if (this._requiresUpdateOnResize) { + this.debouncedForceUpdate(); + } + } + if (this.onVisibilityChange) { + this.onVisibilityChange(this.__intersectionVisibility); + } + }, { + threshold: 0.1 + }); + this.__intersectionObserverInstance.observe(node); + }); + } + if (this.onResizeObserved) { + if (!RESIZE_OBSERVER_AVAILABLE) { + $(document.body).rebind(`resize.resObs${ this.getUniqueId()}`, () => { + this.onResizeObserved(node.offsetWidth, node.offsetHeight); + }); + } else { + this.__resizeObserverInstance = new ResizeObserver(entries => { + this.onResizeObserved(entries[0].contentRect.width, entries[0].contentRect.height); + }); + this.__resizeObserverInstance.observe(node); + } + } + if (this.attachRerenderCallbacks) { + this.attachRerenderCallbacks(); } - }); - if (timeout > 0) { - (timer = tSleep(timeout / 1e3)).then(ready); } -}); -Chat.prototype.allChatsHadLoadedHistory = function () { - const chatIds = this.chats.keys(); - for (let i = chatIds.length; i--;) { - const room = this.chats[chatIds[i]]; - if (room.isLoading()) { - return false; + findDOMNode() { + if (!this.domNode) { + let _this$domRef; + this.domNode = (_this$domRef = this.domRef) == null ? void 0 : _this$domRef.current; } + return this.domNode; } - return true; -}; -Chat.prototype.allChatsHadInitialLoadedHistory = function () { - const self = this; - const chatIds = self.chats.keys(); - for (let i = chatIds.length; i--;) { - const room = self.chats[chatIds[i]]; - if (room.chatId && room.initialMessageHistLoaded === false) { + isComponentVisible() { + if (!this.__isMounted) { + return false; + } + if (this.customIsEventuallyVisible) { + const ciev = this.customIsEventuallyVisible; + const result = typeof ciev === "function" ? ciev.call(this) : ciev; + if (result !== -1) { + return result; + } + } + if (this.__intersectionVisibility === false) { + return false; + } else if (this.__intersectionVisibility === true) { + return true; + } + const domNode = this.findDOMNode(); + if (!this.props.hideable && (!domNode || domNode.offsetParent === null)) { + return false; + } + if (!$(domNode).is(":visible")) { return false; } + return verge.inViewport(domNode); } - return true; -}; -Chat.prototype.getPrivateRoom = function (h) { - 'use strict'; - - return this.chats[h] || false; -}; -Chat.prototype.createAndShowPrivateRoom = promisify(function (resolve, reject, h) { - M.openFolder(`chat/p/${ h}`).then(() => { - const room = this.getPrivateRoom(h); - assert(room, 'room not found..'); - resolve(room); - }).catch(reject); -}); -Chat.prototype.createAndShowGroupRoomFor = function (contactHashes, topic = '', opts = {}) { - this.trigger('onNewGroupChatRequest', [contactHashes, { - topic, - ...opts - }]); -}; -Chat.prototype.createAndStartMeeting = function (topic, audio, video) { - megaChat.createAndShowGroupRoomFor([], topic, { - keyRotation: false, - createChatLink: true, - isMeeting: true - }); - megaChat.rebind('onRoomInitialized.meetingCreate', (e, room) => { - room.rebind('onNewMeetingReady.meetingCreate', () => { - room.startCall(audio, video); - }); - }); -}; -Chat.prototype._destroyAllChatsFromChatd = function () { - const self = this; - asyncApiReq({ - 'a': 'mcf', - 'v': Chatd.VERSION - }).then(r => { - r.c.forEach((chatRoomMeta) => { - if (chatRoomMeta.g === 1) { - chatRoomMeta.u.forEach((u) => { - if (u.u !== u_handle) { - api_req({ - a: 'mcr', - id: chatRoomMeta.id, - u: u.u, - v: Chatd.VERSION - }); - } - }); - api_req({ - a: 'mcr', - id: chatRoomMeta.id, - u: u_handle, - v: Chatd.VERSION - }); - } - }); - }); -}; -Chat.prototype._leaveAllGroupChats = function () { - asyncApiReq({ - 'a': 'mcf', - 'v': Chatd.VERSION - }).then(r => { - r.c.forEach((chatRoomMeta) => { - if (chatRoomMeta.g === 1) { - asyncApiReq({ - "a": "mcr", - "id": chatRoomMeta.id, - "v": Chatd.VERSION - }); - } - }); - }); -}; -Chat.prototype.getEmojiDataSet = async function (name) { - assert(name === "categories" || name === "emojis", "Invalid emoji dataset name passed."); - if (!this._emojiDataLoading) { - this._emojiDataLoading = Object.create(null); - } - if (!this._emojiData) { - this._emojiData = { - 'emojisUtf': Object.create(null), - 'emojisSlug': Object.create(null) - }; - } - if (this._emojiData[name]) { - return this._emojiData[name]; - } - if (this._emojiDataLoading[name]) { - return this._emojiDataLoading[name]; - } - if (name === "categories") { - this._emojiData[name] = ["people", "nature", "food", "activity", "travel", "objects", "symbols", "flags"]; - return this._emojiData[name]; + isComponentEventuallyVisible() { + if (!this.__isMounted) { + return false; + } + if (this.customIsEventuallyVisible) { + const ciev = this.customIsEventuallyVisible; + return typeof ciev === "function" ? ciev.call(this) : !!ciev; + } + if (typeof this.props.isVisible !== 'undefined') { + return this.props.isVisible; + } + return this.__intersectionVisibility !== false; } - const { - promise - } = mega; - this._emojiDataLoading[name] = promise; - M.xhr({ - type: 'json', - url: `${staticpath}js/chat/emojidata/${name}_v${EMOJI_DATASET_VERSION}.json` - }).then((ev, data) => { - if (!data) { - promise.reject(EFAILED); + eventuallyUpdate() { + if (!window.megaChat || megaChat.isLoggingOut || this._updatesDisabled || !this._wasRendered || !this.__isMounted) { return; } - this._emojiData[name] = data; - delete this._emojiDataLoading[name]; - if (name === "emojis") { - this._mapEmojisToAliases(); - } - promise.resolve(data); - }).catch((ex, error) => { - if (d) { - this.logger.warn('Failed to load emoji data "%s": %s', name, error, [ex]); + if (!this.isComponentEventuallyVisible()) { + this._requiresUpdateOnResize = true; + return; } - delete this._emojiDataLoading[name]; - promise.reject(error || ex); - }); - return promise; -}; -Chat.prototype._mapEmojisToAliases = function () { - const { - emojis - } = this._emojiData; - if (emojis) { - this._emojiData.emojisUtf = Object.create(null); - this._emojiData.emojisSlug = Object.create(null); - for (let i = emojis.length; i--;) { - const emoji = emojis[i]; - this._emojiData.emojisUtf[emoji.u] = emoji; - this._emojiData.emojisSlug[emoji.n] = emoji; + if (this._requiresUpdateOnResize) { + this._requiresUpdateOnResize = false; } + this.forceUpdate(); } -}; -Chat.prototype.isValidEmojiSlug = function (slug) { - const self = this; - const emojiData = self._emojiData.emojis; - if (!emojiData) { - self.getEmojiDataSet('emojis'); - return false; - } - for (let i = 0; i < emojiData.length; i++) { - if (emojiData[i].n === slug) { - return true; + tempDisableUpdates(forHowLong) { + const self = this; + self._updatesDisabled = true; + if (self._updatesReenableTimer) { + clearTimeout(self._updatesReenableTimer); } + const timeout = forHowLong ? forHowLong : self.REENABLE_UPDATES_AFTER_TIMEOUT ? self.REENABLE_UPDATES_AFTER_TIMEOUT : REENABLE_UPDATES_AFTER_TIMEOUT; + self._updatesReenableTimer = setTimeout(() => { + self.tempEnableUpdates(); + }, timeout); } -}; -Chat.prototype.getPresence = function (user_handle) { - if (user_handle && this.plugins.presencedIntegration) { - return this.plugins.presencedIntegration.getPresence(user_handle); - } -}; -Chat.prototype.getPresenceAsCssClass = function (user_handle) { - const presence = this.getPresence(user_handle); - return this.presenceStringToCssClass(presence); -}; -Chat.prototype.presenceStringToCssClass = function (presence) { - if (presence === UserPresence.PRESENCE.ONLINE) { - return 'online'; - } else if (presence === UserPresence.PRESENCE.AWAY) { - return 'away'; - } else if (presence === UserPresence.PRESENCE.DND) { - return 'busy'; - } else if (!presence || presence === UserPresence.PRESENCE.OFFLINE) { - return 'offline'; - } else { - return 'black'; - } -}; -Chat.prototype.generateTempMessageId = function (roomId, messageAndMeta) { - let messageIdHash = u_handle + roomId; - if (messageAndMeta) { - messageIdHash += messageAndMeta; + tempEnableUpdates() { + clearTimeout(this._updatesReenableTimer); + this._updatesDisabled = false; + this.eventuallyUpdate(); } - return `m${ fastHashFunction(messageIdHash) }_${ unixtime()}`; -}; -Chat.prototype.getChatById = function (chatdId) { - const self = this; - if (self.chats[chatdId]) { - return self.chats[chatdId]; - } else if (self.chatIdToRoomId && self.chatIdToRoomId[chatdId] && self.chats[self.chatIdToRoomId[chatdId]]) { - return self.chats[self.chatIdToRoomId[chatdId]]; + onResizeDoUpdate() { + this.eventuallyUpdate(); } - if (this.chats[this.handleToId[chatdId]]) { - return this.chats[this.handleToId[chatdId]]; + _getUniqueIDForMap(map, payload) { + return `${map }.${ payload}`; } - let found = false; - self.chats.forEach((chatRoom) => { - if (!found && chatRoom.chatId === chatdId) { - found = chatRoom; - return false; + _recurseAddListenersIfNeeded(idx, map, depth) { + depth |= 0; + if (map instanceof MegaDataMap && !(this._contactChangeListeners && this._contactChangeListeners.includes(map))) { + const cacheKey = this._getUniqueIDForMap(map, idx); + const instanceId = this.getUniqueId(); + if (!_propertyTrackChangesVars._listenersMap[instanceId]) { + _propertyTrackChangesVars._listenersMap[instanceId] = Object.create(null); + } + if (!_propertyTrackChangesVars._listenersMap[instanceId][cacheKey]) { + _propertyTrackChangesVars._listenersMap[instanceId][cacheKey] = [map, map.addChangeListener(() => this.onPropOrStateUpdated())]; + } } - }); - return found; -}; -Chat.prototype.getNoteChat = function () { - return Object.values(this.chats).find(c => c.isNote); -}; -Chat.prototype.getMessageByMessageId = async function (chatId, messageId) { - const chatRoom = this.getChatById(chatId); - const msg = chatRoom.messagesBuff.getMessageById(messageId); - if (msg) { - return msg; - } - const { - chatdPersist - } = this.plugins.chatdIntegration.chatd; - if (chatdPersist) { - const [msg] = await chatdPersist.getMessageByMessageId(chatId, messageId).catch(dump) || []; - if (msg) { - return Message.fromPersistableObject(chatRoom, msg); + if (depth++ < MAX_TRACK_CHANGES_RECURSIVE_DEPTH && !this.props.manualDataChangeTracking) { + const mapKeys = map instanceof MegaDataMap ? map.keys() : Object.keys(map); + for (let i = 0; i < mapKeys.length; i++) { + const k = mapKeys[i]; + if (map[k]) { + this._recurseAddListenersIfNeeded(`${idx }_${ k}`, map[k], depth); + } + } } } - if (d) { - this.logger.debug('getMessageByMessageId: Cannot find %s on %s', messageId, chatId); - } - return Promise.reject(ENOENT); -}; -Chat.prototype.haveAnyActiveCall = function () { - const self = this; - const chatIds = self.chats.keys(); - for (let i = 0; i < chatIds.length; i++) { - if (self.chats[chatIds[i]].haveActiveCall()) { - return true; + _checkDataStructForChanges(idx, v, rv, depth) { + if (!v && v === rv) { + return false; } - } - return false; -}; -Chat.prototype.haveAnyOnHoldCall = function () { - const self = this; - const chatIds = self.chats.keys(); - for (let i = 0; i < chatIds.length; i++) { - if (self.chats[chatIds[i]].haveActiveOnHoldCall()) { + if (!rv && v) { return true; } - } - return false; -}; -Chat.prototype.openChatAndSendFilesDialog = function (user_handle) { - 'use strict'; - - this.smartOpenChat(user_handle).then((room) => { - if (room.$rConversationPanel && room.$rConversationPanel.isMounted()) { - room.trigger('openSendFilesDialog'); - } else { - room.one('onComponentDidMount.sendFilesDialog', () => { - onIdle(() => room.trigger('openSendFilesDialog')); - }); + if (v === null) { + return rv !== null; } - room.setActive(); - }).catch(this.logger.error.bind(this.logger)); -}; -Chat.prototype.openChatAndAttachNodes = async function (targets, nodes, silent) { - const promises = []; - if (d) { - console.group('Attaching nodes to chat room(s)...', targets, nodes); - } - const attachNodes = roomId => this.smartOpenChat(roomId).then(room => { - return room.attachNodes(nodes).then(res => { - if (res !== EBLOCKED && res !== ENOENT) { - return room; + if (v instanceof MegaDataMap) { + const cacheKey = this._getUniqueIDForMap(v, idx); + const dataChangeHistory = _propertyTrackChangesVars._dataChangedHistory; + const instanceId = this.getUniqueId(); + if (!dataChangeHistory[instanceId]) { + dataChangeHistory[instanceId] = Object.create(null); } - }); - }).catch(ex => { - if (d) { - this.logger.warn('Cannot openChat for %s and hence nor attach nodes to it.', roomId, ex); - } - throw ex; - }); - if (!Array.isArray(targets)) { - targets = [targets]; - } - for (let i = targets.length; i--;) { - promises.push(attachNodes(targets[i])); - } - const result = (await Promise.allSettled(promises)).map(e => e.value).filter(Boolean); - let folderCount = 0; - let fileCount = 0; - for (let i = nodes.length; i--;) { - const { - t - } = M.getNodeByHandle(nodes[i]) || {}; - if (t === 1) { - folderCount++; - } else { - fileCount++; - } - } - let message = mega.icu.format(l.toast_send_chat_items, nodes.length); - if (fileCount === 0 && folderCount) { - message = mega.icu.format(l.toast_send_chat_folders, folderCount); - } else if (folderCount === 0 && fileCount) { - message = mega.icu.format(l.toast_send_chat_files, fileCount); - } - for (let i = result.length; i--;) { - if (result[i] instanceof ChatRoom) { - const room = result[i]; - mega.ui.toast.show(message); - if (!silent) { - await M.openFolder(room.getRoomUrl().replace('fm/', '')).catch(dump); + if (dataChangeHistory[instanceId][cacheKey] !== v._dataChangeIndex) { + if (window.RENDER_DEBUG) { + console.error("changed: ", this.getElementName(), cacheKey, v._dataChangeTrackedId, v._dataChangeIndex, v); + } + dataChangeHistory[instanceId][cacheKey] = v._dataChangeIndex; + return true; } - break; + return false; } + return depth < MAX_TRACK_CHANGES_RECURSIVE_DEPTH && v && v.byteLength === undefined && typeof v === "object" && this._recursiveSearchForDataChanges(idx, v, rv, depth + 1) === true; } - if (d) { - console.groupEnd(); - } - return result; -}; -Chat.prototype.toggleUIFlag = function (name) { - this.chatUIFlags.set(name, this.chatUIFlags[name] ? 0 : 1); -}; -Chat.prototype.onSnActionPacketReceived = function () { - if (this._queuedMccPackets.length > 0) { - const aps = this._queuedMccPackets; - this._queuedMccPackets = []; - for (let i = 0; i < aps.length; i++) { - mBroadcaster.sendMessage('onChatdChatUpdatedActionPacket', aps[i]); + _recursiveSearchForDataChanges(idx, map, referenceMap, depth) { + const self = this; + depth = depth || 0; + if (!this.isMounted() || this._updatesDisabled === true) { + return; } - } - this.processQueuedMcsmPackets(); -}; -Chat.prototype.processQueuedMcsmPackets = function () { - const aps = Object.values(this._queuedMcsmPackets); - if (aps.length) { - for (let i = 0; i < aps.length; i++) { - const ap = aps[i]; - const { - type, - data - } = ap; - const { - meetingsManager - } = this.plugins; - if (type === 'mcsmp') { - const chatRoom = this.getChatById(data.cid); - if (chatRoom) { - const scheduledMeeting = meetingsManager.attachMeeting(data, true); - delete this._queuedMcsmPackets[scheduledMeeting.id]; - return scheduledMeeting.iAmOwner ? null : notify.notifyFromActionPacket({ - ...data, - a: type - }); + if (!this._wasRendered) { + if (window.RENDER_DEBUG) console.error("First time render", self.getElementName(), map, referenceMap); + this._wasRendered = true; + return true; + } + if (idx === "p_children") { + if (map.map && referenceMap.map) { + const oldKeys = map.map((child) => { + return child ? child.key : child; + }); + const newKeys = referenceMap.map((child) => { + return child ? child.key : child; + }); + if (!shallowEqual(oldKeys, newKeys)) { + return true; + } + } else if (!map && referenceMap || map && !referenceMap) { + return true; + } else if (map.$$typeof && referenceMap.$$typeof) { + if (!shallowEqual(map.props, referenceMap.props) || !shallowEqual(map.state, referenceMap.state)) { + return true; } } - if (type === 'mcsmr') { - meetingsManager.detachMeeting(data); - delete this._queuedMcsmPackets[data.id]; + } else if (map && !referenceMap || !map && referenceMap || map && referenceMap && !shallowEqual(map, referenceMap)) { + return true; + } + const mapKeys = map instanceof MegaDataMap ? map.keys() : Object.keys(map); + for (let i = mapKeys.length; i--;) { + const k = mapKeys[i]; + if (this._checkDataStructForChanges(`${idx }_${ k}`, map[k], referenceMap[k], depth)) { + return true; } } + return false; } -}; -Chat.prototype.getFrequentContacts = function () { - if (Chat._frequentsCache) { - return Chat._frequentsCache; - } - const {chats} = this; - const recentContacts = {}; - const promises = []; - const finishedLoadingChats = {}; - const loadingMoreChats = {}; - const _calculateLastTsFor = function (r, maxMessages) { - const mb = r.messagesBuff; - const len = mb.messages.length; - const msgs = mb.messages.slice(Math.max(0, len - maxMessages), len); - for (let i = 0; i < msgs.length; i++) { - const msg = msgs[i]; - let contactHandle = msg.userId === mega.BID && msg.meta ? msg.meta.userId : msg.userId; - if (r.type === "private" && contactHandle === u_handle) { - contactHandle = contactHandle || r.getParticipantsExceptMe()[0]; + shouldComponentUpdate(nextProps, nextState) { + let shouldRerender = false; + if (megaChat && megaChat.isLoggingOut) { + return false; + } + if (!this.isMounted() || this._updatesDisabled === true) { + if (window.RENDER_DEBUG) { + console.error("shouldUpdate? No.", "F1", this.getElementName(), this.props, nextProps, this.state, nextState); } - if (contactHandle !== mega.BID && contactHandle !== strongvelope.COMMANDER && contactHandle in M.u && M.u[contactHandle].c === 1 && contactHandle !== u_handle) { - if (!recentContacts[contactHandle] || recentContacts[contactHandle].ts < msg.delay) { - recentContacts[contactHandle] = { - 'userId': contactHandle, - 'ts': msg.delay - }; + return false; + } + if (this.customIsEventuallyVisible) { + let ciev = this.customIsEventuallyVisible; + ciev = typeof ciev === "function" ? ciev.call(this) : !!ciev; + if (!this._queueUpdateWhenVisible && !ciev) { + this._queueUpdateWhenVisible = true; + if (window.RENDER_DEBUG) { + console.error("shouldUpdate? No.", "F1.1", this.getElementName(), this.props, nextProps, this.state, nextState); } + } else if (this._queueUpdateWhenVisible && ciev) { + delete this._queueUpdateWhenVisible; + return true; } } - }; - const _histDecryptedCb = function () { - const mb = this.messagesBuff; - if (!loadingMoreChats[this.chatId] && mb.messages.length < 32 && mb.haveMoreHistory()) { - loadingMoreChats[this.chatId] = true; - mb.retrieveChatHistory(false); - } else { - this.unbind(CHAT_ONHISTDECR_RECNT); - _calculateLastTsFor(this, 32); - delete loadingMoreChats[this.chatId]; - finishedLoadingChats[this.chatId] = true; - mb.detachMessages(); + if (this.specShouldComponentUpdate) { + const r = this.specShouldComponentUpdate(nextProps, nextState); + if (r === false) { + if (window.RENDER_DEBUG) { + console.error("shouldUpdate? No.", "F2", this.getElementName(), this.props, nextProps, this.state, nextState); + } + this._requiresUpdateOnResize = true; + return false; + } else if (r === true) { + return true; + } } - }; - const _checkFinished = function (chatId) { - return function () { - return finishedLoadingChats[chatId] === true; - }; - }; - chats.forEach(chatRoom => { - const name = `getFrequentContacts(${chatRoom.roomId})`; - if (chatRoom.isLoading()) { - finishedLoadingChats[chatRoom.chatId] = false; - chatRoom.rebind(CHAT_ONHISTDECR_RECNT, _histDecryptedCb); - promises.push(createTimeoutPromise(_checkFinished(chatRoom.chatId), 300, 10000, false, name)); - } else if (chatRoom.messagesBuff.messages.length < 32 && chatRoom.messagesBuff.haveMoreHistory()) { - loadingMoreChats[chatRoom.chatId] = true; - finishedLoadingChats[chatRoom.chatId] = false; - chatRoom.messagesBuff.retrieveChatHistory(false); - chatRoom.rebind(CHAT_ONHISTDECR_RECNT, _histDecryptedCb); - promises.push(createTimeoutPromise(_checkFinished(chatRoom.chatId), 300, 15000, false, name)); - } else { - _calculateLastTsFor(chatRoom, 32); + if (!this.props.disableCheckingVisibility && !this.isComponentEventuallyVisible()) { + if (window.RENDER_DEBUG) { + console.error("shouldUpdate? No.", "FVis", this.getElementName(), this.props, nextProps, this.state, nextState); + } + this._requiresUpdateOnResize = true; + return false; } - }); - Chat._frequentsCache = new Promise((resolve, reject) => { - Promise.allSettled(promises).then(() => { - const result = Object.values(recentContacts).sort((a, b) => a.ts < b.ts ? 1 : b.ts < a.ts ? -1 : 0).reverse(); - tSleep(300).then(() => { - delete Chat._frequentsCache; - }); - return result; - }).then(resolve).catch(reject); - }); - return Chat._frequentsCache; -}; -Chat.prototype.lastRoomContacts = async function (chatRoom) { - let timeout; - let loaded = false; - let loadMore = false; - const { - promise - } = mega; - const proc = () => { - if (timeout) { - timeout.abort(); + if (this.props !== null) { + shouldRerender = this._recursiveSearchForDataChanges("p", nextProps, this.props); } - const { - messages - } = chatRoom.messagesBuff; - const arr = messages.slice(Math.max(0, messages.length - 32)); - let first = ''; - let second = ''; - for (let i = arr.length; i--;) { - const message = arr[i]; - const h = message.userId === mega.BID && message.meta ? message.meta.userId : message.userId; - if (h !== mega.BID && h !== strongvelope.COMMANDER && h !== u_handle && h in M.u && M.u[h].c === 1) { - if (first && first !== h) { - second = h; - break; - } - first = h; + if (shouldRerender === false) { + if (window.RENDER_DEBUG) { + console.error("shouldUpdate? No.", "F3", this.getElementName(), this.props, nextProps, this.state, nextState); } } - if (second) { - promise.resolve([first, second]); - } else if (first) { - promise.resolve([first]); - } else { - promise.resolve([]); + if (shouldRerender === false && this.state !== null) { + shouldRerender = this._recursiveSearchForDataChanges("s", nextState, this.state); } - chatRoom.messagesBuff.detachMessages(); - }; - const next = () => { - if (!loadMore && chatRoom.messagesBuff.messages.length < 32 && chatRoom.messagesBuff.haveMoreHistory()) { - if (timeout) { - timeout.restart(); + if (window.RENDER_DEBUG) { + if (shouldRerender) {} + console.error("shouldRerender?", shouldRerender, "rendered: ", this.getElementName(), "props:", this.props, "nextProps:", this.props, "state:", this.state); + } + if (shouldRerender === true) { + if (this.props) { + this._recurseAddListenersIfNeeded("p", this.props); + } + if (this.state) { + this._recurseAddListenersIfNeeded("s", this.state); } - loadMore = true; - chatRoom.messagesBuff.retrieveChatHistory(false); } else { - chatRoom.off('onHistoryDecrypted.lrc'); - proc(); + if (window.RENDER_DEBUG) { + console.error("shouldUpdate? No.", "F4", this.getElementName(), this.props, nextProps, this.state, nextState); + } } - }; - if (chatRoom.isLoading()) { - loaded = false; - chatRoom.rebind('onHistoryDecrypted.lrc', next); - timeout = tSleep(10); - } else if (chatRoom.messagesBuff.messages.length < 32 && chatRoom.messagesBuff.haveMoreHistory()) { - loaded = false; - loadMore = true; - chatRoom.rebind('onHistoryDecrypted.lrc', next); - chatRoom.messagesBuff.retrieveChatHistory(false); - timeout = tSleep(10); - } else { - proc(); + return shouldRerender; } - if (timeout) { - timeout.then(() => { - if (!loaded) { - chatRoom.off('onHistoryDecrypted.lrc'); - promise.resolve([]); - } - }); + onPropOrStateUpdated() { + this.eventuallyUpdate(); } - return promise; -}; -Chat.prototype.eventuallyAddDldTicketToReq = function (req) { - if (!u_handle) { - return; + getElementName() { + return this._reactInternalFiber.elementType.name; } - const currentRoom = this.getCurrentRoom(); - if (currentRoom && currentRoom.type === "public" && currentRoom.publicChatHandle && (is_chatlink || currentRoom.membersSetFromApi && !currentRoom.membersSetFromApi.members[u_handle])) { - req.cauth = currentRoom.publicChatHandle; + safeForceUpdate() { + if (this.__isMounted) { + this.forceUpdate(); + } } -}; -Chat.prototype.removeMessagesByRetentionTime = function (chatId) { - if (this.chats.length > 0) { - if (chatId) { - if (this.logger && d > 3) { - this.logger.debug(`Chat.prototype.removeMessagesByRetentionTime chatId=${chatId}`); + componentDidUpdate() { + if (window.RENDER_DEBUG) { + const self = this; + const getElementName = function () { + if (!self.constructor) { + return "unknown"; + } + return self.constructor.name; + }; + console.error("renderedX: ", getElementName(), "props:", this.props, "state:", this.state); + } + if (this.domNode && !this.domNode.isConnected) { + delete this.domNode; + } + } + UNSAFE_componentWillReceiveProps(nextProps, nextContext) { + if (localStorageProfileRenderFns) { + const self = this; + const componentName = self.constructor ? self.constructor.name : "unknown"; + if (!this._wrappedRender) { + FUNCTIONS.forEach((fnName) => { + const _origFn = self[fnName]; + if (_origFn) { + self[fnName] = function () { + const start = performance.now(); + const res = _origFn.apply(this, arguments); + REACT_RENDER_CALLS[`${componentName }.${ fnName}`] = REACT_RENDER_CALLS[`${componentName }.${ fnName}`] || 0; + REACT_RENDER_CALLS[`${componentName }.${ fnName}`] += performance.now() - start; + return res; + }; + } + }); + self._wrappedRender = true; } - const room = this.getChatById(chatId); - if (room) { - room.removeMessagesByRetentionTime(); + REACT_RENDER_CALLS.sorted = function () { + const sorted = []; + Object.keys(REACT_RENDER_CALLS).sort((a, b) => { + if (REACT_RENDER_CALLS[a] < REACT_RENDER_CALLS[b]) { + return 1; + } else if (REACT_RENDER_CALLS[a] > REACT_RENDER_CALLS[b]) { + return -1; + } else { + return 0; + } + }).forEach((k) => { + if (typeof REACT_RENDER_CALLS[k] !== 'function') { + sorted.push([k, REACT_RENDER_CALLS[k]]); + } + }); + return sorted; + }; + REACT_RENDER_CALLS.clear = function () { + Object.keys(REACT_RENDER_CALLS).forEach((k) => { + if (typeof REACT_RENDER_CALLS[k] !== 'function') { + delete REACT_RENDER_CALLS[k]; + } + }); + }; + } + } + _internalDetachRenderCallbacks() { + const items = this._dataStructListeners || false; + for (let i = items.length; i--;) { + const item = items[i]; + if (item[0] === 'dsprops') { + console.assert(item[2].removeChangeListener(item[1]), 'listener not found..'); } + } + } + addDataStructListenerForProperties(obj, properties) { + if (!(obj instanceof MegaDataMap)) { return; } - const chatIds = this.chats.keys(); - for (let i = 0; i < chatIds.length; i++) { - const chatRoom = this.chats[chatIds[i]]; - if (chatRoom.retentionTime > 0 && chatRoom.state === ChatRoom.STATE.READY) { - if (this.logger && d > 3) { - this.logger.debug(`Chat.prototype.removeMessagesByRetentionTime roomId=${chatRoom.roomId}`); - } - chatRoom.removeMessagesByRetentionTime(); - } + if (!this._dataStructListeners) { + this._dataStructListeners = []; } + properties = array.to.object(properties); + const id = obj.addChangeListener((obj, data, k) => properties[k] && this.onPropOrStateUpdated()); + this._dataStructListeners.push(['dsprops', id, obj]); } -}; -Chat.prototype.loginOrRegisterBeforeJoining = function (chatHandle, forceRegister, forceLogin, notJoinReq, onLoginSuccessCb) { - if (!chatHandle && page !== 'securechat' && (page === 'chat' || page.indexOf('chat') > -1)) { - chatHandle = getSitePath().split("chat/")[1].split("#")[0]; +}, (0,_babel_runtime_helpers_applyDecoratedDescriptor0__ .A)(_class.prototype, "componentWillUnmount", [_dec], Object.getOwnPropertyDescriptor(_class.prototype, "componentWillUnmount"), _class.prototype), (0,_babel_runtime_helpers_applyDecoratedDescriptor0__ .A)(_class.prototype, "debouncedForceUpdate", [_dec2], Object.getOwnPropertyDescriptor(_class.prototype, "debouncedForceUpdate"), _class.prototype), (0,_babel_runtime_helpers_applyDecoratedDescriptor0__ .A)(_class.prototype, "componentDidMount", [_dec3], Object.getOwnPropertyDescriptor(_class.prototype, "componentDidMount"), _class.prototype), (0,_babel_runtime_helpers_applyDecoratedDescriptor0__ .A)(_class.prototype, "eventuallyUpdate", [_dec4], Object.getOwnPropertyDescriptor(_class.prototype, "eventuallyUpdate"), _class.prototype), (0,_babel_runtime_helpers_applyDecoratedDescriptor0__ .A)(_class.prototype, "onResizeDoUpdate", [_dec5], Object.getOwnPropertyDescriptor(_class.prototype, "onResizeDoUpdate"), _class.prototype), _class); +class ContactAwareComponent extends MegaRenderMixin { + constructor(props) { + super(props); + this.loadContactInfo(); } - assert(chatHandle, 'missing chat handle when calling megaChat.loginOrRegisterBeforeJoining'); - const chatRoom = megaChat.getCurrentRoom(); - const chatKey = `#${ window.location.hash.split("#").pop()}`; - const finish = function (stay) { - if (!notJoinReq) { - localStorage.autoJoinOnLoginChat = JSON.stringify([chatHandle, unixtime(), chatKey, chatRoom.chatId]); + _validContact() { + const { + contact + } = this.props; + if (!contact) { + return false; } - if (!stay) { - window.location.reload(); + return (contact.h || contact.u) in M.u; + } + _attachRerenderCbContacts(others) { + if (!this._validContact()) { + return; } - return stay; - }; - const doShowLoginDialog = function () { - mega.ui.showLoginRequiredDialog({ - minUserType: 3, - skipInitialDialog: 1, - onLoginSuccessCb - }).done(() => { - if (page !== 'login' && onLoginSuccessCb) { - onLoginSuccessCb(); - } - }); - }; - const doShowRegisterDialog = function () { - mega.ui.showRegisterDialog({ - title: l[5840], - onCreatingAccount () {}, - onLoginAttemptFailed () { - msgDialog(`warninga:${ l[171]}`, l[1578], l[218], null, (e) => { - if (e) { - $('.pro-register-dialog').addClass('hidden'); - if (signupPromptDialog) { - signupPromptDialog.hide(); - } - doShowLoginDialog(); - } - }); - }, - onAccountCreated (gotLoggedIn, registerData) { - if (finish(!gotLoggedIn)) { - security.register.cacheRegistrationData(registerData); - mega.ui.sendSignupLinkDialog(registerData); - megaChat.destroy(); - } - }, - onLoginSuccessCb - }); - }; - if (u_handle && u_handle !== "AAAAAAAAAAA") { - return finish(); - } - if (forceRegister) { - return doShowRegisterDialog(); - } else if (forceLogin) { - return doShowLoginDialog(); + this.addDataStructListenerForProperties(this.props.contact, ['name', 'firstName', 'lastName', 'nickname', 'm', 'avatar'].concat(Array.isArray(others) ? others : [])); } - if (u_wasloggedin()) { - doShowLoginDialog(); - } else { - doShowRegisterDialog(); + attachRerenderCallbacks() { + this._attachRerenderCbContacts(); } -}; -Chat.prototype.highlight = (text, matches, dontEscape) => { - if (text && matches) { - text = dontEscape ? text : escapeHTML(text); - const tags = []; - text = text.replace(/<[^>]+>/g, match => `@@!${ tags.push(match) - 1 }!@@`).split(' '); - const done = []; - for (let i = 0; i < matches.length; i++) { - const match = matches[i].str; - if (!done.includes(match)) { - done.push(match); - for (let j = 0; j < text.length; j++) { - const word = text[j]; - const wordNormalized = ChatSearch._normalize_str(word); - const matchPos = wordNormalized.indexOf(match); - if (matchPos > -1) { - const split = wordNormalized.split(match); - text[j] = wordNormalized === word ? split.join(`[$]${match}[/$]`) : megaChat._highlightDiacritics(word, matchPos, split, match); - } - } + loadContactInfo() { + let _contact$avatar; + if (!this._validContact()) { + return; + } + const { + contact, + chatRoom + } = this.props; + const contactHandle = contact.h || contact.u; + const syncName = !ContactAwareComponent.unavailableNames[contactHandle] && !contact.firstName && !contact.lastName; + const syncMail = megaChat.FORCE_EMAIL_LOADING || (contact.c === 1 || contact.c === 2) && !contact.m && !is_chatlink; + const syncAvtr = (is_chatlink && (!contact.avatar || ((_contact$avatar = contact.avatar) == null ? void 0 : _contact$avatar.type) === "text") || !contact.avatar) && !avatars[contactHandle] && !ContactAwareComponent.unavailableAvatars[contactHandle]; + const loader = () => { + if (!this.isComponentEventuallyVisible()) { + this.__isLoadingContactInfo = null; + this._requiresUpdateOnResize = true; + return; } + const promises = []; + const chatHandle = is_chatlink.ph || chatRoom && chatRoom.publicChatHandle; + if (syncName) { + promises.push(megaChat.plugins.userHelper.getUserName(contactHandle, chatHandle)); + } + if (syncMail) { + promises.push(M.syncContactEmail(contactHandle)); + } + if (syncAvtr) { + promises.push(useravatar.loadAvatar(contactHandle, chatHandle).catch(() => { + ContactAwareComponent.unavailableAvatars[contactHandle] = true; + })); + } + return Promise.allSettled(promises).always(() => { + this.eventuallyUpdate(); + this.__isLoadingContactInfo = false; + if (!contact.firstName && !contact.lastName) { + ContactAwareComponent.unavailableNames[contactHandle] = true; + } + }); + }; + if (syncName || syncMail || syncAvtr) { + (this.__isLoadingContactInfo = tSleep(0.3)).then(loader).catch(dump); } - text = text.join(' ').replace(/\@\@\!\d+\!\@\@/g, match => { - return tags[parseInt(match.replace("@@!", "").replace("!@@"), 10)]; - }); - return text.replace(/\[\$]/g, '').replace(/\[\/\$]/g, ''); - } - return null; -}; -Chat.prototype._highlightDiacritics = function (word, matchPos, split, match) { - const parts = []; - const origMatch = word.substring(matchPos, matchPos + match.length); - let pos = 0; - for (let k = 0; k < split.length; k++) { - parts.push(word.substring(pos, pos + split[k].length)); - pos = pos + split[k].length + match.length; } - return parts.join(`[$]${origMatch}[/$]`); -}; -Chat.prototype.html = function (content) { - if (content) { - return this.plugins.emoticonsFilter.processHtmlMessage(escapeHTML(content)); + componentDidUpdate() { + super.componentDidUpdate(); + if (this.__isLoadingContactInfo === null) { + this.loadContactInfo(); + } } - return ''; -}; -Chat.prototype.updateKeysInProtocolHandlers = function () { - this.chats.forEach(r => { - const ph = r.protocolHandler; - if (ph) { - ph.reinitWithNewData(u_handle, u_privCu25519, u_privEd25519, u_pubEd25519, ph.chatMode); + componentWillUnmount() { + super.componentWillUnmount(); + if (this.__isLoadingContactInfo) { + this.__isLoadingContactInfo.abort(); + this.__isLoadingContactInfo = false; } - }); -}; -Chat.prototype.eventuallyInitMeetingUI = function () { - if (!window.location.hash) { - return; } - let loc = page.split("#")[0]; - loc = loc.replace("fm/", "/"); - if (loc.indexOf("chat/") === 0) { - this.initialPubChatHandle = loc.substr(5).split("?")[0]; + isLoadingContactInfo() { + return !!this.__isLoadingContactInfo; } +} +ContactAwareComponent.unavailableAvatars = Object.create(null); +ContactAwareComponent.unavailableNames = Object.create(null); + + }, + + 8022 +(_, EXP_, REQ_) { + +"use strict"; + REQ_.d(EXP_, { + BE: () => ContactFingerprint, + U5: () => MembersAmount, + bq: () => ContactButton, + eu: () => Avatar, + hU: () => ContactPickerWidget, + hm: () => ContactPickerDialog, + i1: () => ContactPresence, + lO: () => MAX_FREQUENTS, + n4: () => ContactVerified, + nB: () => ContactCard, + uA: () => ContactAwareName + }); + + const _babel_runtime_helpers_extends0__ = REQ_(8168); + const react1__ = REQ_(1594); + const react1___default = REQ_.n(react1__); + const _mixins2__ = REQ_(8264); + const _ui_utils_jsx3__ = REQ_(6411); + const _ui_perfectScrollbar_jsx4__ = REQ_(1301); + const _ui_buttons_jsx5__ = REQ_(5155); + const _ui_dropdowns_jsx6__ = REQ_(1510); + const _ui_modalDialogs7__ = REQ_(8120); + const _link_jsx8__ = REQ_(4649); + const _updateObserver_jsx9__ = REQ_(4372); + const _contactsPanel_utils_jsx10__ = REQ_(836); + + + + + + + + + + + + +const MAX_FREQUENTS = 3; +const closeDropdowns = () => { + document.dispatchEvent(new Event('closeDropdowns')); }; -Chat.prototype.enqueueChatRoomEvent = function (eventName, eventData) { - if (!this.is_initialized) { - return; +class ContactButton extends _mixins2__ .u9 { + constructor(props) { + super(props); + this.dropdownItemGenerator = this.dropdownItemGenerator.bind(this); } - const { - chatId - } = eventData; - if (!this._queuedChatRoomEvents[chatId]) { - this._queuedChatRoomEvents[chatId] = []; - (this._queuedChatRoomEvents[chatId].timer = tSleep(15)).then(() => { - if (d) { - this.logger.warn('Timer ran out, events lost...', this._queuedChatRoomEvents[chatId]); - } - delete this._queuedChatRoomEvents[chatId]; - }); + customIsEventuallyVisible() { + if (this.props.chatRoom) { + return this.props.chatRoom.isCurrentlyActive; + } + return -1; } - this._queuedChatRoomEvents[chatId].push([eventName, eventData]); -}; -Chat.prototype.autoJoinIfNeeded = function () { - const rawAutoLoginInfo = localStorage.autoJoinOnLoginChat; - if (u_type && rawAutoLoginInfo) { - const autoLoginChatInfo = tryCatch(JSON.parse.bind(JSON))(rawAutoLoginInfo) || false; - if (unixtime() - 7200 < autoLoginChatInfo[1]) { - const req = this.plugins.chatdIntegration.getMciphReqFromHandleAndKey(autoLoginChatInfo[0], autoLoginChatInfo[2].substr(1)); - megaChat.rebind('onRoomInitialized.autoJoin', (e, megaRoom) => { - if (megaRoom.chatId === autoLoginChatInfo[3]) { - megaRoom.setActive(); - megaChat.unbind('onRoomInitialized.autoJoin'); - localStorage.removeItem("autoJoinOnLoginChat"); + dropdownItemGenerator() { + let { + contact, + dropdowns, + chatRoom, + dropdownRemoveButton + } = this.props; + dropdowns = dropdowns ? dropdowns : []; + const moreDropdowns = []; + moreDropdowns.push(JSX_("div", { + className: "dropdown-avatar rounded", + key: "mainContactInfo", + onClick: () => { + if (contact.c === 2) { + loadSubPage('fm/account'); } - }); - asyncApiReq(req).catch(dump); - } else { - localStorage.removeItem("autoJoinOnLoginChat"); + if (contact.c === 1) { + loadSubPage(`fm/chat/contacts/${ contact.u}`); + } + } + }, JSX_(Avatar, { + className: "avatar-wrapper context-avatar", + chatRoom, + contact, + hideVerifiedBadge: "true" + }), JSX_("div", { + className: "dropdown-user-name" + }, JSX_("div", { + className: "name" + }, JSX_(ContactAwareName, { + overflow: true, + contact + }), JSX_(ContactPresence, { + className: "small", + contact + })), contact && (megaChat.FORCE_EMAIL_LOADING || contact.c === 1 || contact.c === 2) && JSX_("span", { + className: "email" + }, contact.m)))); + moreDropdowns.push(JSX_(ContactFingerprint, { + key: "fingerprint", + contact + })); + if (dropdowns.length && contact.c !== 2) { + moreDropdowns.push(dropdowns); + moreDropdowns.push(JSX_("hr", { + key: "top-separator" + })); } - } -}; -Chat.prototype.openScheduledMeeting = function (meetingId, toCall) { - const meeting = this.scheduledMeetings[meetingId]; - if (!meeting) { - console.warn('Meeting does not exist', meetingId); - return; - } - window.focus(); - meeting.chatRoom.activateWindow(); - meeting.chatRoom.show(); - if (toCall && this.hasSupportForCalls) { - this.openScheduledMeeting._queue = this.openScheduledMeeting._queue || []; - this.openScheduledMeeting._queue.push(meetingId); - delay('megachat:openScheduledMeetingCall', () => { - const meetingId = this.openScheduledMeeting._queue[0]; - delete this.openScheduledMeeting._queue; - const meetingRoom = this.scheduledMeetings[meetingId].chatRoom; - meetingRoom.activateWindow(); - meetingRoom.show(); - const haveCall = this.haveAnyActiveCall(); - if (haveCall && window.sfuClient) { - const { - chatRoom - } = this.activeCall; - if (chatRoom && chatRoom.chatId === meetingRoom.chatId) { - const peers = chatRoom.getCallParticipants(); - if (peers.includes(u_handle)) { - return d && console.warn('Already in this call'); + if (contact.u === u_handle) { + moreDropdowns.push(JSX_(_ui_dropdowns_jsx6__ .tJ, { + key: "view0", + icon: "sprite-fm-mono icon-user-filled", + label: l[187], + onClick: () => loadSubPage('fm/account') + })); + } + if (contact.c === 1) { + const startAudioCall = () => { + megaChat.createAndShowPrivateRoom(contact.u).then(room => { + room.setActive(); + room.startAudioCall(); + }); + }; + if (megaChat.currentlyOpenedChat && megaChat.currentlyOpenedChat === contact.u) { + moreDropdowns.push(JSX_("div", { + key: "startAudioVideoCall", + "data-simpletipposition": "top", + className: "simpletip", + "data-simpletip": !megaChat.hasSupportForCalls ? l.call_not_suported : '' + }, JSX_(_ui_dropdowns_jsx6__ .tJ, { + disabled: !megaChat.hasSupportForCalls, + key: "startCall", + className: "sprite-fm-mono-before icon-arrow-right-before", + icon: "sprite-fm-mono icon-phone", + submenu: megaChat.hasSupportForCalls, + label: l[19125] + }), JSX_("div", { + className: "dropdown body submenu", + key: "dropdownGroup" + }, JSX_("div", null, JSX_(_ui_dropdowns_jsx6__ .tJ, { + key: "startAudio", + icon: "sprite-fm-mono icon-phone", + disabled: !megaChat.hasSupportForCalls, + label: l[1565], + onClick: startAudioCall + })), JSX_("div", null, JSX_(_ui_dropdowns_jsx6__ .tJ, { + key: "startVideo", + icon: "sprite-fm-mono icon-video-call-filled", + disabled: !megaChat.hasSupportForCalls, + label: l[1566], + onClick: () => { + megaChat.createAndShowPrivateRoom(contact.u).then(room => { + room.setActive(); + room.startVideoCall(); + }); + } + }))))); + } else { + moreDropdowns.push(JSX_(_ui_dropdowns_jsx6__ .tJ, { + key: "startChat", + icon: "sprite-fm-mono icon-chat", + label: l[5885], + onClick: () => { + loadSubPage(`fm/chat/p/${ contact.u}`); + } + })); + } + moreDropdowns.push(JSX_("hr", { + key: "files-separator" + })); + moreDropdowns.push(JSX_(_ui_dropdowns_jsx6__ .tJ, { + key: "send-files-item", + icon: "sprite-fm-mono icon-send-files", + label: l[6834], + disabled: mega.paywall, + onClick: () => { + megaChat.openChatAndSendFilesDialog(contact.u); + } + })); + moreDropdowns.push(JSX_(_ui_dropdowns_jsx6__ .tJ, { + key: "share-item", + icon: "sprite-fm-mono icon-folder-outgoing-share", + label: l[6775], + onClick: () => { + openCopyShareDialog(contact.u); + } + })); + } else if (!is_chatlink && !is_eplusplus && (!contact.c || contact.c === 2 && contact.u !== u_handle)) { + moreDropdowns.push(JSX_(_ui_dropdowns_jsx6__ .tJ, { + key: "view2", + icon: "sprite-fm-mono icon-add", + label: l[101], + onClick: () => { + const isAnonymousUser = !u_handle || u_type !== 3; + const ADD_CONTACT = 'addContact'; + if (is_chatlink && isAnonymousUser) { + megaChat.loginOrRegisterBeforeJoining(undefined, undefined, undefined, true); + if (localStorage.getItem(ADD_CONTACT) === null) { + localStorage.setItem(ADD_CONTACT, JSON.stringify({ + u: contact.u, + unixTime: unixtime() + })); + } + } else { + loadingDialog.show(); + M.syncContactEmail(contact.u, true).then(email => { + if (Object.values(M.opc || {}).some(cr => cr.m === email)) { + closeDialog(); + msgDialog('warningb', '', l[17545]); + } else { + M.inviteContact(M.u[u_handle].m, email); + const title = l[150]; + const msg = l[5898].replace('[X]', email); + closeDialog(); + msgDialog('info', title, msg.replace('[X]', email)); + } + }).catch(() => { + const { + chatRoom + } = this.props; + const { + u: userHandle + } = contact; + if (chatRoom.call) { + return mBroadcaster.sendMessage('meetings:ephemeralAdd', userHandle); + } + const name = M.getNameByHandle(userHandle); + return msgDialog('info', '', l.ephemeral_title ? l.ephemeral_title.replace('%1', name) : `${name} is using an ephemeral session.`, l.ephemeral_info); + }).finally(() => loadingDialog.hide()); } } + })); + } + if (u_attr && contact.u !== u_handle) { + if (moreDropdowns.length > 0 && !(moreDropdowns.length === 2 && moreDropdowns[1] && moreDropdowns[1].key === "fingerprint")) { + moreDropdowns.push(JSX_("hr", { + key: "nicknames-separator" + })); } - (0,call.dQ)(true, meetingRoom).then(() => meetingRoom.startAudioCall(true)).catch(ex => d && console.warn('Already in a call.', ex)); - }); + moreDropdowns.push(JSX_(_ui_dropdowns_jsx6__ .tJ, { + key: "set-nickname", + icon: "sprite-fm-mono icon-rename", + label: contact.nickname === '' ? l.set_nickname_label : l.edit_nickname_label, + onClick: () => nicknames.setNicknameDialog.init(contact.u) + })); + } + if (dropdownRemoveButton && dropdownRemoveButton.length) { + moreDropdowns.push(JSX_("hr", { + key: "remove-separator" + })); + moreDropdowns.push(dropdownRemoveButton); + } + return moreDropdowns; + } + render() { + let { + label = '', + className = '', + contact, + dropdownIconClasses = [], + verticalOffset, + dropdownDisabled, + noLoading, + noContextMenu + } = this.props; + let dropdownPosition = "left top"; + let vertOffset = 0; + let horizOffset = -30; + if (!contact) { + return null; + } + if (label) { + className = `user-card-name ${className}${className.includes('message') ? '' : ' selectable-txt'}`; + dropdownIconClasses = ''; + dropdownPosition = 'left bottom'; + vertOffset = 25; + horizOffset = 0; + } + if (typeof verticalOffset !== 'undefined') { + vertOffset = verticalOffset; + } + if (!contact.name && !contact.m && !noLoading && this.isLoadingContactInfo()) { + label = JSX_("em", { + className: "contact-name-loading" + }); + className = `contact-button-loading ${className}`; + } + return noContextMenu ? JSX_("div", { + className: "user-card-name light selectable-txt" + }, label) : JSX_(_ui_buttons_jsx5__ .$, { + className, + icon: dropdownIconClasses, + disabled: dropdownDisabled, + label + }, JSX_(_ui_dropdowns_jsx6__ .ms, { + className: "context contact-card-dropdown", + positionMy: dropdownPosition, + positionAt: dropdownPosition, + vertOffset, + horizOffset, + dropdownItemGenerator: this.dropdownItemGenerator, + noArrow: true + })); } +} +ContactButton.defaultProps = { + 'manualDataChangeTracking': true, + 'skipQueuedUpdatesOnResize': true }; -Chat.prototype.playSound = tryCatch((sound, options, stop) => { - if (options === true) { - stop = true; - options = undefined; +class ContactVerified extends _mixins2__ .w9 { + attachRerenderCallbacks() { + this.addDataStructListenerForProperties(this.props.contact, ['fingerprint']); } - if (stop) { - ion.sound.stop(sound); + render() { + if (is_chatlink) { + return null; + } + const {contact} = this.props; + if (!contact) { + return null; + } + if (u_authring && u_authring.Ed25519) { + const verifyState = u_authring.Ed25519[contact.u] || {}; + if (verifyState.method >= authring.AUTHENTICATION_METHOD.FINGERPRINT_COMPARISON) { + return JSX_("div", { + className: ` + user-card-verified + ${this.props.className || ''} + ` + }); + } + } else if (!pubEd25519[contact.u]) { + crypt.getPubEd25519(contact.u).then(() => { + if (pubEd25519[contact.u]) { + this.safeForceUpdate(); + } + }); + } + return null; } - return ion.sound.play(sound, options); -}); -Chat.prototype.fetchSoundBuffer = async function (sound) { - if (this.SOUNDS.buffers[sound]) { - return this.SOUNDS.buffers[sound].slice(); +} +ContactVerified.defaultProps = { + 'manualDataChangeTracking': true, + 'skipQueuedUpdatesOnResize': true +}; +class ContactPresence extends _mixins2__ .w9 { + constructor(...args) { + super(...args); + this.domRef = react1___default().createRef(); } - let res = await M.xhr({ - url: `${staticpath}media/${sound}.mp3`, - type: 'arraybuffer' - }).catch(() => { - console.warn('Failed to fetch sound .mp3 file', sound); - }); - if (!res) { - res = await M.xhr({ - url: `${staticpath}media/${sound}.ogg`, - type: 'arraybuffer' - }).catch(() => { - console.error('Failed to fetch sound .ogg file', sound); - }); - } - if (!res) { - throw ENOENT; + attachRerenderCallbacks() { + this.addDataStructListenerForProperties(this.props.contact, ['presence']); } - this.SOUNDS.buffers[sound] = res.target.response.slice(); - return res.target.response; -}; -window.Chat = Chat; -if (false) // removed by dead control flow -{} -const chat = { - Chat -}; - -}, - -623 -() { - -(function () { - const ChatGlobalEventManager = function () {}; - lazy(ChatGlobalEventManager.prototype, 'listeners', function () { - window.addEventListener('hashchange', ev => this.triggered(ev)); - $(window).rebind('resize.chatGlobalEventManager', ev => this.triggered(ev)); - const listeners = Object.create(null); - listeners.resize = Object.create(null); - listeners.hashchange = Object.create(null); - return listeners; - }); - ChatGlobalEventManager.prototype.addEventListener = function (eventName, namespace, cb) { - this.listeners[eventName][namespace] = this.listeners[namespace] || cb; - }; - ChatGlobalEventManager.prototype.removeEventListener = function (eventName, namespace) { - delete this.listeners[eventName][namespace]; - }; - ChatGlobalEventManager.prototype.triggered = SoonFc(140, function _chatEVDispatcher(ev) { - if (M.chat) { - const listeners = this.listeners[ev.type]; - for (const k in listeners) { - listeners[k](ev); - } + render() { + const { + contact, + className + } = this.props; + if (!contact || !contact.c) { + return null; } - }); - window.chatGlobalEventManager = new ChatGlobalEventManager(); -})(); - -}, - -553 -(_, EXP_, REQ_) { - -"use strict"; -// ESM COMPAT FLAG -REQ_.r(EXP_); - -// EXPORTS -REQ_.d(EXP_, { - MCO_FLAGS: () => MCO_FLAGS, - RETENTION_FORMAT: () => RETENTION_FORMAT, - "default": () => chatRoom -}); - -;// ./js/chat/utils.jsx -async function prepareExportIo(dl) { - const { - zname, - size - } = dl; - if (window.isSecureContext && typeof showSaveFilePicker === 'function' && typeof FileSystemFileHandle !== 'undefined' && 'createWritable' in FileSystemFileHandle.prototype && typeof FileSystemWritableFileStream !== 'undefined' && 'seek' in FileSystemWritableFileStream.prototype) { - const file = await window.showSaveFilePicker({ - suggestedName: zname - }).catch(ex => { - if (String(ex).includes('aborted')) { - throw new Error('Aborted'); - } - dump(ex); + return JSX_("div", { + ref: this.domRef, + className: ` + user-card-presence + ${megaChat.userPresenceToCssClass(contact.presence)} + ${className || ''} + ` }); - if (file) { - const stream = await file.createWritable().catch(dump); - if (stream) { - return { - stream, - write (data, position, done) { - this.stream.write({ - type: 'write', - position, - data - }).then(done).catch(dump); - }, - download () { - this.abort(); - }, - abort () { - this.stream.close(); - }, - setCredentials () { - this.begin(); - } - }; - } - } - } - if (MemoryIO.usable() && Math.min(MemoryIO.fileSizeLimit, 94371840) > size) { - return new MemoryIO('chat_0', dl); - } else if (window.requestFileSystem) { - return new FileSystemAPI('chat_0', dl); } - throw new Error('Download methods are unsupported'); -} -function prepareExportStreams(attachNodes, onEmpty) { - return attachNodes.map(node => { - return { - name: node.name, - lastModified: new Date((node.mtime || node.ts) * 1000), - input: M.gfsfetch.getReadableStream(node, { - error(ex, n) { - if (d) { - console.error(`${n.h}: ${ex}`); - } - onEmpty(n.s); - } - }) - }; - }); } -;// ./js/chat/chatRoom.jsx - -const RETENTION_FORMAT = { - HOURS: 'hour', - DAYS: 'day', - WEEKS: 'week', - MONTHS: 'month', - DISABLED: 'none' -}; -const MCO_FLAGS = { - OPEN_INVITE: 'oi', - SPEAK_REQUEST: 'sr', - WAITING_ROOM: 'w' +ContactPresence.defaultProps = { + manualDataChangeTracking: true, + skipQueuedUpdatesOnResize: true }; -window.RETENTION_FORMAT = RETENTION_FORMAT; -window.MCO_FLAGS = MCO_FLAGS; -const ChatRoom = function (megaChat, roomId, type, users, ctime, lastActivity, chatId, chatShard, chatdUrl, noUI, publicChatHandle, publicChatKey, ck, isMeeting, retentionTime, mcoFlags, organiser) { - const self = this; - this.logger = MegaLogger.getLogger(`room[${ roomId }]`, {}, megaChat.logger); - this.megaChat = megaChat; - MegaDataObject.call(this, { - state: null, - users: [], - roomId: null, - type: null, - messages: [], - ctime: 0, - lastActivity: 0, - callRequest: null, - isCurrentlyActive: false, - _messagesQueue: [], - unreadCount: 0, - chatId: undefined, - chatdUrl: undefined, - chatShard: undefined, - members: {}, - membersSet: false, - membersLoaded: false, - topic: '', - flags: 0x00, - publicLink: null, - observers: 0, - dnd: null, - alwaysNotify: null, - retentionTime: 0, - activeCallIds: null, - meetingsLoading: null, - options: {}, - scheduledMeeting: undefined, - historyTimedOut: false - }); - this.roomId = roomId; - this.instanceIndex = ChatRoom.INSTANCE_INDEX++; - this.type = type; - this.ctime = ctime; - this.lastActivity = lastActivity ? lastActivity : 0; - this.chatd = megaChat.plugins.chatdIntegration.chatd; - this.chatId = chatId; - this.chatIdBin = chatId ? base64urldecode(chatId) : ""; - this.chatShard = chatShard; - this.chatdUrl = chatdUrl; - this.publicLink = null; - this.publicChatHandle = publicChatHandle; - this.publicChatKey = publicChatKey; - this.ck = ck; - this.scrolledToBottom = 1; - this.callRequest = null; - this.shownMessages = {}; - this.retentionTime = retentionTime; - this.activeSearches = 0; - this.activeCallIds = new MegaDataMap(this); - this.ringingCalls = new MegaDataMap(this); - this.isMeeting = isMeeting; - this.isNote = type === 'private' && roomId === u_handle; - this.callUserLimited = false; - this.members = Object.create(null); - Object.defineProperty(this.members, 'hasOwnProperty', { - value(p) { - return p in this; - } - }); - if (type === "private") { - users.forEach((userHandle) => { - self.members[userHandle] = 3; - }); - } else { - users.forEach((userHandle) => { - self.members[userHandle] = 0; - }); - } - this.options = {}; - mcoFlags = mcoFlags || {}; - for (const flag of Object.values(MCO_FLAGS)) { - this.options[flag] = mcoFlags[flag] || 0; +const LastActivity = (0,_mixins2__ .Zz)(_updateObserver_jsx9__ .Y)((() => class LastActivity extends _mixins2__ .u9 { + attachRerenderCallbacks() { + this._attachRerenderCbContacts(['ats', 'lastGreen', 'presence']); } - this.organiser = organiser; - this.setState(ChatRoom.STATE.INITIALIZED); - this.isCurrentlyActive = false; - if (d) { - this.rebind('onStateChange.chatRoomDebug', (e, oldState, newState) => { - self.logger.debug("Will change state from: ", ChatRoom.stateToText(oldState), " to ", ChatRoom.stateToText(newState)); - }); + shouldComponentUpdate() { + return true; } - self.rebind('onStateChange.chatRoom', (e, oldState, newState) => { - if (newState === ChatRoom.STATE.READY && !self.isReadOnly() && self.chatd && self.isOnline() && self.chatIdBin) { - if (d > 2) { - self.logger.warn('Restoring persisted messages...', self.type, self.isCurrentlyActive); - } - const cim = self.getChatIdMessages(); - cim.restore(true); + render() { + const { + contact, + showLastGreen + } = this.props; + if (!contact) { + return null; } - }); - self.rebind('onMessagesBuffAppend.lastActivity', (e, msg) => { - if (is_chatlink || self.isNote) { - return; + const lastActivity = !contact.ats || contact.lastGreen > contact.ats ? contact.lastGreen : contact.ats; + const SECONDS = Date.now() / 1000 - lastActivity; + const timeToLast = SECONDS > 3888000 ? l[20673] : time2last(lastActivity, true); + const hasActivityStatus = showLastGreen && contact.presence <= 2 && lastActivity; + return JSX_("span", null, hasActivityStatus ? (l[19994] || 'Last seen %s').replace('%s', timeToLast) : M.onlineStatusClass(contact.presence)[0]); + } +})()); +class ContactAwareName extends _mixins2__ .u9 { + render() { + const { + contact, + emoji, + overflow + } = this.props; + if (!contact || !M.u[contact.u || contact.h]) { + return null; } - const ts = msg.delay ? msg.delay : msg.ts; - if (!ts) { - return; + const name = M.getNameByHandle(contact.u || contact.h); + if (emoji || overflow) { + const EmojiComponent = overflow ? _ui_utils_jsx3__ .sp : _ui_utils_jsx3__ .zT; + return JSX_(EmojiComponent, this.props, name); } - const contactForMessage = msg && Message.getContactForMessage(msg); - if (contactForMessage && contactForMessage.u !== u_handle) { - if (!contactForMessage.ats || contactForMessage.ats < ts) { - contactForMessage.ats = ts; - } + return JSX_("span", null, name); + } +} +class MembersAmount extends _mixins2__ .u9 { + render() { + const { + chatRoom + } = this.props; + return JSX_("span", null, mega.icu.format(l[20233], Object.keys(chatRoom.members).length)); + } +} +class ContactFingerprint extends _mixins2__ .w9 { + constructor(...args) { + super(...args); + this.domRef = react1___default().createRef(); + } + attachRerenderCallbacks() { + this.addDataStructListenerForProperties(this.props.contact, ['fingerprint']); + } + render() { + const { + contact, + className + } = this.props; + if (!contact || !contact.u || is_chatlink) { + return null; } - if (self.lastActivity && self.lastActivity >= ts) { - if (msg.deleted) { - const { - delay, - ts - } = self.messagesBuff.getLastMessageFromServer(); - self.lastActivity = delay || ts; + const infoBlocks = []; + userFingerprint(contact.u, (fingerprints) => { + fingerprints.forEach((v, k) => { + infoBlocks.push(JSX_("span", { + key: `fingerprint-${ k}` + }, v)); + }); + }); + let verifyButton = null; + if (contact.c === 1 && u_authring && u_authring.Ed25519) { + const verifyState = u_authring.Ed25519[contact.u] || {}; + if (typeof verifyState.method === "undefined" || verifyState.method < authring.AUTHENTICATION_METHOD.FINGERPRINT_COMPARISON) { + verifyButton = JSX_(_ui_buttons_jsx5__ .$, { + className: "dropdown-verify active", + label: l.verify_credentials, + icon: "sprite-fm-mono icon-key", + onClick: () => { + closeDropdowns(); + fingerprintDialog(contact.u); + } + }); } - return; } - self.lastActivity = ts; - if (msg.userId === u_handle) { - self.didInteraction(u_handle, ts); - return; + return infoBlocks.length ? JSX_("div", { + ref: this.domRef, + className: ` + dropdown-fingerprint + ${className || ''} + ` + }, JSX_("div", { + className: "contact-fingerprint-title" + }, JSX_("span", null, l[6872])), JSX_("div", { + className: "contact-fingerprint-txt selectable-txt" + }, infoBlocks), verifyButton) : null; + } +} +ContactFingerprint.defaultProps = { + 'manualDataChangeTracking': true, + 'skipQueuedUpdatesOnResize': true +}; +class Avatar extends _mixins2__ .u9 { + render() { + const self = this; + const {contact} = this.props; + if (!contact) { + return null; } - if (self.type === "private") { - const targetUserId = self.getParticipantsExceptMe()[0]; - let targetUserNode; - if (M.u[targetUserId]) { - targetUserNode = M.u[targetUserId]; - } else if (msg.userId) { - targetUserNode = M.u[msg.userId]; + if (!contact.m && contact.email) { + contact.m = contact.email; + } + const avatarMeta = useravatar.generateContactAvatarMeta(contact); + let classes = `${this.props.className ? this.props.className : ' avatar-wrapper small-rounded-avatar' } ${ contact.u } in-chat`; + classes += " chat-avatar"; + let displayedAvatar; + let verifiedElement = null; + if (!this.props.hideVerifiedBadge && !is_chatlink) { + verifiedElement = JSX_(ContactVerified, { + contact: this.props.contact, + className: this.props.verifiedClassName + }); + } + const extraProps = {}; + if (this.props.simpletip) { + classes += " simpletip"; + if (this.props.simpletip === true) { + extraProps['data-simpletip'] = M.getNameByHandle(contact.h || contact.u) || megaChat.plugins.userHelper.SIMPLETIP_USER_LOADER; } else { - console.error("Missing participant in a 1on1 room."); - return; + extraProps['data-simpletip'] = this.props.simpletip; } - assert(targetUserNode && targetUserNode.u, 'No hash found for participant'); - assert(M.u[targetUserNode.u], 'User not found in M.u'); - if (targetUserNode) { - self.didInteraction(targetUserNode.u, self.lastActivity); + if (this.props.simpletipWrapper) { + extraProps['data-simpletipwrapper'] = this.props.simpletipWrapper; } - } else if (self.type === "group" || self.type === "public") { - let contactHash; - if (msg.authorContact) { - contactHash = msg.authorContact.u; - } else if (msg.userId) { - contactHash = msg.userId; + if (this.props.simpletipOffset) { + extraProps['data-simpletipoffset'] = this.props.simpletipOffset; } - if (contactHash && M.u[contactHash]) { - self.didInteraction(contactHash, self.lastActivity); + if (this.props.simpletipPosition) { + extraProps['data-simpletipposition'] = this.props.simpletipPosition; + } + if (this.props.simpletipClass) { + extraProps['data-simpletip-class'] = this.props.simpletipClass; } - assert(contactHash, 'Invalid hash for user (extracted from inc. message)'); - } else { - throw new Error("Not implemented"); - } - }); - self.rebind('onMembersUpdated.coreRoomDataMngmt', (e, eventData) => { - if (self.state === ChatRoom.STATE.LEFT && eventData.priv >= 0 && eventData.priv < 255) { - self.membersLoaded = false; - self.setState(ChatRoom.STATE.JOINING, true); } - let queuedMembersUpdatedEvent = false; - if (self.membersLoaded === false) { - if (eventData.priv >= 0 && eventData.priv < 255) { - const addParticipant = function addParticipant() { - self.protocolHandler.addParticipant(eventData.userId); - self.members[eventData.userId] = eventData.priv; - ChatdIntegration._ensureContactExists([eventData.userId]); - self.trigger('onMembersUpdatedUI', eventData); - }; - if (is_chatlink) { - megaChat.initContacts([eventData.userId]); - } - ChatdIntegration._waitForProtocolHandler(self, addParticipant); - queuedMembersUpdatedEvent = true; + if (avatarMeta.type === "image") { + displayedAvatar = JSX_("div", (0,_babel_runtime_helpers_extends0__ .A)({ + className: classes, + style: this.props.style + }, extraProps, { + onClick: self.props.onClick ? e => { + closeDropdowns(); + self.props.onClick(e); + } : self.onClick + }), verifiedElement, JSX_("img", { + src: avatarMeta.avatar, + style: this.props.imgStyles + })); + } else { + classes += ` color${ avatarMeta.avatar.colorIndex}`; + const isLoading = self.isLoadingContactInfo(); + if (isLoading) { + classes += " default-bg"; } - } else if (eventData.priv === 255 || eventData.priv === -1) { - const deleteParticipant = function deleteParticipant() { - if (eventData.userId === u_handle) { - Object.keys(self.members).forEach((userId) => { - self.protocolHandler.removeParticipant(userId); - self.members[userId] = userId === u_handle ? ChatRoom.MembersSet.PRIVILEGE_STATE.LEFT : ChatRoom.MembersSet.PRIVILEGE_STATE.READONLY; - }); - } else { - self.protocolHandler.removeParticipant(eventData.userId); - delete self.members[eventData.userId]; - } - self.trigger('onMembersUpdatedUI', eventData); - }; - ChatdIntegration._waitForProtocolHandler(self, deleteParticipant); - queuedMembersUpdatedEvent = true; + displayedAvatar = JSX_("div", (0,_babel_runtime_helpers_extends0__ .A)({ + className: classes, + style: this.props.style + }, extraProps, { + onClick: self.props.onClick ? e => { + closeDropdowns(); + self.props.onClick(e); + } : self.onClick + }), verifiedElement, JSX_("span", null, isLoading ? "" : avatarMeta.avatar.letters)); } - if (eventData.userId === u_handle) { - self.membersLoaded = true; + return displayedAvatar; + } +} +Avatar.defaultProps = { + 'manualDataChangeTracking': true, + 'skipQueuedUpdatesOnResize': true +}; +class ContactCard extends _mixins2__ .u9 { + attachRerenderCallbacks() { + this._attachRerenderCbContacts(['presence']); + } + specShouldComponentUpdate(nextProps, nextState) { + const foundKeys = Object.keys(this.props); + if (foundKeys.includes('dropdowns')) { + array.remove(foundKeys, 'dropdowns', true); } - if (!queuedMembersUpdatedEvent) { - self.members[eventData.userId] = eventData.priv; - self.trigger('onMembersUpdatedUI', eventData); + let shouldUpdate; + if (foundKeys.length) { + const k = foundKeys[0]; + shouldUpdate = shallowEqual(nextProps[k], this.props[k]); } - }); - if (is_chatlink && !is_chatlink.callId && !this.options.w) { - const unbind = () => { - self.unbind('onMessagesHistoryDone.chatlinkAlreadyIn'); - self.unbind('onMembersUpdated.chatlinkAlreadyIn'); - }; - self.rebind('onMembersUpdated.chatlinkAlreadyIn', (e, eventData) => { - if (eventData.userId === u_handle && eventData.priv >= 0) { - unbind(); - return this.megaChat.routing.reinitAndOpenExistingChat(this.chatId, this.publicChatHandle); - } - }); - self.rebind('onMessagesHistoryDone.chatlinkAlreadyIn', (e, data) => { - if (!data.chatdPersist) { - unbind(); - } - }); - } - self.rebind('onMembersUpdatedUI.chatRoomMembersSync', (e, eventData) => { - if (eventData.userId === u_handle) { - self.messagesBuff.joined = true; - if (eventData.priv === 255 || eventData.priv === -1) { - if (self.state === ChatRoom.STATE.JOINING) { - self.setState(ChatRoom.STATE.LEFT); - } - } else { - if (self.state === ChatRoom.STATE.JOINING) { - self.setState(ChatRoom.STATE.READY); - } - } + if (!shouldUpdate) { + shouldUpdate = shallowEqual(nextState, this.state); } - self.trackDataChange(); - }); - self.getParticipantsExceptMe().forEach((userHandle) => { - const contact = M.u[userHandle]; - if (contact && contact.c) { - getLastInteractionWith(contact.u); + if (!shouldUpdate && this.state.props.dropdowns && nextProps.state.dropdowns && this.state.props.dropdowns.map && nextProps.state.dropdowns.map) { + const oldKeys = this.state.props.dropdowns.map(child => child.key); + const newKeys = nextProps.state.dropdowns.map(child => child.key); + if (!shallowEqual(oldKeys, newKeys)) { + shouldUpdate = true; + } } - }); - self.megaChat.trigger('onRoomCreated', [self]); - if (this.type === "public" && self.megaChat.publicChatKeys[self.chatId]) { - self.publicChatKey = self.megaChat.publicChatKeys[self.chatId]; + return shouldUpdate; } - $(window).rebind(`focus.${ self.roomId}`, () => { - if (self.isCurrentlyActive) { - self.trigger("onChatShown"); - } - }); - self.megaChat.rebind(`onRoomDestroy.${ self.roomId}`, (e, room) => { - if (room.roomId == self.roomId) { - $(window).off(`focus.${ self.roomId}`); + render() { + let _this$props$chatRoom; + const { + contact + } = this.props; + if (!contact) { + return null; } - }); - self.initialMessageHistLoaded = false; - let timer = null; - const _historyIsAvailable = ev => { - self.initialMessageHistLoaded = ev ? true : -1; - if (timer) { - timer.abort(); - timer = null; + const pres = megaChat.userPresenceToCssClass(contact.presence); + let username = (this.props.namePrefix || '') + (M.getNameByHandle(contact.u) || contact.m); + if (contact.u === u_handle) { + username += ` (${escapeHTML(l[8885])})`; } - self.unbind('onMarkAsJoinRequested.initHist'); - self.unbind('onHistoryDecrypted.initHist'); - self.unbind('onMessagesHistoryDone.initHist'); - }; - self.rebind('onHistoryDecrypted.initHist', _historyIsAvailable); - self.rebind('onMessagesHistoryDone.initHist', _historyIsAvailable); - self.rebind('onMarkAsJoinRequested.initHist', () => { - (timer = tSleep(300)).then(() => { - if (d) { - self.logger.warn("Timed out waiting to load hist for:", self.chatId || self.roomId); - } - this.historyTimedOut = true; - this.trigger('onHistTimeoutChange'); - timer = null; - _historyIsAvailable(false); - }); - }); - self.rebind('onRoomDisconnected', () => { - if (!self.call) { - for (const activeCallId of self.activeCallIds.keys()) { - self.activeCallIds.remove(activeCallId); + let escapedUsername = JSX_(_ui_utils_jsx3__ .sp, null, username); + const dropdowns = this.props.dropdowns || []; + const noContextMenu = this.props.noContextMenu || ''; + const noContextButton = this.props.noContextButton || ''; + const dropdownRemoveButton = this.props.dropdownRemoveButton || []; + const highlightSearchValue = this.props.highlightSearchValue || false; + const emailTooltips = this.props.emailTooltips || false; + const searchValue = this.props.searchValue || ""; + let usernameBlock; + if (!noContextMenu) { + usernameBlock = JSX_(ContactButton, { + key: "lnk", + dropdowns, + noContextMenu, + contact, + className: "light", + label: escapedUsername, + chatRoom: this.props.chatRoom, + dropdownRemoveButton, + verticalOffset: 0 + }); + } else { + if (highlightSearchValue && searchValue.length > 0) { + const matches = []; + const regex = new RegExp(RegExpEscape(searchValue), 'gi'); + let result; + while (result = regex.exec(username)) { + matches.push({ + idx: result.index, + str: result[0] + }); + } + if (matches.length > 0) { + escapedUsername = JSX_(_ui_utils_jsx3__ .P9, null, megaChat.highlight(megaChat.html(username), matches, true)); + } } - megaChat.updateSectionUnreadCount(); - } - }); - this.rebind(`onCallUserLimitExceeded.${chatId}`, () => { - if (this.callUserLimited) { - return; - } - (this.callUserLimited = tSleep(60)).always(() => { - this.callUserLimited = false; - this.trackDataChange(); - }); - }); - this.membersSetFromApi = new ChatRoom.MembersSet(this); - if (publicChatHandle) { - this.onPublicChatRoomInitialized(); - } - return this; -}; -inherits(ChatRoom, MegaDataObject); -ChatRoom.STATE = { - 'INITIALIZED': 5, - 'JOINING': 10, - 'JOINED': 20, - 'READY': 150, - 'ENDED': 190, - 'LEAVING': 200, - 'LEFT': 250 -}; -ChatRoom.INSTANCE_INDEX = 0; -ChatRoom.ANONYMOUS_PARTICIPANT = mega.BID; -ChatRoom.ARCHIVED = 0x01; -ChatRoom.TOPIC_MAX_LENGTH = 30; -ChatRoom.SCHEDULED_MEETINGS_INTERVAL = 1.8e6; -ChatRoom._fnRequireParticipantKeys = function (fn, scope) { - return function (...args) { - const participants = this.protocolHandler.getTrackedParticipants(); - return ChatdIntegration._ensureKeysAreLoaded(undefined, participants).then(() => { - return fn.apply(scope || this, args); - }).catch(ex => { - this.logger.error("Failed to retrieve keys..", ex); - }); - }; -}; -ChatRoom.MembersSet = function (chatRoom) { - this.chatRoom = chatRoom; - this.members = {}; -}; -ChatRoom.MembersSet.PRIVILEGE_STATE = { - NOT_AVAILABLE: -5, - OPERATOR: 3, - FULL: 2, - READONLY: 0, - LEFT: -1 -}; -ChatRoom.encryptTopic = function (protocolHandler, newTopic, participants, isPublic = false) { - if (protocolHandler instanceof strongvelope.ProtocolHandler && participants.size > 0) { - const topic = protocolHandler.embeddedEncryptTo(newTopic, strongvelope.MESSAGE_TYPES.TOPIC_CHANGE, participants, undefined, isPublic); - if (topic) { - return base64urlencode(topic); + usernameBlock = emailTooltips ? JSX_("div", { + className: "user-card-name light simpletip selectable-txt", + "data-simpletip": contact.m, + "data-simpletipposition": "top" + }, escapedUsername) : JSX_("div", { + className: "user-card-name light selectable-txt" + }, escapedUsername); } + let userCard = null; + const className = this.props.className || ''; + userCard = className.includes('short') ? JSX_("div", { + className: "user-card-data" + }, usernameBlock, JSX_("div", { + className: "user-card-status" + }, this.props.isInCall ? JSX_("div", { + className: "audio-call" + }, JSX_("i", { + className: "sprite-fm-mono icon-phone" + })) : null, JSX_(LastActivity, { + contact, + showLastGreen: this.props.showLastGreen + }))) : JSX_("div", { + className: "user-card-data" + }, usernameBlock, JSX_(ContactPresence, { + contact, + className: this.props.presenceClassName + }), this.props.isInCall ? JSX_("div", { + className: "audio-call" + }, JSX_("i", { + className: "sprite-fm-mono icon-phone" + })) : null, JSX_("div", { + className: "user-card-email selectable-txt" + }, contact.m)); + return JSX_("div", { + className: ` + contacts-info body + ${pres === 'offline' ? 'offline' : ''} + ${className || ''} + `, + style: this.props.style, + onClick: ev => { + let _this$props$onClick, _this$props; + return (_this$props$onClick = (_this$props = this.props).onClick) == null ? void 0 : _this$props$onClick.call(_this$props, contact, ev); + }, + onDoubleClick: ev => { + let _this$props$onDoubleC, _this$props2; + return (_this$props$onDoubleC = (_this$props2 = this.props).onDoubleClick) == null ? void 0 : _this$props$onDoubleC.call(_this$props2, contact, ev); + } + }, this.props.withSelfNote ? JSX_("div", { + className: ` + note-chat-signifier + ${(_this$props$chatRoom = this.props.chatRoom) != null && _this$props$chatRoom.hasMessages() ? '' : 'note-chat-empty'} + ` + }, JSX_("i", { + className: "sprite-fm-mono icon-file-text-thin-outline note-chat-icon" + })) : JSX_(Avatar, { + className: "avatar-wrapper small-rounded-avatar", + contact, + chatRoom: this.props.chatRoom + }), is_chatlink || noContextButton ? null : JSX_(ContactButton, { + key: "button", + dropdowns, + dropdownIconClasses: this.props.dropdownIconClasses || '', + disabled: this.props.dropdownDisabled, + noContextMenu, + contact, + className: this.props.dropdownButtonClasses, + dropdownRemoveButton, + noLoading: this.props.noLoading, + chatRoom: this.props.chatRoom, + verticalOffset: 0 + }), this.props.selectable ? JSX_("div", { + className: "user-card-tick-wrap" + }, JSX_("i", { + className: "sprite-fm-mono icon-check" + })) : null, megaChat.WITH_SELF_NOTE && this.props.withSelfNote ? JSX_("div", { + className: "user-card-data" + }, JSX_("div", { + className: "user-card-name light selectable-txt note-chat-label" + }, l.note_label), JSX_("div", { + className: "user-card-status" + })) : userCard); } - return false; +} +ContactCard.defaultProps = { + dropdownButtonClasses: "tiny-button", + dropdownIconClasses: "tiny-icon icons-sprite grey-dots", + presenceClassName: '', + manualDataChangeTracking: true, + skipQueuedUpdatesOnResize: true }; -ChatRoom.MembersSet.prototype.trackFromActionPacket = function (ap, isMcf) { - const self = this; - const apMembers = {}; - (ap.u || []).forEach((r) => { - apMembers[r.u] = r.p; - }); - Object.keys(self.members).forEach((u_h) => { - if (typeof apMembers[u_h] === 'undefined') { - self.remove(u_h); - } else if (apMembers[u_h] !== self.members[u_h]) { - self.update(u_h, apMembers[u_h]); - } - }); - Object.keys(apMembers).forEach((u_h) => { - if (typeof self.members[u_h] === 'undefined') { - const priv2 = apMembers[u_h]; - !isMcf ? self.add(u_h, priv2) : self.init(u_h, priv2); - } else if (apMembers[u_h] !== self.members[u_h]) { - self.update(u_h, apMembers[u_h]); +class ContactItem extends _mixins2__ .u9 { + render() { + const self = this; + const {contact} = this.props; + if (!contact) { + return null; } - }); - if (!isMcf && ap.m === 1 && !ap.n && ap.url && ap.ou !== u_handle && typeof ap.p === 'undefined' && !ap.topicChange) { - self.chatRoom.trigger('onMeAdded', ap.ou); + const username = this.props.namePrefix ? this.props.namePrefix : `${ M.getNameByHandle(contact.u)}`; + return JSX_("div", { + className: "selected-contact-card short" + }, JSX_("div", { + className: "remove-contact-bttn", + onClick: e => { + if (self.props.onClick) { + self.props.onClick(contact, e); + } + } + }, JSX_("i", { + className: "tiny-icon small-cross" + })), JSX_(Avatar, { + contact, + className: "avatar-wrapper small-rounded-avatar", + hideVerifiedBadge: true, + chatRoom: this.props.chatRoom + }), JSX_("div", { + className: "user-card-data simpletip", + "data-simpletip": username || megaChat.plugins.userHelper.SIMPLETIP_USER_LOADER, + "data-simpletipposition": "top" + }, JSX_(ContactButton, { + noContextMenu: this.props.noContextMenu, + contact, + className: "light", + label: JSX_(_ui_utils_jsx3__ .zT, null, username), + chatRoom: this.props.chatRoom + }))); } +} +ContactItem.defaultProps = { + 'manualDataChangeTracking': true, + 'skipQueuedUpdatesOnResize': true }; -ChatRoom.MembersSet.prototype.init = function (handle, privilege) { - this.members[handle] = privilege; - this.chatRoom.trackDataChange(); -}; -ChatRoom.MembersSet.prototype.update = function (handle, privilege) { - this.members[handle] = privilege; - this.chatRoom.trackDataChange(); -}; -ChatRoom.MembersSet.prototype.add = function (handle, privilege) { - this.members[handle] = privilege; - if (handle === u_handle) { - this.chatRoom.trigger('onMeJoined'); - } - this.chatRoom.trackDataChange(); -}; -ChatRoom.MembersSet.prototype.remove = function (handle) { - delete this.members[handle]; - if (handle === u_handle) { - this.chatRoom.trigger('onMeLeft'); - } - this.chatRoom.trackDataChange(); -}; -ChatRoom.prototype.trackMemberUpdatesFromActionPacket = function (ap, isMcf) { - if (!ap.u) { - return; - } - if (this.membersSetFromApi) { - this.membersSetFromApi.trackFromActionPacket(ap, isMcf); - } -}; -ChatRoom.prototype.getCallParticipants = function () { - const ids = this.activeCallIds.keys(); - if (ids.length === 0) { - return []; - } - return this.activeCallIds[ids[0]]; -}; -ChatRoom.prototype.getChatIdMessages = function () { - return this.chatd.chatIdMessages[this.chatIdBin]; -}; -ChatRoom.prototype.getRetentionFormat = function (retentionTime) { - retentionTime = retentionTime || this.retentionTime; - switch (true) { - case retentionTime === 0: - return RETENTION_FORMAT.DISABLED; - case retentionTime % daysToSeconds(30) === 0 || retentionTime >= 31536000: - return RETENTION_FORMAT.MONTHS; - case retentionTime % daysToSeconds(7) === 0: - return RETENTION_FORMAT.WEEKS; - case retentionTime % daysToSeconds(1) === 0: - return RETENTION_FORMAT.DAYS; - default: - return RETENTION_FORMAT.HOURS; +class ContactPickerWidget extends _mixins2__ .w9 { + constructor(...args) { + super(...args); + this.contactLinkListener = null; + this.domRef = react1___default().createRef(); + this.state = { + searchValue: '', + selected: this.props.selected || [], + publicLink: M.account && M.account.contactLink || undefined + }; + this.normalize = input => ChatSearch._normalize_str(String(input || '').toLowerCase()); + this.onSearchChange = ev => { + this.setState({ + searchValue: ev.target.value + }); + }; + this.renderParticipantsList = () => { + const { + contacts, + emailTooltips, + onSelected + } = this.props; + const { + selected + } = this.state; + const $$list = contacts.map(handle => { + const added = selected.includes(handle); + return JSX_(ContactCard, { + key: handle, + className: ` + contacts-search short + ${added ? 'selected' : ''} + `, + contact: M.u[handle], + selectable: true, + emailTooltips, + noContextButton: true, + noContextMenu: true, + onClick: () => { + this.setState({ + selected: added ? selected.filter(h => h !== handle) : [...selected, handle] + }, () => onSelected(this.state.selected)); + } + }); + }); + return JSX_(_ui_perfectScrollbar_jsx4__ .O, { + className: "contacts-search-scroll", + selected, + contacts + }, JSX_("div", { + className: "contacts-search-subsection" + }, JSX_("div", { + className: "contacts-list-header" + }, megaChat.activeCall ? l.call_participants : l[16217]), JSX_("div", { + className: "contacts-search-list" + }, $$list))); + }; } -}; -ChatRoom.prototype.getRetentionTimeFormatted = function (retentionTime) { - retentionTime = retentionTime || this.retentionTime; - switch (this.getRetentionFormat(retentionTime)) { - case RETENTION_FORMAT.MONTHS: - return Math.floor(secondsToDays(retentionTime) / 30); - case RETENTION_FORMAT.WEEKS: - return secondsToDays(retentionTime) / 7; - case RETENTION_FORMAT.DAYS: - return secondsToDays(retentionTime); - case RETENTION_FORMAT.HOURS: - return secondsToHours(retentionTime); - case RETENTION_FORMAT.DISABLED: - return 0; + renderInviteWarning() { + const { + chatRoom + } = this.props; + const { + activeCall + } = megaChat; + if (!activeCall || activeCall.chatRoom.chatId !== chatRoom.chatId || !activeCall.sfuClient.callLimits || !activeCall.sfuClient.callLimits.usr || chatRoom.getCallParticipants().length < activeCall.sfuClient.callLimits.usr) { + return null; + } + return JSX_("div", { + className: "picker-user-limit-banner" + }, activeCall.organiser === u_handle ? (0,_ui_utils_jsx3__ .lI)(l.invite_limit_banner_organiser, '[A]', _link_jsx8__ .A, { + onClick() { + window.open(`${getBaseUrl()}/pro`, '_blank', 'noopener,noreferrer'); + eventlog(500263); + } + }) : l.invite_limit_banner_host); } -}; -ChatRoom.prototype.getRetentionLabel = function (retentionTime) { - retentionTime = retentionTime || this.retentionTime; - const days = secondsToDays(retentionTime); - const months = Math.floor(days / 30); - const hours = secondsToHours(retentionTime); - switch (this.getRetentionFormat(retentionTime)) { - case RETENTION_FORMAT.DISABLED: - return l.disabled_chat_history_cleaning_status; - case RETENTION_FORMAT.MONTHS: - return mega.icu.format(l.months_chat_history_plural, months); - case RETENTION_FORMAT.WEEKS: - return mega.icu.format(l.weeks_chat_history_plural, days / 7); - case RETENTION_FORMAT.DAYS: - return mega.icu.format(l.days_chat_history_plural, days); - case RETENTION_FORMAT.HOURS: - return mega.icu.format(l.hours_chat_history_plural, hours); + componentDidMount() { + super.componentDidMount(); + setContactLink(this.domRef && this.domRef.current); + this.contactLinkListener = mBroadcaster.addListener('contact:setContactLink', publicLink => this.state.publicLink ? null : this.setState({ + publicLink + })); } -}; -ChatRoom.prototype.setRetention = function (time) { - asyncApiReq({ - "a": "mcsr", - "id": this.chatId, - "d": time, - "ds": 1 - }); -}; -ChatRoom.prototype.removeMessagesByRetentionTime = function () { - const self = this; - const {messages} = self.messagesBuff; - if (messages.length === 0 || this.retentionTime === 0) { - return; + componentDidUpdate() { + const self = this; + if (self.scrollToLastSelected && self.psSelected) { + self.scrollToLastSelected = false; + self.psSelected.scrollToPercentX(100, false); + } + if (self.searchContactsScroll) { + self.searchContactsScroll.reinitialise(); + } } - const newest = messages.getItem(messages.length - 1); - let lowestValue = newest.orderValue; - let deleteFrom = null; - let lastMessage = null; - const deletePreviousTo = (new Date() - self.retentionTime * 1000) / 1000; - const cp = self.megaChat.plugins.chatdIntegration.chatd.chatdPersist; - let finished = false; - if (typeof cp !== 'undefined') { - const done = function (message) { - if (message) { - if (self.retentionTime > 0 && self.messagesBuff.messages.length > 0) { - self.messagesBuff._removeMessagesBefore(message.messageId); - } - cp.removeMessagesBefore(self.chatId, message.orderValue); - } - }; - if (newest.delay < deletePreviousTo) { - cp.clearChatHistoryForChat(self.chatId); - return; + UNSAFE_componentWillMount() { + if (super.UNSAFE_componentWillMount) { + super.UNSAFE_componentWillMount(); } - const removeMsgs = function () { - cp._paginateMessages(lowestValue, Chatd.MESSAGE_HISTORY_LOAD_COUNT, self.chatId).then((messages) => { - messages = messages[0]; - if (messages.length) { - for (let i = 0; i < messages.length; i++) { - const message = messages[i]; - if (message.msgObject.delay < deletePreviousTo) { - deleteFrom = lastMessage || message; - break; + const self = this; + if (self.props.multiple) { + $(document.body).rebind(`keypress.contactPicker${ self.getUniqueId()}`, (e) => { + const keyCode = e.which || e.keyCode; + if (keyCode === 13) { + if (self.state.selected) { + e.preventDefault(); + e.stopPropagation(); + closeDropdowns(); + if (self.props.onSelectDone) { + self.props.onSelectDone(self.state.selected); } - lastMessage = message; - lowestValue = message.orderValue; } - } else { - finished = true; - } - if (!finished && !deleteFrom) { - onIdle(removeMsgs); - } else { - done(deleteFrom); } }); - }; - removeMsgs(); + } } - if (self.retentionTime > 0 && self.messagesBuff.messages.length > 0) { - let message; - while (message = self.messagesBuff.messages.getItem(0)) { - if (message.delay < deletePreviousTo) { - if (!self.messagesBuff.messages.removeByKey(message.messageId)) { - break; - } - } else { - break; - } + componentWillUnmount() { + super.componentWillUnmount(); + const self = this; + delete self._foundFrequents; + if (self.props.multiple) { + $(document.body).off(`keypress.contactPicker${ self.getUniqueId()}`); + } + if (this.contactLinkListener) { + mBroadcaster.removeListener(this.contactLinkListener); } } -}; -ChatRoom.prototype.isOnline = function () { - const shard = this.chatd.shards[this.chatShard]; - return shard ? shard.isOnline() : false; -}; -ChatRoom.prototype.isOnlineForCalls = function () { - const chatdChat = this.getChatIdMessages(); - if (!chatdChat) { - return false; - } - return chatdChat.loginState() >= LoginState.HISTDONE; -}; -ChatRoom.prototype.isArchived = function () { - const self = this; - return self.flags & ChatRoom.ARCHIVED; -}; -ChatRoom.prototype.isAnonymous = function () { - return is_chatlink && this.type === "public" && this.publicChatHandle && this.publicChatKey && this.publicChatHandle === megaChat.initialPubChatHandle; -}; -ChatRoom.prototype.isDisplayable = function () { - return !this.isArchived() || this.call; -}; -ChatRoom.prototype.isMuted = function () { - return pushNotificationSettings.getDnd(this.chatId) || pushNotificationSettings.getDnd(this.chatId) === 0; -}; -ChatRoom.prototype.persistToFmdb = function () { - const self = this; - if (fmdb) { - const users = []; - if (self.members) { - Object.keys(self.members).forEach((user_handle) => { - users.push({ - u: user_handle, - p: self.members[user_handle] - }); - }); - } - if (self.chatId && self.chatShard !== undefined) { - const roomInfo = { - 'id': self.chatId, - 'cs': self.chatShard, - 'g': self.type === "group" || self.type === "public" ? 1 : 0, - 'u': users, - 'ts': self.ctime, - 'ct': self.ct, - 'ck': self.ck ? self.ck : null, - 'f': self.flags, - 'm': self.type === "public" ? 1 : 0 - }; - fmdb.add('mcf', { - id: roomInfo.id, - d: roomInfo - }); - } - } -}; -ChatRoom.prototype.updateFlags = function (f, updateUI) { - const self = this; - const flagChange = self.flags !== f; - self.flags = f; - if (self.isArchived()) { - megaChat.archivedChatsCount++; - } else { - megaChat.archivedChatsCount--; - } - self.persistToFmdb(); - if (updateUI && flagChange) { - if (megaChat.currentlyOpenedChat && megaChat.chats[megaChat.currentlyOpenedChat] && megaChat.chats[megaChat.currentlyOpenedChat].chatId === self.chatId) { - loadSubPage('fm/chat/'); - } else { - megaChat.refreshConversations(); + _eventuallyAddContact(v, contacts, selectableContacts, forced) { + const self = this; + const withSelfNote = this.props.withSelfNote && v.u === u_handle; + if (v.u === u_handle && !this.props.step && !withSelfNote) { + return false; } - } - this.trackDataChange(); -}; -ChatRoom.stateToText = function (state) { - let txt = null; - $.each(ChatRoom.STATE, (k, v) => { - if (state === v) { - txt = k; + if (!forced && v.c !== 1 && v.u !== u_handle) { return false; } - }); - return txt; -}; -ChatRoom.prototype.setState = function (newState, isRecover) { - const self = this; - assert(newState, 'Missing state'); - if (newState === self.state) { - self.logger.debug("Ignoring .setState, newState === oldState, current state: ", self.getStateAsText()); - return; - } - if (self.state) { - assert(newState === ChatRoom.STATE.JOINING && isRecover || newState === ChatRoom.STATE.INITIALIZED && isRecover || newState > self.state, `Invalid state change. Current:${ ChatRoom.stateToText(self.state) }to${ ChatRoom.stateToText(newState)}`); - } - const oldState = self.state; - self.state = newState; - self.trigger('onStateChange', [oldState, newState]); -}; -ChatRoom.prototype.getStateAsText = function () { - const self = this; - return ChatRoom.stateToText(self.state); -}; -ChatRoom.prototype.getParticipants = function () { - const self = this; - return Object.keys(self.members); -}; -ChatRoom.prototype.getParticipantsExceptMe = function (userHandles) { - const res = clone(userHandles || this.getParticipants()); - array.remove(res, u_handle, true); - return res; -}; -ChatRoom.prototype.getParticipantsTruncated = function (maxMembers = 5, maxLength = ChatRoom.TOPIC_MAX_LENGTH) { - const truncatedParticipantNames = []; - const members = Object.keys(this.members); - for (let i = 0; i < members.length; i++) { - const handle = members[i]; - const name = M.getNameByHandle(handle); - if (!handle || !name || handle === u_handle) { - continue; + if (self.props.exclude && self.props.exclude.indexOf(v.u) > -1) { + return false; } - if (i > maxMembers) { - break; + let isDisabled = false; + if (!self.wasMissingKeysForContacts) { + self.wasMissingKeysForContacts = {}; } - truncatedParticipantNames.push(name.length > maxLength ? `${name.substr(0, maxLength) }...` : name); - } - if (truncatedParticipantNames.length === maxMembers) { - truncatedParticipantNames.push('...'); - } - return truncatedParticipantNames.join(', '); -}; -ChatRoom.prototype.getRoomTitle = function () { - const formattedDate = l[19077].replace('%s1', new Date(this.ctime * 1000).toLocaleString()); - if (this.isNote) { - return l.note_label; - } - if (this.type === 'private') { - const participants = this.getParticipantsExceptMe(); - return participants && Array.isArray(participants) ? M.getNameByHandle(participants[0]) : formattedDate; - } - if (this.topic === '' || !this.topic) { - return this.getParticipantsTruncated() || formattedDate; - } - const formattedTopic = this.getTruncatedRoomTopic(); - const isCanceled = this.scheduledMeeting && this.scheduledMeeting.isCanceled; - return isCanceled ? `${formattedTopic} ${l.canceled_meeting}` : formattedTopic; -}; -ChatRoom.prototype.getTruncatedRoomTopic = function (maxLength = ChatRoom.TOPIC_MAX_LENGTH) { - return this.topic && this.topic.length > maxLength ? `${this.topic.substr(0, maxLength) }...` : this.topic; -}; -ChatRoom.prototype.setRoomTopic = async function (newTopic) { - if (newTopic && newTopic.trim().length && newTopic !== this.getRoomTitle()) { - this.scrolledToBottom = true; - const participants = this.protocolHandler.getTrackedParticipants(); - await ChatdIntegration._ensureKeysAreLoaded(undefined, participants); - const topic = this.protocolHandler.embeddedEncryptTo(newTopic, strongvelope.MESSAGE_TYPES.TOPIC_CHANGE, participants, undefined, this.type === 'public'); - if (topic) { - return api.req({ - a: 'mcst', - id: this.chatId, - ct: base64urlencode(topic), - v: Chatd.VERSION + if (!self.wasMissingKeysForContacts[v.u] && (!pubCu25519[v.u] || !pubEd25519[v.u])) { + self.wasMissingKeysForContacts[v.u] = true; + ChatdIntegration._ensureKeysAreLoaded(undefined, [v.u]).always(() => { + if (self.isMounted()) { + self.safeForceUpdate(); + } }); + isDisabled = true; + return true; + } else if (self.wasMissingKeysForContacts[v.u] && (!pubCu25519[v.u] || !pubEd25519[v.u])) { + return false; } - } -}; -ChatRoom.prototype.leave = function (notify) { - const valid = this.type === 'group' || this.type === 'public'; - console.assert(valid, `Can't leave room "${this.roomId}" of type "${this.type}"`); - if (!valid) { - return; - } - this._leaving = true; - this.topic = ''; - if (notify) { - this.trigger('onLeaveChatRequested'); - } - if (this.state !== ChatRoom.STATE.LEFT) { - this.setState(ChatRoom.STATE.LEAVING); - this.setState(ChatRoom.STATE.LEFT); - } - if (this.activeCallIds.length) { - for (const activeCallId of this.activeCallIds.keys()) { - this.activeCallIds.remove(activeCallId); - } - megaChat.updateSectionUnreadCount(); - } -}; -ChatRoom.prototype.archive = function () { - const self = this; - const mask = 0x01; - const flags = ChatRoom.ARCHIVED; - asyncApiReq({ - 'a': 'mcsf', - 'id': self.chatId, - 'm': 1, - 'f': flags, - 'v': Chatd.VERSION - }).then(r => { - if (r === 0) { - self.updateFlags(flags, true); + if (self.state.searchValue && self.state.searchValue.length > 0) { + const searchValue = this.normalize(this.state.searchValue); + const { + name, + nickname, + fullname, + u, + m + } = { + ...v, + name: withSelfNote ? l.note_label : v.name + }; + const matches = [name, nickname, fullname, M.getNameByHandle(u), !this.props.skipMailSearch && m].some(field => this.normalize(field).includes(searchValue)); + if (!matches) { + return false; + } } - }); -}; -ChatRoom.prototype.unarchive = function () { - const self = this; - const mask = 0x01; - const flags = 0x00; - asyncApiReq({ - 'a': 'mcsf', - 'id': self.chatId, - 'm': 1, - 'f': 0, - 'v': Chatd.VERSION - }).then(res => { - if (res === 0) { - self.updateFlags(0, true); + let selectedClass = ""; + if (self.state.selected && self.state.selected.indexOf(v.u) !== -1) { + selectedClass = "selected"; } - }); -}; -ChatRoom.prototype.destroy = function (notifyOtherDevices, noRedirect) { - const self = this; - self.megaChat.trigger('onRoomDestroy', [self]); - const mc = self.megaChat; - const roomJid = self.roomId; - if (!self.stateIsLeftOrLeaving()) { - self.leave(notifyOtherDevices); - } else if (self.type === "public" && self.publicChatHandle) { - if (typeof self.members[u_handle] === 'undefined') { - self.megaChat.plugins.chatdIntegration.handleLeave(self); - } - } - if (self.isCurrentlyActive) { - self.isCurrentlyActive = false; - } - Soon(() => { - mc.chats.remove(roomJid); - if (!noRedirect && u_type === 3) { - loadSubPage('fm/chat'); - } - }); -}; -ChatRoom.prototype.updatePublicHandle = async function (remove, cim, force) { - if (force) { - this.publicLink = null; - } - if (!remove && this.publicLink) { - return this.publicLink; - } - return asyncApiReq({ - a: 'mcph', - id: this.chatId, - v: Chatd.VERSION, - cim: cim ? 1 : 0, - d: remove ? 1 : undefined - }).then(res => { - assert(remove && res === 0 || Array.isArray(res) && res[1].length === 8); - this.publicLink = remove ? null : `chat/${res[1]}#${this.protocolHandler.getUnifiedKey()}`; - }).catch(ex => { - this.logger.warn('updatePublicHandle', ex); - this.publicLink = null; - }); -}; -ChatRoom.prototype.iAmInRoom = function () { - return !(!this.members.hasOwnProperty(u_handle) || this.members[u_handle] === -1); -}; -ChatRoom.prototype.joinViaPublicHandle = function () { - const self = this; - if (!fminitialized && is_chatlink) { - if (u_type) { - return new Promise((res, rej) => { - self.megaChat.plugins.chatdIntegration.joinChatViaPublicHandle(self).then(() => { - self.megaChat.routing.reinitAndOpenExistingChat(self.chatId, self.publicChatHandle).then(res, rej); - }, ex => { - console.error("Failed joining a chat room (u_type)", ex); - rej(ex); - }); - }); - } - return; - } - if (!self.iAmInRoom() && self.type === "public" && self.publicChatHandle) { - return megaChat.plugins.chatdIntegration.joinChatViaPublicHandle(self); - } - return Promise.reject(); -}; -ChatRoom.prototype.switchOffPublicMode = ChatRoom._fnRequireParticipantKeys(function () { - let { - topic, - protocolHandler, - chatId - } = this; - if (topic) { - topic = protocolHandler.embeddedEncryptTo(topic, strongvelope.MESSAGE_TYPES.TOPIC_CHANGE, protocolHandler.getTrackedParticipants(), true, false); - topic = base64urlencode(topic); - } - return asyncApiReq({ - a: 'mcscm', - id: chatId, - ct: topic || undefined, - v: Chatd.VERSION - }).then(() => { - protocolHandler.switchOffOpenMode(); - }); -}); -ChatRoom.prototype.show = function () { - if (this.isCurrentlyActive) { - return false; - } - this.megaChat.hideAllChats(); - if (d) { - this.logger.debug(' ---- show'); - } - $.tresizer(); - onIdle(() => { - this.scrollToChat(); - this.trackDataChange(); - }); - this.isCurrentlyActive = true; - this.lastShownInUI = Date.now(); - this.megaChat.setAttachments(this.roomId); - this.megaChat.lastOpenedChat = this.roomId; - this.megaChat.currentlyOpenedChat = this.roomId; - this.trigger('activity'); - this.trigger('onChatShown'); - let tmp = this.megaChat.rootDOMNode; - if (tmp = tmp.querySelector('.conversation-panels')) { - tmp.classList.remove('hidden'); - if (tmp = tmp.querySelector(`.conversation-panel[data-room-id="${this.chatId}"]`)) { - tmp.classList.remove('hidden'); - } - } - if (tmp = document.getElementById(`conversation_${this.roomId}`)) { - tmp.classList.add('active'); - } - if (mega.ui.mInfoPanel) { - mega.ui.mInfoPanel.hide(); - } -}; -ChatRoom.prototype.scrollToChat = function () { - this._scrollToOnUpdate = true; - const { - $chatTreePanePs - } = megaChat; - if ($chatTreePanePs && $chatTreePanePs.length) { - const li = document.querySelector(`ul.conversations-pane li#conversation_${this.roomId}`); - if (li && !verge.inViewport(li, -72)) { - Object.values($chatTreePanePs).forEach(({ - ref - }) => { - if (ref.domNode) { - const wrapOuterHeight = $(ref.domNode).outerHeight(); - const itemOuterHeight = $('li:first', ref.domNode).outerHeight(); - const pos = li.offsetTop; - if (ref.domNode.contains(li)) { - ref.doProgramaticScroll == null || ref.doProgramaticScroll(Math.max(0, pos - wrapOuterHeight / 2 + itemOuterHeight), true); - } - } - }); - this._scrollToOnUpdate = false; - } - } -}; -ChatRoom.prototype.isActive = function () { - return document.hasFocus() && this.isCurrentlyActive; -}; -ChatRoom.prototype.setActive = function () { - loadSubPage(this.getRoomUrl()); -}; -ChatRoom.prototype.isLoading = function () { - const mb = this.messagesBuff; - return mb.messagesHistoryIsLoading() || mb.isDecrypting; -}; -ChatRoom.prototype.getRoomUrl = function (getRawLink) { - const self = this; - if (self.type === "private") { - const participants = self.getParticipantsExceptMe(); - const contact = M.u[participants[0] || u_handle]; - if (contact) { - return `fm/chat/p/${ contact.u}`; - } - } else if (!getRawLink && is_chatlink && self.type === "public" && self.publicChatHandle && self.publicChatKey) { - return `chat/${ self.publicChatHandle }#${ self.publicChatKey}`; - } else if (self.type === "public") { - return `fm/chat/c/${ self.roomId}`; - } else if (self.type === "group" || self.type === "public") { - return `fm/chat/g/${ self.roomId}`; - } else { - throw new Error("Can't get room url for unknown room type."); - } -}; -ChatRoom.prototype.activateWindow = function () { - const self = this; - loadSubPage(self.getRoomUrl()); -}; -ChatRoom.prototype.hide = function () { - let _tmp; - if (d) { - this.logger.debug(' ---- hide', this.isCurrentlyActive); - } - this.isCurrentlyActive = false; - this.lastShownInUI = Date.now(); - if (this.megaChat.currentlyOpenedChat === this.roomId) { - this.megaChat.currentlyOpenedChat = null; - } - let tmp = this.megaChat.rootDOMNode.querySelector(`.conversation-panel[data-room-id="${this.chatId}"]`); - (_tmp = tmp) == null || _tmp.classList.add('hidden'); - if (tmp = document.getElementById(`conversation_${this.roomId}`)) { - tmp.classList.remove('active'); - } - this.trigger('onChatHidden', this.isCurrentlyActive); -}; -ChatRoom.prototype.appendMessage = function (message) { - const self = this; - if (message.deleted) { - return false; - } - if (self.shownMessages[message.messageId]) { - return false; - } - if (!message.orderValue) { - const mb = self.messagesBuff; - if (mb.messages.length > 0) { - const prevMsg = mb.messages.getItem(mb.messages.length - 1); - if (!prevMsg) { - self.logger.error("self.messages got out of sync...maybe there are some previous JS exceptions that caused that? note that messages may be displayed OUT OF ORDER in the UI."); - } else { - let nextVal = prevMsg.orderValue + 0.1; - if (!prevMsg.sent) { - const cid = megaChat.plugins.chatdIntegration.chatd.chatIdMessages[self.chatIdBin]; - if (cid && cid.highnum) { - nextVal = ++cid.highnum; - } - } - message.orderValue = nextVal; - } - } - } - message.source = Message.SOURCE.SENT; - self.trigger('onMessageAppended', message); - self.messagesBuff.messages.push(message); - self.shownMessages[message.messageId] = true; -}; -ChatRoom.prototype.getNavElement = function () { - const self = this; - return $(`.nw-conversations-item[data-room-id="${ self.chatId }"]`); -}; -ChatRoom.prototype.sendMessage = function (message) { - const self = this; - const {megaChat} = this; - const messageId = megaChat.generateTempMessageId(self.roomId, message); - const msgObject = new Message(self, self.messagesBuff, { - messageId, - 'userId': u_handle, - message, - 'textContents': message, - 'delay': unixtime(), - 'sent': Message.STATE.NOT_SENT - }); - self.trigger('onSendMessage'); - self.appendMessage(msgObject); - return self._sendMessageToTransport(msgObject).then(internalId => { - if (!internalId) { - this.logger.warn(`Got unexpected(?) 'sendingnum'...`, internalId); - } - msgObject.internalId = internalId; - msgObject.orderValue = internalId; - return internalId || -0xBADF; - }).catch(ex => { - this.logger.error(`sendMessage failed..`, msgObject, ex); - }); -}; -ChatRoom.prototype._sendMessageToTransport = function (messageObject) { - const self = this; - const {megaChat} = this; - megaChat.trigger('onPreBeforeSendMessage', messageObject); - megaChat.trigger('onBeforeSendMessage', messageObject); - megaChat.trigger('onPostBeforeSendMessage', messageObject); - return megaChat.plugins.chatdIntegration.sendMessage(self, messageObject); -}; -ChatRoom.prototype._sendNodes = async function (nodeids, users) { - const u = this.type === 'public' ? [strongvelope.COMMANDER] : users; - const promises = nodeids.map(nodeId => asyncApiReq({ - a: 'mcga', - n: [nodeId], - u, - id: this.chatId, - v: Chatd.VERSION - })); - const res = await Promise.allSettled(promises); - const sent = []; - for (let i = res.length; i--;) { - if (res[i].status === 'fulfilled') { - sent.push(nodeids[i]); - } - } - if (!sent.length) { - throw ENOENT; - } - return sent; -}; -ChatRoom.prototype.attachNodes = async function (nodes, names) { - if (!Array.isArray(nodes)) { - nodes = [nodes]; - } - const handles = new Set(); - for (let i = nodes.length; i--;) { - const n = nodes[i]; - const h = String(crypto_keyok(n) && n.h || n); - if (!M.getNodeByHandle(h)) { - handles.add(h); - } - } - if (handles.size) { - await dbfetch.acquire([...handles]); - } - return this._attachNodes(nodes, names); -}; -ChatRoom.prototype._attachNodes = mutex('chatroom-attach-nodes', function _(resolve, reject, nodes, names) { - let i; - let step = 0; - const users = []; - const self = this; - let result = null; - let copy = Object.create(null); - let send = Object.create(null); - let link = Object.create(null); - let nmap = Object.create(null); - const members = self.getParticipantsExceptMe(); - const sendMessage = nodes => { - return new Promise(resolve => { - for (let i = nodes.length; i--;) { - const n = nmap[nodes[i]] || M.getNodeByHandle(nodes[i]); - console.assert(n.h, `Node not found... ${nodes[i]}`); - if (n.h) { - const name = names && (names[n.hash] || names[n.h]) || n.name; - this.sendMessage(Message.MANAGEMENT_MESSAGE_TYPES.MANAGEMENT + Message.MANAGEMENT_MESSAGE_TYPES.ATTACHMENT + JSON.stringify([{ - h: n.h, - k: n.k, - t: n.t, - s: n.s, - fa: n.fa, - ts: n.ts, - hash: n.hash, - name, - des: n.des - }])); - } - } - resolve(); - }); - }; - const attach = nodes => { - console.assert(this.type === 'public' || users.length || this.isNote, 'No users to send to?!'); - return this.isNote ? sendMessage(nodes) : this._sendNodes(nodes, users).then(res => sendMessage(res)); - }; - const done = function () { - if (--step < 1) { - nmap = null; - resolve(result); - } - }; - const fail = function (ex) { - if (ex === EBLOCKED) { - result = ex; - } else if (ex === ENOENT) { - result = result || ex; - } else if (d) { - _.logger.error(ex); - } - done(); - }; - if (d && !_.logger) { - _.logger = new MegaLogger('attachNodes', {}, self.logger); - } - for (i = members.length; i--;) { - const usr = M.getUserByHandle(members[i]); - if (usr.u) { - users.push(usr.u); - } - } - for (i = nodes.length; i--;) { - const h = nodes[i]; - const n = crypto_keyok(h) ? h : M.getNodeByHandle(h); - if (n.t) { - link[n.h] = 1; - continue; - } - if (n.hash) { - nmap[n.hash] = n; - if (names && names[n.h]) { - names[n.hash] = names[n.h]; - } - } - let op = send; - if (n.u !== u_handle || M.getNodeRoot(n.h) === 'shares') { - op = copy; - } - op[n.h] = 1; - nmap[n.h] = n; - } - copy = Object.keys(copy); - send = Object.keys(send); - link = Object.keys(link); - if (d) { - _.logger.debug('copy:%d, send:%d, link:%d', copy.length, send.length, link.length, copy, send, link); - } - if (link.length) { - ++step; - Promise.resolve(mega.fileRequestCommon.storage.isDropExist(link)).then(res => { - if (res.length) { - return mega.fileRequest.showRemoveWarning(res); - } - }).then(() => { - const createLink = h => M.createPublicLink(h).then(({ - link - }) => this.sendMessage(link)); - return Promise.all(link.map(createLink)); - }).then(done).catch(fail); - } - if (send.length) { - step++; - attach(send).then(done).catch(fail); - } - if (copy.length) { - step++; - this._copyNodesToAttach(copy, nmap).then(res => attach(res)).then(done).catch(fail); - } - if (!step) { - if (d) { - _.logger.warn('Nothing to do here...'); - } - queueMicrotask(done); - } -}); -ChatRoom.prototype._copyNodesToAttach = async function (copy, nmap) { - const { - h: target - } = await M.myChatFilesFolder.get(true); - if (!M.c[target]) { - await dbfetch.get(target); - } - const dir = Object.keys(M.c[target] || {}); - const rem = []; - for (let i = copy.length; i--;) { - const n = nmap[copy[i]] || M.getNodeByHandle(copy[i]); - console.assert(n.h, `Node not found.. ${copy[i]}`); - for (let y = dir.length; y--;) { - const b = M.getNodeByHandle(dir[y]); - if (n.h === b.h || b.hash === n.hash) { - if (d) { - this.logger.info('deduplication %s:%s', n.h, b.h, [n], [b]); - } - rem.push(n.h); - copy.splice(i, 1); - break; - } - } - } - let res = []; - if (copy.length) { - res = await M.copyNodes(copy, target, false, false, { - targetChatId: this.chatId - }); - } else if (d) { - this.logger.info('No new nodes to copy.', rem); - } - assert(Array.isArray(res), `Unexpected response, ${res && res.message || res}`, res); - const [h] = res; - res = [...rem, ...res]; - assert(res.length, 'Unexpected condition... nothing to attach ?!'); - for (let i = res.length; i--;) { - const n = nmap[res[i]] || M.getNodeByHandle(res[i]); - if (n.fv) { - if (d) { - this.logger.info('Skipping file-version %s', n.h, n); - } - res.splice(i, 1); - } - } - if (h && !res.length) { - if (d) { - this.logger.info('Adding nothing but a file-version?..', h); - } - res = [h]; - } - return res; -}; -ChatRoom.prototype.onUploadStart = function (data) { - const self = this; - if (d) { - self.logger.debug('onUploadStart', data); - } -}; -ChatRoom.prototype.uploadFromComputer = function () { - this.scrolledToBottom = true; - $('#fileselect1').trigger('click'); -}; -ChatRoom.prototype.attachContacts = function (ids) { - for (let i = 0; i < ids.length; i++) { - const nodeId = ids[i]; - const node = M.u[nodeId]; - this.sendMessage(Message.MANAGEMENT_MESSAGE_TYPES.MANAGEMENT + Message.MANAGEMENT_MESSAGE_TYPES.CONTACT + JSON.stringify([{ - u: node.u, - email: node.m, - name: node.name || node.m - }])); - } -}; -ChatRoom.prototype.getMessageById = function (messageId) { - const self = this; - const msgs = self.messagesBuff.messages; - const msgKeys = msgs.keys(); - for (let i = 0; i < msgKeys.length; i++) { - const k = msgKeys[i]; - const v = msgs[k]; - if (v && v.messageId === messageId) { - return v; - } - } - return false; -}; -ChatRoom.prototype.hasMessages = function (userMessagesOnly = false) { - return this.messagesBuff.messages.some(m => !userMessagesOnly || m.messageHtml); -}; -ChatRoom.prototype.renderContactTree = function () { - const self = this; - const $navElement = self.getNavElement(); - const $count = $('.nw-conversations-unread', $navElement); - const count = self.messagesBuff.getUnreadCount(); - if (count > 0) { - $count.text(count > 9 ? "9+" : count); - $navElement.addClass("unread"); - } else if (count === 0) { - $count.text(""); - $navElement.removeClass("unread"); - } - $navElement.data('chatroom', self); -}; -ChatRoom.prototype.getUnreadCount = function () { - const self = this; - return self.messagesBuff.getUnreadCount(); -}; -ChatRoom.prototype.recover = function () { - const self = this; - self.callRequest = null; - if (self.state !== ChatRoom.STATE.LEFT) { - self.membersLoaded = false; - self.setState(ChatRoom.STATE.JOINING, true); - self.megaChat.trigger("onRoomCreated", [self]); - return MegaPromise.resolve(); - } else { - return MegaPromise.reject(); - } -}; -ChatRoom.prototype.showMissingUnifiedKeyDialog = function () { - return msgDialog(`warningb:!^${l.msg_dlg_cancel}!${l[23433]}`, null, l[200], l.chat_key_failed_dlg_text, reload => reload ? M.reload() : null, 1); -}; -ChatRoom.prototype.hasInvalidKeys = function () { - if (!is_chatlink && this.type === 'public') { - const { - unifiedKey - } = this.protocolHandler || {}; - if (!unifiedKey || unifiedKey && unifiedKey.length !== 16 || !this.ck || this.ck && this.ck.length !== 32) { - console.error('Error instantiating room/call -- missing `unifiedKey`/malformed `ck` for public chat.'); - const { - owner, - actors - } = mBroadcaster.crossTab; - eventlog(99751, JSON.stringify([1, buildVersion.website || 'dev', String(this.chatId).length | 0, this.type | 0, this.isMeeting | 0, typeof unifiedKey, String(unifiedKey || '').length | 0, typeof this.ck, String(this.ck).length | 0, !!owner | 0, Object(actors).length | 0])); - return true; - } - } - return false; -}; -ChatRoom.prototype.joinCall = ChatRoom._fnRequireParticipantKeys(function (audio, video, callId) { - if (!megaChat.hasSupportForCalls || this.activeCallIds.length === 0 || this.meetingsLoading) { - return; - } - if (this.hasInvalidKeys()) { - return this.showMissingUnifiedKeyDialog(); - } - this.meetingsLoading = { - title: l.joining, - audio, - video - }; - callId = callId || this.activeCallIds.keys()[0]; - return asyncApiReq({ - 'a': 'mcmj', - 'cid': this.chatId, - "mid": callId - }).then(r => { - this.startOrJoinCall(callId, r.url, audio, video, r.organiser); - }); -}); -ChatRoom.prototype.startOrJoinCall = function (callId, url, audio, video, organiser) { - tryCatch(() => { - const call = this.call = megaChat.activeCall = megaChat.plugins.callManager2.createCall(this, callId, this.protocolHandler.chatMode === strongvelope.CHAT_MODE.PUBLIC && str_to_ab(this.protocolHandler.unifiedKey)); - call.setOrganiser(organiser); - return call.connect(url, audio, video); - }, ex => { - let _this$call; - (_this$call = this.call) == null || _this$call.destroy(); - this.call = megaChat.activeCall = null; - this.meetingsLoading = false; - console.error('Failed to start/join call:', ex); - })(); -}; -ChatRoom.prototype.rejectCall = function (callId) { - if (this.activeCallIds.length === 0) { - return; - } - callId = callId || this.activeCallIds.keys()[0]; - if (this.type === "private") { - return asyncApiReq({ - 'a': 'mcme', - 'cid': this.chatId, - 'mid': callId - }); - } - const shard = this.chatd.shards[this.chatShard]; - if (shard) { - shard.sendCallReject(base64urldecode(this.chatId), base64urldecode(callId)); - } - return Promise.resolve(); -}; -ChatRoom.prototype.ringUser = function (userId, callId, callstate) { - assert(userId, 'Missing user handle.'); - assert(callId, 'Missing chat handle.'); - assert(this.type !== 'private', 'Unexpected chat type.'); - const shard = this.chatd.shards[this.chatShard]; - if (shard) { - api.req({ - a: 'mcru', - u: userId, - cid: this.chatId - }).then(() => shard.ringUser(this.chatIdBin, base64urldecode(userId), base64urldecode(callId), callstate)).catch(dump); - } -}; -ChatRoom.prototype.endCallForAll = function (callId) { - if (this.activeCallIds.length && this.type !== 'private') { - callId = callId || this.activeCallIds.keys()[0]; - asyncApiReq({ - 'a': 'mcme', - 'cid': this.chatId, - 'mid': callId - }); - eventlog(99761, JSON.stringify([this.chatId, callId, this.isMeeting | 0])); - } -}; -ChatRoom.prototype.startAudioCall = function (scheduled) { - return this.startCall(true, false, scheduled); -}; -ChatRoom.prototype.startVideoCall = function (scheduled) { - return this.startCall(true, true, scheduled); -}; -ChatRoom.prototype.startCall = ChatRoom._fnRequireParticipantKeys(function (audio, video, scheduled) { - if (!megaChat.hasSupportForCalls || this.meetingsLoading) { - return; - } - if (this.activeCallIds.length > 0) { - this.joinCall(this.activeCallIds.keys()[0]); - return; - } - if (this.hasInvalidKeys()) { - return this.showMissingUnifiedKeyDialog(); - } - this.meetingsLoading = { - title: l.starting, - audio, - video - }; - const opts = { - a: 'mcms', - cid: this.chatId, - sm: scheduled && this.scheduledMeeting && this.scheduledMeeting.id - }; - if (localStorage.sfuId) { - opts.sfu = parseInt(localStorage.sfuId, 10); - } - return asyncApiReq(opts).then(r => { - this.startOrJoinCall(r.callId, r.sfu, audio, video, r.organiser); - }).catch(ex => { - this.meetingsLoading = false; - this.logger.error(`Failed to start call: ${ex}`); - }); -}); -ChatRoom.prototype.subscribeForCallEvents = function () { - const callMgr = megaChat.plugins.callManager2; - this.rebind("onChatdPeerJoinedCall.callManager", (e, data) => { - if (!this.activeCallIds.exists(data.callId)) { - this.activeCallIds.set(data.callId, []); - } - this.activeCallIds.set(data.callId, [...this.activeCallIds[data.callId], ...data.participants]); - const parts = data.participants; - for (let i = 0; i < parts.length; i++) { - if (this.type === "private" || parts[i] === u_handle && this.ringingCalls.exists(data.callId)) { - this.ringingCalls.remove(data.callId); - callMgr.trigger("onRingingStopped", { - callId: data.callId, - chatRoom: this - }); - } - } - megaChat.updateSectionUnreadCount(); - this.callParticipantsUpdated(); - }); - this.rebind("onChatdPeerLeftCall.callManager", (e, data) => { - if (!this.activeCallIds[data.callId]) { - return; - } - const parts = data.participants; - for (let i = 0; i < parts.length; i++) { - array.remove(this.activeCallIds[data.callId], parts[i], true); - if (parts[i] === u_handle && this.ringingCalls.exists(data.callId)) { - this.ringingCalls.remove(data.callId); - callMgr.trigger("onRingingStopped", { - callId: data.callId, - chatRoom: this - }); - } - } - this.callParticipantsUpdated(); - }); - this.rebind("onCallLeft.callManager", (e, data) => { - console.warn("onCallLeft:", JSON.stringify(data)); - const { - call - } = this; - if (!call || call.callId !== data.callId) { - if (d) { - console.warn("... no active call or event not for it"); - } - return; - } - this.meetingsLoading = false; - call.hangUp(data.reason); - megaChat.activeCall = this.call = null; - }); - this.rebind("onChatdCallEnd.callManager", (e, data) => { - if (d) { - console.warn("onChatdCallEnd:", JSON.stringify(data)); - } - this.meetingsLoading = false; - this.activeCallIds.remove(data.callId); - if (this.callUserLimited) { - this.callUserLimited.abort(); - } - this.callUserLimited = false; - this.stopRinging(data.callId); - this.callParticipantsUpdated(); - megaChat.updateSectionUnreadCount(); - }); - this.rebind('onCallState.callManager', function (e, data) { - const ac = this.activeCallIds[data.callId]; - console.assert(ac, `unknown call: ${data.callId}`); - if (ac) { - callMgr.onCallState(data, this); - this.callParticipantsUpdated(); - } - }); - this.rebind('onRoomDisconnected.callManager', function () { - this.activeCallIds.clear(); - megaChat.updateSectionUnreadCount(); - if (navigator.onLine) { - return; - } - if (this.call) { - this.trigger('ChatDisconnected', this); - } - this.callParticipantsUpdated(); - }); - this.rebind('onStateChange.callManager', function (e, oldState, newState) { - if (newState === ChatRoom.STATE.LEFT && this.call) { - this.call.hangUp(SfuClient.TermCode.kLeavingRoom); - } - }); - this.rebind('onCallPeerLeft.callManager', (e, data) => { - const { - call - } = this; - if (!call || call.isDestroyed || call.hasOtherParticipant() || SfuClient.isTermCodeRetriable(data.reason)) { - return; - } - if (this.type === 'private') { - return this.trigger('onCallLeft', { - callId: call.callId - }); - } - setTimeout(() => { - if (this.call === call) { - this.call.initCallTimeout(); - } - }, 3000); - }); - this.rebind('onMeAdded', (e, addedBy) => { - if (this.activeCallIds.length > 0) { - const callId = this.activeCallIds.keys()[0]; - if (this.ringingCalls.exists(callId)) { - return; - } - this.ringingCalls.set(callId, addedBy); - this.megaChat.trigger('onIncomingCall', [this, callId, addedBy, callMgr]); - this.fakedLocalRing = true; - setTimeout(() => { - delete this.fakedLocalRing; - if (this.ringingCalls.exists(callId)) { - callMgr.trigger("onRingingStopped", { - callId, - chatRoom: this - }); - } - }, 30e3); - } - }); -}; -ChatRoom.prototype.stateIsLeftOrLeaving = function () { - return this.state == ChatRoom.STATE.LEFT || this.state == ChatRoom.STATE.LEAVING || (!is_chatlink && this.state === ChatRoom.STATE.READY && this.membersSetFromApi && !this.membersSetFromApi.members.hasOwnProperty(u_handle) || is_chatlink && !this.members.hasOwnProperty(u_handle)); -}; -ChatRoom.prototype._clearChatMessagesFromChatd = function () { - this.chatd.shards[this.chatShard].retention(base64urldecode(this.chatId), 1); -}; -ChatRoom.prototype.isReadOnly = function () { - if (this.type === "private") { - const members = this.getParticipantsExceptMe(); - if (members[0] && !Object(M.u[members[0]]).c) { - return true; - } - } - return this.members && this.members[u_handle] <= 0 || !this.members.hasOwnProperty(u_handle) || this.privateReadOnlyChat || this.state === ChatRoom.STATE.LEAVING || this.state === ChatRoom.STATE.LEFT; -}; -ChatRoom.prototype.iAmOperator = function () { - return this.type === 'private' || this.members && this.members[u_handle] === ChatRoom.MembersSet.PRIVILEGE_STATE.OPERATOR; -}; -ChatRoom.prototype.iAmReadOnly = function () { - return this.type !== 'private' && this.members && this.members[u_handle] === ChatRoom.MembersSet.PRIVILEGE_STATE.READONLY; -}; -ChatRoom.prototype.iAmWaitingRoomPeer = function () { - return this.options.w && !this.iAmOperator(); -}; -ChatRoom.prototype.didInteraction = function (user_handle, ts) { - const self = this; - const newTs = ts || unixtime(); - if (user_handle === u_handle) { - Object.keys(self.members).forEach((user_handle) => { - const contact = M.u[user_handle]; - if (contact && user_handle !== u_handle && contact.c === 1) { - setLastInteractionWith(contact.u, `1:${ newTs}`); - } - }); - } else { - const contact = M.u[user_handle]; - if (contact && user_handle !== u_handle && contact.c === 1) { - setLastInteractionWith(contact.u, `1:${ newTs}`); - } - } -}; -ChatRoom.prototype.retrieveAllHistory = function () { - const self = this; - self.messagesBuff.retrieveChatHistory().done(() => { - if (self.messagesBuff.haveMoreHistory()) { - self.retrieveAllHistory(); - } - }); -}; -ChatRoom.prototype.seedRoomKeys = async function (keys) { - assert(Array.isArray(keys) && keys.length, `Invalid keys parameter for seedRoomKeys.`, keys); - if (d > 2) { - this.logger.warn('Seeding room keys...', keys); - } - const promises = [ChatdIntegration._ensureKeysAreLoaded(keys, undefined, this.publicChatHandle)]; - if (!this.protocolHandler) { - promises.push(ChatdIntegration._waitForProtocolHandler(this)); - } - if (!this.notDecryptedKeys) { - this.notDecryptedKeys = Object.create(null); - } - for (let i = keys.length; i--;) { - const { - key, - keyid, - keylen, - userId - } = keys[i]; - this.notDecryptedKeys[`${userId}-${keyid}`] = { - userId, - keyid, - keylen, - key - }; - } - const promise = this._keysAreSeeding = Promise.all(promises).then(() => { - const res = this.protocolHandler.seedKeys(keys); - for (let i = res.length; i--;) { - delete this.notDecryptedKeys[res[i]]; - } - return res; - }).catch(ex => { - this.logger.error('Failed to seed room keys!', ex, keys); - throw ex; - }).finally(() => { - if (promise === this._keysAreSeeding) { - delete this._keysAreSeeding; - } - }); - return promise; -}; -ChatRoom.prototype.truncate = function () { - const self = this; - const chatMessages = self.messagesBuff.messages; - if (chatMessages.length > 0) { - let lastChatMessageId = null; - let i = chatMessages.length - 1; - while (lastChatMessageId == null && i >= 0) { - const message = chatMessages.getItem(i); - if (message instanceof Message && message.dialogType !== "truncated") { - lastChatMessageId = message.messageId; - } - i--; - } - if (lastChatMessageId) { - asyncApiReq({ - a: 'mct', - id: self.chatId, - m: lastChatMessageId, - v: Chatd.VERSION - }).catch(ex => { - if (ex === -2) { - msgDialog('warninga', l[135], l[8880]); - } - }); - } - } -}; -ChatRoom.prototype.getActiveCalls = function () { - return this.activeCallIds.map((parts, id) => { - return parts.indexOf(u_handle) > -1 ? id : undefined; - }); -}; -ChatRoom.prototype.haveActiveCall = function () { - return this.getActiveCalls().length > 0; -}; -ChatRoom.prototype.haveActiveOnHoldCall = function () { - const activeCallIds = this.getActiveCalls(); - for (let i = 0; i < activeCallIds.length; i++) { - const call = megaChat.plugins.callManager2.calls[`${this.chatId }_${ activeCallIds[i]}`]; - if (call && call.av & SfuClient.Av.onHold) { - return true; - } - } - return false; -}; -ChatRoom.prototype.havePendingGroupCall = function () { - if (this.type !== "group" && this.type !== "public") { - return false; - } - return this.activeCallIds.length > 0; -}; -ChatRoom.prototype.havePendingCall = function () { - return this.activeCallIds.length > 0; -}; -ChatRoom.prototype.getActiveCallMessageId = function (ignoreActive) { - const self = this; - if (!ignoreActive && !self.havePendingCall() && !self.haveActiveCall()) { - return false; - } - const msgs = self.messagesBuff.messages; - for (let i = msgs.length - 1; i >= 0; i--) { - const msg = msgs.getItem(i); - if (msg.dialogType === "remoteCallEnded") { - return false; - } - if (msg.dialogType === "remoteCallStarted") { - return msg.messageId; - } - } -}; -ChatRoom.prototype.stopRinging = function (callId) { - if (this.ringingCalls.exists(callId)) { - this.ringingCalls.remove(callId); - } - megaChat.plugins.callManager2.trigger("onRingingStopped", { - callId, - chatRoom: this - }); -}; -ChatRoom.prototype.callParticipantsUpdated = function () { - const self = this; - let msgId = self.getActiveCallMessageId(); - if (!msgId) { - msgId = self.getActiveCallMessageId(true); - } - const callParts = self.getCallParticipants() || []; - self.uniqueCallParts = {}; - for (let i = 0; i < callParts.length; i++) { - self.uniqueCallParts[callParts[i]] = true; - } - if (this.callUserLimited && this.canJoinLimitedCall()) { - this.callUserLimited.abort(); - this.callUserLimited = false; - } - const msg = self.messagesBuff.getMessageById(msgId); - msg && msg.wrappedChatDialogMessage && msg.wrappedChatDialogMessage.trackDataChange(); - self.trackDataChange(); -}; -ChatRoom.prototype.onPublicChatRoomInitialized = function () { - const self = this; - if (self.type !== "public" || !localStorage.autoJoinOnLoginChat) { - return; - } - const autoLoginChatInfo = tryCatch(JSON.parse.bind(JSON))(localStorage.autoJoinOnLoginChat) || false; - if (autoLoginChatInfo[0] === self.publicChatHandle) { - localStorage.removeItem("autoJoinOnLoginChat"); - if (unixtime() - 7200 < autoLoginChatInfo[1]) { - const doJoinEventually = function (state) { - if (state === ChatRoom.STATE.READY) { - self.joinViaPublicHandle(); - self.unbind(`onStateChange.${ self.publicChatHandle}`); - } - }; - self.rebind(`onStateChange.${ self.publicChatHandle}`, (e, oldState, newState) => { - doJoinEventually(newState); - }); - doJoinEventually(self.state); - } - } -}; -ChatRoom.prototype.isUIMounted = function () { - return this._uiIsMounted; -}; -ChatRoom.prototype.attachSearch = function () { - this.activeSearches++; -}; -ChatRoom.prototype.detachSearch = function () { - if (--this.activeSearches === 0) { - this.messagesBuff.detachMessages(); - } - this.activeSearches = Math.max(this.activeSearches, 0); - this.trackDataChange(); -}; -ChatRoom.prototype.scrollToMessageId = function (msgId, index, retryActive) { - const self = this; - if (!self.isCurrentlyActive && !retryActive) { - tSleep(1.5).then(() => { - self.scrollToMessageId(msgId, index, true); - }); - return; - } - assert(self.isCurrentlyActive, 'chatRoom is not visible'); - self.isScrollingToMessageId = true; - if (!self.$rConversationPanel) { - self.one(`onHistoryPanelComponentDidMount.scrollToMsgId${ msgId}`, () => { - self.scrollToMessageId(msgId, index); - }); - return; - } - const ps = self.$rConversationPanel.messagesListScrollable; - assert(ps); - const msgObj = self.messagesBuff.getMessageById(msgId); - if (msgObj) { - const elem = $(`.${ msgId }.message.body`)[0]; - self.scrolledToBottom = false; - ps.scrollToElement(elem, true); - self.$rConversationPanel.lastScrollPosition = undefined; - self.isScrollingToMessageId = false; - } else if (self.messagesBuff.isRetrievingHistory) { - self.one(`onHistoryDecrypted.scrollToMsgId${ msgId}`, () => { - self.one(`onComponentDidUpdate.scrollToMsgId${ msgId}`, () => { - self.scrollToMessageId(msgId, index); - }); - }); - } else if (self.messagesBuff.haveMoreHistory()) { - self.messagesBuff.retrieveChatHistory(!index || index <= 0 ? undefined : index); - ps.doProgramaticScroll(0, true); - self.one(`onHistoryDecrypted.scrollToMsgId${ msgId}`, () => { - self.one(`onComponentDidUpdate.scrollToMsgId${ msgId}`, () => { - self.scrollToMessageId(msgId); - }); - }); - } else { - self.isScrollingToMessageId = false; - } -}; -ChatRoom.prototype.setMcoFlags = function (flags) { - const req = { - a: 'mco', - cid: this.chatId, - ...flags - }; - asyncApiReq(req).dump('roomSetCallFlags'); -}; -ChatRoom.prototype.toggleOpenInvite = function () { - if (this.type === 'private' || !this.iAmOperator()) { - return; - } - this.setMcoFlags({ - [MCO_FLAGS.OPEN_INVITE]: Math.abs(this.options[MCO_FLAGS.OPEN_INVITE] - 1) - }); -}; -ChatRoom.prototype.toggleWaitingRoom = function () { - if (this.type === 'private' || !this.iAmOperator()) { - return; - } - this.setMcoFlags({ - [MCO_FLAGS.WAITING_ROOM]: Math.abs(this.options[MCO_FLAGS.WAITING_ROOM] - 1) - }); -}; -ChatRoom.prototype.exportToFile = function () { - if (this.messagesBuff.messages.length === 0 || this.exportIo) { - return; - } - loadingDialog.show('chat_export'); - eventlog(99874); - this._exportChat().then(() => { - eventlog(99875, JSON.stringify([1])); - }).catch(ex => { - if (d) { - console.warn('Chat export: ', ex); - } - const report = [String(ex && ex.message || ex).replace(/\s+/g, '').substring(0, 64)]; - report.unshift(report[0] === 'Aborted' ? 1 : 0); - if (!report[0]) { - msgDialog('error', '', l.export_chat_failed, '', undefined, 1); - } - eventlog(99875, JSON.stringify(report)); - }).finally(() => { - loadingDialog.hide('chat_export'); - this.isScrollingToMessageId = false; - onIdle(() => this.messagesBuff.detachMessages()); - }); -}; -ChatRoom.prototype._exportChat = async function () { - this.isScrollingToMessageId = true; - while (this.messagesBuff.haveMoreHistory()) { - await this.messagesBuff.retrieveChatHistory(100); - } - await Promise.allSettled([this.messagesBuff.isDecrypting || Promise.resolve(), this.messagesBuff.$sharedFilesLoading || Promise.resolve(), this.messagesBuff.$isDecryptingSharedFiles || Promise.resolve()]); - do { - await this.messagesBuff.retrieveSharedFilesHistory(100); - } while (this.messagesBuff.haveMoreSharedFiles); - let withMedia = !!M.v.length; - if (withMedia) { - withMedia = await asyncMsgDialog(`*confirmation:!^${l.export_chat_media_dlg_conf}!${l.export_chat_media_dlg_rej}`, '', l.export_chat_media_dlg_title, l.export_chat_media_dlg_text); - if (withMedia === null) { - throw new Error('Aborted'); - } - } - let { - attachNodes, - stringNodes - } = this.messagesBuff.getExportContent(withMedia); - stringNodes = stringNodes.join('\n'); - const basename = M.getSafeName(this.getRoomTitle()); - const zname = l.export_chat_zip_file.replace('%s', basename); - const bufferName = l.export_chat_text_file.replace('%s', basename); - if (attachNodes.length) { - const p = []; - const n = []; - let s = 0; - for (const node of attachNodes) { - s += node.s; - if (node.ph) { - p.push(node.ph); - } else { - n.push(node.h); - } - } - const res = await asyncApiReq({ - a: 'qbq', - s, - n, - p - }); - if (res === 1 || res === 2) { - const fallback = await asyncMsgDialog('confirmation', '', l.export_chat_media_obq_title, l.export_chat_media_obq_text); - if (fallback) { - return M.saveAs(stringNodes, bufferName); - } - } else if (res === 0) { - await M.require('clientzip_js'); - const data = new TextEncoder().encode(stringNodes); - const dl = { - size: data.byteLength + s, - n: bufferName, - t: unixtime(), - id: this.chatId, - p: '', - io: Object.create(null), - writer: Object.create(null), - offset: 0, - zname - }; - const io = await prepareExportIo(dl); - const t = new Date((this.lastActivity || this.ctime) * 1000); - let failedCount = 0; - const src = prepareExportStreams(attachNodes, size => { - failedCount++; - dl.done += size; - }); - src.unshift({ - name: bufferName, - lastModified: t, - input: data.buffer - }); - dl.done = 0; - const reader = clientZip.downloadZip(src).body.getReader(); - dl.nextChunk = async () => { - const read = await reader.read().catch(dump); - if (!read) { - reader.cancel().catch(ex => { - if (ex !== EOVERQUOTA) { - msgDialog('error', '', l.export_chat_failed, ex < 0 ? api_strerror(ex) : ex, undefined, 1); - } - }); - io.abort(); - delete this.exportIo; - loadingDialog.hideProgress(); - return; - } - if (read.done) { - loadingDialog.hideProgress(); - io.download(zname); - delete this.exportIo; - if (failedCount) { - msgDialog('error', '', l.export_chat_failed, l.export_chat_partial_fail, undefined, 1); - } - } else { - dl.done += read.value.byteLength; - loadingDialog.showProgress(dl.done / dl.size * 100); - io.write(read.value, dl.offset, dl.nextChunk); - dl.offset += read.value.length; - } - }; - io.begin = dl.nextChunk; - io.setCredentials(false, dl.size, zname); - this.exportIo = io; - } else { - throw new Error(`Unexpected qbq response ${res}`); - } - } else { - return M.saveAs(stringNodes, bufferName); - } -}; -ChatRoom.prototype.canJoinLimitedCall = function () { - const callParts = this.getCallParticipants(); - return this.iAmOperator() && callParts.length < CallManager2.CALL_USER_LIMIT || callParts.length < CallManager2.CALL_USER_LIMIT - 1; -}; -window.ChatRoom = ChatRoom; -const chatRoom = { - ChatRoom -}; - -}, - -137 -(_, EXP_, REQ_) { - -"use strict"; -REQ_.d(EXP_, { -LP: () => getUniqueId, -N9: () => timing, -Zz: () => compose, -hG: () => SoonFcWrap, -u9: () => ContactAwareComponent, -w9: () => MegaRenderMixin -}); - -const _applyDecoratedDescriptor0__ = REQ_(793); -const react_dom1__ = REQ_(206); -const react_dom1 = REQ_.n(react_dom1__); -const react2__ = REQ_(594); -const react2 = REQ_.n(react2__); - -let _dec, _dec2, _dec3, _dec4, _dec5, _class; - - -const INTERSECTION_OBSERVER_AVAILABLE = typeof IntersectionObserver !== 'undefined'; -const RESIZE_OBSERVER_AVAILABLE = typeof ResizeObserver !== 'undefined'; -function shallowEqual(objA, objB) { - if (objA === objB) { - return true; - } - for (var key in objA) { - if (key === "children") { - continue; - } - if (objA.hasOwnProperty(key)) { - if (!objB.hasOwnProperty(key)) { - return false; - } else if (objA[key] !== objB[key]) { - if (typeof objA[key] === 'function' && typeof objB[key] === 'function') { - if (objA[key].toString() !== objB[key].toString()) { - return false; - } - } else { - return false; - } - } - } - } - for (key in objB) { - if (objB.hasOwnProperty(key) && !objA.hasOwnProperty(key)) { - return false; - } - } - return true; -} -window.shallowEqual = shallowEqual; -const MAX_ALLOWED_DEBOUNCED_UPDATES = 5; -const DEBOUNCED_UPDATE_TIMEOUT = 60; -const REENABLE_UPDATES_AFTER_TIMEOUT = 300; -const MAX_TRACK_CHANGES_RECURSIVE_DEPTH = 1; -let _propertyTrackChangesVars = Object.create(null); -_propertyTrackChangesVars._listenersMap = Object.create(null); -_propertyTrackChangesVars._dataChangedHistory = Object.create(null); -if (window._propertyTrackChangesVars) { - _propertyTrackChangesVars = window._propertyTrackChangesVars; -} else { - window._propertyTrackChangesVars = _propertyTrackChangesVars; -} -window.megaRenderMixinId = window.megaRenderMixinId ? window.megaRenderMixinId : 0; -const FUNCTIONS = ['render', 'shouldComponentUpdate', 'doProgramaticScroll', 'componentDidMount', 'componentDidUpdate', 'componentWillUnmount', 'refreshUI', 'eventuallyInit', 'handleWindowResize', 'focusTypeArea', 'initScrolling', 'updateScroll', 'isActive', 'onMessagesScrollReinitialise', 'specShouldComponentUpdate', 'attachAnimationEvents', 'eventuallyReinitialise', 'reinitialise', 'reinitialised', 'getContentHeight', 'getScrollWidth', 'isAtBottom', 'onResize', 'isComponentEventuallyVisible', 'getCursorPosition', 'getTextareaMaxHeight']; -const localStorageProfileRenderFns = localStorage.profileRenderFns; -if (localStorageProfileRenderFns) { - window.REACT_RENDER_CALLS = {}; -} -let ID_CURRENT = 1; -const DEBUG_THIS = d > 1 ? d : false; -const scheduler = (func, name, debug) => { - const dbug = debug !== false && DEBUG_THIS; - let idnt = null; - let task = null; - const fire = () => { - if (dbug) { - console.warn('Dispatching scheduled task for %s.%s...', idnt, name); - } - if (task) { - queueMicrotask(task); - task = null; - } - }; - const _scheduler = function () { - if (dbug) { - if (!idnt) { - idnt = name[0] === '(' && this.getReactId && this.getReactId() || this; - } - console.warn('Scheduling task from %s.%s...', idnt, name, [this], !!task); - } - if (!task) { - queueMicrotask(fire); - } - let idx = arguments.length; - const args = new Array(idx); - while (idx--) { - args[idx] = arguments[idx]; - } - task = () => { - func.apply(this, args); - }; - }; - if (DEBUG_THIS) { - Object.defineProperty(_scheduler, smbl(name), { - value: func - }); - } - return _scheduler; -}; -const timing = (min, max) => { - return function (target, key, de) { - if (DEBUG_THIS > 2) { - de[key] = de.value; - _timing(de, min, max); - de.value = de[key]; - } - return de; - }; -}; -const logcall = () => { - return function (target, key, descriptor) { - if (DEBUG_THIS > 3) { - const func = descriptor.value; - descriptor.value = function () { - console.group('[logcall] Entering into %s.%s...', this, key); - const r = func.apply(this, arguments); - console.info('[logcall] Leaving %s.%s...', this, key); - console.groupEnd(); - return r; - }; - } - return descriptor; - }; -}; -const schedule = (local, debug) => { - return function (target, property, descriptor) { - if (local) { - const func = descriptor.value; - descriptor = { - configurable: true, - get: function _unusedScheduler() { - Object.defineProperty(this, property, { - value: scheduler(func, `(${ property })`, debug) - }); - return this[property]; - } - }; - } else { - descriptor.value = scheduler(descriptor.value, property, debug); - } - return descriptor; - }; -}; -const compose = (...funcs) => funcs.reduce((a, b) => (...args) => a(b(...args)), arg => arg); -const replaceAt = (i, o, n) => `${o.slice(0, i)}${n}${o.slice(i + n.length)}`; -const SoonFcWrap = (milliseconds, local) => { - return function (target, propertyKey, descriptor) { - descriptor.value = SoonFc(descriptor.value, !local, milliseconds); - return descriptor; - }; -}; -const rAFWrap = () => { - return function (target, propertyKey, descriptor) { - const old = descriptor.value; - descriptor.value = function () { - return old.apply(this, arguments); - }; - return descriptor; - }; -}; -const trycatcher = () => (t, p, d) => (d.value = tryCatch(d.value)) && d; -const getUniqueId = () => makeUUID().slice(-12); -const MegaRenderMixin = (_dec = logcall(), _dec2 = SoonFcWrap(50, true), _dec3 = logcall(), _dec4 = SoonFcWrap(80, true), _dec5 = SoonFcWrap(350, true), _class = class MegaRenderMixin extends react2().Component { - constructor(props) { - super(props); - lazy(this, '__internalReactID', function () { - let key = ''; - let fib = DEBUG_THIS && this._reactInternalFiber; - while (fib) { - let tmp = fib.key; - if (tmp && tmp[0] !== '.' && key.indexOf(tmp) < 0) { - key += `${tmp }/`; - } - if (tmp = fib.memoizedProps) { - if (tmp.contact) { - tmp = tmp.contact.u + (tmp.chatRoom ? `@${ tmp.chatRoom.roomId}` : ''); - } else if (tmp.chatRoom) { - tmp = tmp.chatRoom.roomId; - } else { - tmp = 0; - } - if (tmp && key.indexOf(tmp) < 0) { - key += `${tmp }/`; - } - } - fib = fib._debugOwner; - } - key = key ? `[${ key.substr(0, key.length - 1) }]` : ''; - return `::${ this.constructor.name }[${ `000${ ID_CURRENT++}`.slice(-4) }]${ key}`; - }); - lazy(this, '__internalUniqueID', function () { - return (this.__internalReactID + makeUUID().substr(-12)).replace(/[^a-zA-Z0-9]/g, ''); - }); - Object.defineProperty(this, 'isMounted', { - value: function MegaRenderMixin_isMounted() { - return !!this.__isMounted; - } - }); - if (DEBUG_THIS > 2) { - Object.defineProperty(this, 'safeForceUpdate', { - value: function MegaRenderMixin_safeForceUpdate_debug() { - console.group('%s.safeForceUpdate: mounted:%s, visible:%s', this.getReactId(), this.__isMounted, this.isComponentEventuallyVisible()); - if (this.__isMounted) { - this.forceUpdate(() => { - console.warn('%s.safeForceUpdate finished.', this.getReactId()); - console.groupEnd(); - }); - } - } - }); - Object.keys(this).forEach(k => { - if (this[k] && this[k].apply) { - const orig = this[k]; - this[k] = function () { - let s = performance.now(); - const r = orig.apply(this, arguments); - s = performance.now() - s; - if (s > 30) { - console.error(k, this, "took", s, "ms", 'returned', r); - } - return r; - }; - } - }); - } - if (DEBUG_THIS) { - if (!megaChat.__components) { - megaChat.__components = new WeakMap(); - } - megaChat.__components.set(this, Object.getPrototypeOf(this)); - } - } - componentWillUnmount() { - if (super.componentWillUnmount) { - super.componentWillUnmount(); - } - this.__isMounted = false; - chatGlobalEventManager.removeEventListener('resize', `megaRenderMixing${ this.getUniqueId()}`); - chatGlobalEventManager.removeEventListener('hashchange', `hc${ this.getUniqueId()}`); - const node = this.findDOMNode(); - if (this.__intersectionObserverInstance) { - if (node) { - this.__intersectionObserverInstance.unobserve(node); - } - this.__intersectionObserverInstance.disconnect(); - this.__intersectionObserverInstance = undefined; - } - if (this.onResizeObserved) { - if (!RESIZE_OBSERVER_AVAILABLE) { - $(document.body).unbind(`resize.resObs${ this.getUniqueId()}`); - } else { - this.__resizeObserverInstance.unobserve(node); - this.__resizeObserverInstance.disconnect(); - this.__resizeObserverInstance = undefined; - } - } - const instanceId = this.getUniqueId(); - const listeners = _propertyTrackChangesVars._listenersMap[instanceId]; - if (listeners) { - for (const k in listeners) { - const v = listeners[k]; - v[0].removeChangeListener(v[1]); - } - } - _propertyTrackChangesVars._listenersMap[instanceId] = null; - _propertyTrackChangesVars._dataChangedHistory[instanceId] = null; - if (this._dataStructListeners) { - this._internalDetachRenderCallbacks(); - } - if (this.detachRerenderCallbacks) { - this.detachRerenderCallbacks(); - } - } - getReactId() { - return this.__internalReactID; - } - getUniqueId() { - return this.__internalUniqueID; - } - debouncedForceUpdate() { - this.eventuallyUpdate(); - } - componentDidMount() { - if (super.componentDidMount) { - super.componentDidMount(); - } - this.__isMounted = true; - this._wasRendered = true; - if (this.props.requiresUpdateOnResize || this.requiresUpdateOnResize || !this.props.skipQueuedUpdatesOnResize) { - chatGlobalEventManager.addEventListener('resize', `megaRenderMixing${ this.getUniqueId()}`, () => this.onResizeDoUpdate()); - } - chatGlobalEventManager.addEventListener('hashchange', `hc${ this.getUniqueId()}`, () => this.onResizeDoUpdate()); - if (this.props) { - this._recurseAddListenersIfNeeded("p", this.props); - } - if (this.state) { - this._recurseAddListenersIfNeeded("s", this.state); - } - const node = this.findDOMNode(); - if (INTERSECTION_OBSERVER_AVAILABLE && !this.customIsEventuallyVisible && node && node.nodeType) { - this.__intersectionVisibility = false; - onIdle(() => { - this.__intersectionObserverInstance = new IntersectionObserver(entries => { - const entry = entries.pop(); - if (entry.intersectionRatio < 0.2 && !entry.isIntersecting) { - this.__intersectionVisibility = false; - } else { - this.__intersectionVisibility = true; - if (this._requiresUpdateOnResize) { - this.debouncedForceUpdate(); - } - } - if (this.onVisibilityChange) { - this.onVisibilityChange(this.__intersectionVisibility); - } - }, { - threshold: 0.1 - }); - this.__intersectionObserverInstance.observe(node); - }); - } - if (this.onResizeObserved) { - if (!RESIZE_OBSERVER_AVAILABLE) { - $(document.body).rebind(`resize.resObs${ this.getUniqueId()}`, () => { - this.onResizeObserved(node.offsetWidth, node.offsetHeight); - }); - } else { - this.__resizeObserverInstance = new ResizeObserver(entries => { - this.onResizeObserved(entries[0].contentRect.width, entries[0].contentRect.height); - }); - this.__resizeObserverInstance.observe(node); - } - } - if (this.attachRerenderCallbacks) { - this.attachRerenderCallbacks(); - } - } - findDOMNode() { - if (!this.domNode) { - let _this$domRef; - this.domNode = (_this$domRef = this.domRef) == null ? void 0 : _this$domRef.current; - } - return this.domNode; - } - isComponentVisible() { - if (!this.__isMounted) { - return false; - } - if (this.customIsEventuallyVisible) { - const ciev = this.customIsEventuallyVisible; - const result = typeof ciev === "function" ? ciev.call(this) : ciev; - if (result !== -1) { - return result; - } - } - if (this.__intersectionVisibility === false) { - return false; - } else if (this.__intersectionVisibility === true) { - return true; - } - const domNode = this.findDOMNode(); - if (!this.props.hideable && (!domNode || domNode.offsetParent === null)) { - return false; - } - if (!$(domNode).is(":visible")) { - return false; - } - return verge.inViewport(domNode); - } - isComponentEventuallyVisible() { - if (!this.__isMounted) { - return false; - } - if (this.customIsEventuallyVisible) { - const ciev = this.customIsEventuallyVisible; - return typeof ciev === "function" ? ciev.call(this) : !!ciev; - } - if (typeof this.props.isVisible !== 'undefined') { - return this.props.isVisible; - } - return this.__intersectionVisibility !== false; - } - eventuallyUpdate() { - if (!window.megaChat || megaChat.isLoggingOut || this._updatesDisabled || !this._wasRendered || !this.__isMounted) { - return; - } - if (!this.isComponentEventuallyVisible()) { - this._requiresUpdateOnResize = true; - return; - } - if (this._requiresUpdateOnResize) { - this._requiresUpdateOnResize = false; - } - this.forceUpdate(); - } - tempDisableUpdates(forHowLong) { - const self = this; - self._updatesDisabled = true; - if (self._updatesReenableTimer) { - clearTimeout(self._updatesReenableTimer); - } - const timeout = forHowLong ? forHowLong : self.REENABLE_UPDATES_AFTER_TIMEOUT ? self.REENABLE_UPDATES_AFTER_TIMEOUT : REENABLE_UPDATES_AFTER_TIMEOUT; - self._updatesReenableTimer = setTimeout(() => { - self.tempEnableUpdates(); - }, timeout); - } - tempEnableUpdates() { - clearTimeout(this._updatesReenableTimer); - this._updatesDisabled = false; - this.eventuallyUpdate(); - } - onResizeDoUpdate() { - this.eventuallyUpdate(); - } - _getUniqueIDForMap(map, payload) { - return `${map }.${ payload}`; - } - _recurseAddListenersIfNeeded(idx, map, depth) { - depth |= 0; - if (map instanceof MegaDataMap && !(this._contactChangeListeners && this._contactChangeListeners.includes(map))) { - const cacheKey = this._getUniqueIDForMap(map, idx); - const instanceId = this.getUniqueId(); - if (!_propertyTrackChangesVars._listenersMap[instanceId]) { - _propertyTrackChangesVars._listenersMap[instanceId] = Object.create(null); - } - if (!_propertyTrackChangesVars._listenersMap[instanceId][cacheKey]) { - _propertyTrackChangesVars._listenersMap[instanceId][cacheKey] = [map, map.addChangeListener(() => this.onPropOrStateUpdated())]; - } - } - if (depth++ < MAX_TRACK_CHANGES_RECURSIVE_DEPTH && !this.props.manualDataChangeTracking) { - const mapKeys = map instanceof MegaDataMap ? map.keys() : Object.keys(map); - for (let i = 0; i < mapKeys.length; i++) { - const k = mapKeys[i]; - if (map[k]) { - this._recurseAddListenersIfNeeded(`${idx }_${ k}`, map[k], depth); - } - } - } - } - _checkDataStructForChanges(idx, v, rv, depth) { - if (!v && v === rv) { - return false; - } - if (!rv && v) { - return true; - } - if (v === null) { - return rv !== null; - } - if (v instanceof MegaDataMap) { - const cacheKey = this._getUniqueIDForMap(v, idx); - const dataChangeHistory = _propertyTrackChangesVars._dataChangedHistory; - const instanceId = this.getUniqueId(); - if (!dataChangeHistory[instanceId]) { - dataChangeHistory[instanceId] = Object.create(null); - } - if (dataChangeHistory[instanceId][cacheKey] !== v._dataChangeIndex) { - if (window.RENDER_DEBUG) { - console.error("changed: ", this.getElementName(), cacheKey, v._dataChangeTrackedId, v._dataChangeIndex, v); - } - dataChangeHistory[instanceId][cacheKey] = v._dataChangeIndex; - return true; - } - return false; - } - return depth < MAX_TRACK_CHANGES_RECURSIVE_DEPTH && v && v.byteLength === undefined && typeof v === "object" && this._recursiveSearchForDataChanges(idx, v, rv, depth + 1) === true; - } - _recursiveSearchForDataChanges(idx, map, referenceMap, depth) { - const self = this; - depth = depth || 0; - if (!this.isMounted() || this._updatesDisabled === true) { - return; - } - if (!this._wasRendered) { - if (window.RENDER_DEBUG) console.error("First time render", self.getElementName(), map, referenceMap); - this._wasRendered = true; - return true; - } - if (idx === "p_children") { - if (map.map && referenceMap.map) { - const oldKeys = map.map((child) => { - return child ? child.key : child; - }); - const newKeys = referenceMap.map((child) => { - return child ? child.key : child; - }); - if (!shallowEqual(oldKeys, newKeys)) { - return true; - } - } else if (!map && referenceMap || map && !referenceMap) { - return true; - } else if (map.$$typeof && referenceMap.$$typeof) { - if (!shallowEqual(map.props, referenceMap.props) || !shallowEqual(map.state, referenceMap.state)) { - return true; - } - } - } else if (map && !referenceMap || !map && referenceMap || map && referenceMap && !shallowEqual(map, referenceMap)) { - return true; - } - const mapKeys = map instanceof MegaDataMap ? map.keys() : Object.keys(map); - for (let i = mapKeys.length; i--;) { - const k = mapKeys[i]; - if (this._checkDataStructForChanges(`${idx }_${ k}`, map[k], referenceMap[k], depth)) { - return true; - } - } - return false; - } - shouldComponentUpdate(nextProps, nextState) { - let shouldRerender = false; - if (megaChat && megaChat.isLoggingOut) { - return false; - } - if (!this.isMounted() || this._updatesDisabled === true) { - if (window.RENDER_DEBUG) { - console.error("shouldUpdate? No.", "F1", this.getElementName(), this.props, nextProps, this.state, nextState); - } - return false; - } - if (this.customIsEventuallyVisible) { - let ciev = this.customIsEventuallyVisible; - ciev = typeof ciev === "function" ? ciev.call(this) : !!ciev; - if (!this._queueUpdateWhenVisible && !ciev) { - this._queueUpdateWhenVisible = true; - if (window.RENDER_DEBUG) { - console.error("shouldUpdate? No.", "F1.1", this.getElementName(), this.props, nextProps, this.state, nextState); - } - } else if (this._queueUpdateWhenVisible && ciev) { - delete this._queueUpdateWhenVisible; - return true; - } - } - if (this.specShouldComponentUpdate) { - const r = this.specShouldComponentUpdate(nextProps, nextState); - if (r === false) { - if (window.RENDER_DEBUG) { - console.error("shouldUpdate? No.", "F2", this.getElementName(), this.props, nextProps, this.state, nextState); - } - this._requiresUpdateOnResize = true; - return false; - } else if (r === true) { - return true; - } - } - if (!this.props.disableCheckingVisibility && !this.isComponentEventuallyVisible()) { - if (window.RENDER_DEBUG) { - console.error("shouldUpdate? No.", "FVis", this.getElementName(), this.props, nextProps, this.state, nextState); - } - this._requiresUpdateOnResize = true; - return false; - } - if (this.props !== null) { - shouldRerender = this._recursiveSearchForDataChanges("p", nextProps, this.props); - } - if (shouldRerender === false) { - if (window.RENDER_DEBUG) { - console.error("shouldUpdate? No.", "F3", this.getElementName(), this.props, nextProps, this.state, nextState); - } - } - if (shouldRerender === false && this.state !== null) { - shouldRerender = this._recursiveSearchForDataChanges("s", nextState, this.state); - } - if (window.RENDER_DEBUG) { - if (shouldRerender) {} - console.error("shouldRerender?", shouldRerender, "rendered: ", this.getElementName(), "props:", this.props, "nextProps:", this.props, "state:", this.state); - } - if (shouldRerender === true) { - if (this.props) { - this._recurseAddListenersIfNeeded("p", this.props); - } - if (this.state) { - this._recurseAddListenersIfNeeded("s", this.state); - } - } else { - if (window.RENDER_DEBUG) { - console.error("shouldUpdate? No.", "F4", this.getElementName(), this.props, nextProps, this.state, nextState); - } - } - return shouldRerender; - } - onPropOrStateUpdated() { - this.eventuallyUpdate(); - } - getElementName() { - return this._reactInternalFiber.elementType.name; - } - safeForceUpdate() { - if (this.__isMounted) { - this.forceUpdate(); - } - } - componentDidUpdate() { - if (window.RENDER_DEBUG) { - const self = this; - const getElementName = function () { - if (!self.constructor) { - return "unknown"; - } - return self.constructor.name; - }; - console.error("renderedX: ", getElementName(), "props:", this.props, "state:", this.state); - } - if (this.domNode && !this.domNode.isConnected) { - delete this.domNode; - } - } - UNSAFE_componentWillReceiveProps(nextProps, nextContext) { - if (localStorageProfileRenderFns) { - const self = this; - const componentName = self.constructor ? self.constructor.name : "unknown"; - if (!this._wrappedRender) { - FUNCTIONS.forEach((fnName) => { - const _origFn = self[fnName]; - if (_origFn) { - self[fnName] = function () { - const start = performance.now(); - const res = _origFn.apply(this, arguments); - REACT_RENDER_CALLS[`${componentName }.${ fnName}`] = REACT_RENDER_CALLS[`${componentName }.${ fnName}`] || 0; - REACT_RENDER_CALLS[`${componentName }.${ fnName}`] += performance.now() - start; - return res; - }; - } - }); - self._wrappedRender = true; - } - REACT_RENDER_CALLS.sorted = function () { - const sorted = []; - Object.keys(REACT_RENDER_CALLS).sort((a, b) => { - if (REACT_RENDER_CALLS[a] < REACT_RENDER_CALLS[b]) { - return 1; - } else if (REACT_RENDER_CALLS[a] > REACT_RENDER_CALLS[b]) { - return -1; - } else { - return 0; - } - }).forEach((k) => { - if (typeof REACT_RENDER_CALLS[k] !== 'function') { - sorted.push([k, REACT_RENDER_CALLS[k]]); - } - }); - return sorted; - }; - REACT_RENDER_CALLS.clear = function () { - Object.keys(REACT_RENDER_CALLS).forEach((k) => { - if (typeof REACT_RENDER_CALLS[k] !== 'function') { - delete REACT_RENDER_CALLS[k]; - } - }); - }; - } - } - _internalDetachRenderCallbacks() { - const items = this._dataStructListeners || false; - for (let i = items.length; i--;) { - const item = items[i]; - if (item[0] === 'dsprops') { - console.assert(item[2].removeChangeListener(item[1]), 'listener not found..'); - } - } - } - addDataStructListenerForProperties(obj, properties) { - if (!(obj instanceof MegaDataMap)) { - return; - } - if (!this._dataStructListeners) { - this._dataStructListeners = []; - } - properties = array.to.object(properties); - const id = obj.addChangeListener((obj, data, k) => properties[k] && this.onPropOrStateUpdated()); - this._dataStructListeners.push(['dsprops', id, obj]); - } -}, (0,_applyDecoratedDescriptor0__.A)(_class.prototype, "componentWillUnmount", [_dec], Object.getOwnPropertyDescriptor(_class.prototype, "componentWillUnmount"), _class.prototype), (0,_applyDecoratedDescriptor0__.A)(_class.prototype, "debouncedForceUpdate", [_dec2], Object.getOwnPropertyDescriptor(_class.prototype, "debouncedForceUpdate"), _class.prototype), (0,_applyDecoratedDescriptor0__.A)(_class.prototype, "componentDidMount", [_dec3], Object.getOwnPropertyDescriptor(_class.prototype, "componentDidMount"), _class.prototype), (0,_applyDecoratedDescriptor0__.A)(_class.prototype, "eventuallyUpdate", [_dec4], Object.getOwnPropertyDescriptor(_class.prototype, "eventuallyUpdate"), _class.prototype), (0,_applyDecoratedDescriptor0__.A)(_class.prototype, "onResizeDoUpdate", [_dec5], Object.getOwnPropertyDescriptor(_class.prototype, "onResizeDoUpdate"), _class.prototype), _class); -class ContactAwareComponent extends MegaRenderMixin { - constructor(props) { - super(props); - this.loadContactInfo(); - } - _validContact() { - const { - contact - } = this.props; - if (!contact) { - return false; - } - return (contact.h || contact.u) in M.u; - } - _attachRerenderCbContacts(others) { - if (!this._validContact()) { - return; - } - this.addDataStructListenerForProperties(this.props.contact, ['name', 'firstName', 'lastName', 'nickname', 'm', 'avatar'].concat(Array.isArray(others) ? others : [])); - } - attachRerenderCallbacks() { - this._attachRerenderCbContacts(); - } - loadContactInfo() { - let _contact$avatar; - if (!this._validContact()) { - return; - } - const { - contact, - chatRoom - } = this.props; - const contactHandle = contact.h || contact.u; - const syncName = !ContactAwareComponent.unavailableNames[contactHandle] && !contact.firstName && !contact.lastName; - const syncMail = megaChat.FORCE_EMAIL_LOADING || (contact.c === 1 || contact.c === 2) && !contact.m && !is_chatlink; - const syncAvtr = (is_chatlink && (!contact.avatar || ((_contact$avatar = contact.avatar) == null ? void 0 : _contact$avatar.type) === "text") || !contact.avatar) && !avatars[contactHandle] && !ContactAwareComponent.unavailableAvatars[contactHandle]; - const loader = () => { - if (!this.isComponentEventuallyVisible()) { - this.__isLoadingContactInfo = null; - this._requiresUpdateOnResize = true; - return; - } - const promises = []; - const chatHandle = is_chatlink.ph || chatRoom && chatRoom.publicChatHandle; - if (syncName) { - promises.push(megaChat.plugins.userHelper.getUserName(contactHandle, chatHandle)); - } - if (syncMail) { - promises.push(M.syncContactEmail(contactHandle)); - } - if (syncAvtr) { - promises.push(useravatar.loadAvatar(contactHandle, chatHandle).catch(() => { - ContactAwareComponent.unavailableAvatars[contactHandle] = true; - })); - } - return Promise.allSettled(promises).always(() => { - this.eventuallyUpdate(); - this.__isLoadingContactInfo = false; - if (!contact.firstName && !contact.lastName) { - ContactAwareComponent.unavailableNames[contactHandle] = true; - } - }); - }; - if (syncName || syncMail || syncAvtr) { - (this.__isLoadingContactInfo = tSleep(0.3)).then(loader).catch(dump); - } - } - componentDidUpdate() { - super.componentDidUpdate(); - if (this.__isLoadingContactInfo === null) { - this.loadContactInfo(); - } - } - componentWillUnmount() { - super.componentWillUnmount(); - if (this.__isLoadingContactInfo) { - this.__isLoadingContactInfo.abort(); - this.__isLoadingContactInfo = false; - } - } - isLoadingContactInfo() { - return !!this.__isLoadingContactInfo; - } -} -ContactAwareComponent.unavailableAvatars = Object.create(null); -ContactAwareComponent.unavailableNames = Object.create(null); - -}, - -424 -(_, EXP_, REQ_) { - -"use strict"; -REQ_.d(EXP_, { -A: () => ChatToaster -}); -const react0__ = REQ_(594); -const react0 = REQ_.n(react0__); -const _mixins1__ = REQ_(137); -const _meetings_call_jsx2__ = REQ_(3); -const _ui_buttons3__ = REQ_(994); - - - - -const NAMESPACE = 'chat-toast'; -class ChatToaster extends react0().Component { - constructor(props) { - super(props); - this.uid = `${this.constructor.name}--${(0,_mixins1__.LP)()}`; - this.domRef = react0().createRef(); - this.state = { - toast: null, - endTime: 0, - fmToastId: null, - persistentToast: null - }; - this.toasts = []; - this.persistentToasts = []; - } - enqueueToast(e) { - if (this.props.showDualNotifications && e.data.options && e.data.options.persistent) { - this.persistentToasts.push(e.data); - } else { - this.toasts.push(e.data); - } - this.pollToasts(); - } - pollToasts() { - const { - toast: shownToast, - persistentToast: shownPersistentToast - } = this.state; - const { - isRootToaster, - showDualNotifications, - onShownToast - } = this.props; - const now = Date.now(); - if (this.toasts.length + this.persistentToasts.length) { - if (this.domRef.current && (!isRootToaster && _meetings_call_jsx2__.Ay.isExpanded() || M.chat)) { - if (this.toasts.length && !shownToast) { - this.dispatchToast(this.toasts.shift(), now); - } - if (showDualNotifications && this.persistentToasts.length && !shownPersistentToast) { - const persistentToast = this.persistentToasts.shift(); - this.setState({ - persistentToast - }, () => this.pollToasts()); - if (typeof onShownToast === 'function') { - onShownToast(persistentToast); - } - } - } else if (isRootToaster && this.toasts.length && !shownToast) { - const toast = this.toasts.shift(); - this.dispatchToast(toast, now, { - fmToastId: 'tmp' - }); - this.dispatchFMToast(toast); - } - } - } - dispatchFMToast(toast, redraw) { - window.toaster.alerts.medium(...toast.renderFM()).then(fmToastId => { - if (!redraw) { - toast.onShown(fmToastId); - } - this.setState({ - fmToastId - }); - if (toast.updater && typeof toast.updater === 'function') { - toast.updater(); - toast.updateInterval = setInterval(() => { - toast.updater(); - const value = toast.render(); - if (!value) { - window.toaster.alerts.hide(fmToastId); - return this.onClose(toast.options && toast.options.persistent); - } - if (value !== $('span', `#${fmToastId}`).text()) { - $('span', `#${fmToastId}`).text(value); - } - }, 250); - } - }); - } - dispatchToast(toast, now, options = {}) { - const { - fmToastId, - endTime, - silent - } = options; - const { - onShownToast, - onHideToast - } = this.props; - this.setState({ - toast, - endTime: endTime || now + toast.getTTL(), - fmToastId - }, () => { - if (!silent) { - toast.onShown(); - } - this.timeout = setTimeout(() => { - delete this.timeout; - this.setState({ - toast: null, - endTime: 0 - }, () => this.pollToasts()); - if (typeof toast.onEnd === 'function') { - toast.onEnd(); - } - if (typeof onHideToast === 'function') { - onHideToast(toast); - } - if (toast.updateInterval) { - clearInterval(toast.updateInterval); - delete toast.updateInterval; - } - }, endTime ? endTime - now : toast.getTTL()); - }); - if (typeof onShownToast === 'function') { - onShownToast(toast); - } - } - onClose(persistent) { - const { - showDualNotifications, - onHideToast - } = this.props; - const { - toast, - persistentToast - } = this.state; - if (showDualNotifications && persistent) { - if (typeof persistentToast.onEnd === 'function') { - persistentToast.onEnd(); - } - this.setState({ - persistentToast: null - }, () => this.pollToasts()); - if (typeof onHideToast === 'function') { - onHideToast(persistentToast); - } - return; - } - if (toast.updateInterval) { - clearInterval(toast.updateInterval); - delete toast.updateInterval; - } - clearTimeout(this.timeout); - delete this.timeout; - if (typeof toast.onEnd === 'function') { - toast.onEnd(); - } - if (typeof onHideToast === 'function') { - onHideToast(toast); - } - this.setState({ - toast: null, - endTime: 0 - }, () => this.pollToasts()); - } - flush() { - const { - toast, - persistentToast, - fmToastId - } = this.state; - this.endToastIntervals(); - if (fmToastId && fmToastId !== 'tmp') { - window.toaster.alerts.hide(fmToastId); - } - this.toasts = []; - this.persistentToasts = []; - if (this.timeout) { - clearTimeout(this.timeout); - delete this.timeout; - } - if (toast) { - this.onClose(toast.persistent); - } - if (persistentToast) { - this.onClose(true); - } - this.setState({ - toast: null, - endTime: 0, - fmToastId: null, - persistentToast: null - }); - } - endToastIntervals() { - if (!this.props.isRootToaster) { - return; - } - for (const toast of this.toasts) { - if (toast.updateInterval) { - clearInterval(toast.updateInterval); - } - } - for (const toast of this.persistentToasts) { - if (toast.updateInterval) { - clearInterval(toast.updateInterval); - } - } - } - componentDidMount() { - megaChat.rebind(`onChatToast.toaster${this.uid}`, e => this.enqueueToast(e)); - megaChat.rebind(`onChatToastFlush.toaster${this.uid}`, () => this.flush()); - onIdle(() => this.pollToasts()); - if (this.props.isRootToaster) { - this.bpcListener = mBroadcaster.addListener('beforepagechange', tpage => { - const { - toast, - endTime, - fmToastId - } = this.state; - const now = Date.now(); - if (toast && endTime - 500 > now) { - const toChat = tpage.includes('chat') && tpage !== 'securechat'; - if (toChat && !M.chat) { - clearTimeout(this.timeout); - window.toaster.alerts.hide(fmToastId); - if (toast.updateInterval) { - clearInterval(toast.updateInterval); - delete toast.updateInterval; - } - this.dispatchToast(toast, now, { - endTime, - silent: true - }); - } else if (!toChat && M.chat) { - clearTimeout(this.timeout); - this.dispatchToast(toast, now, { - fmToastId: 'tmp', - endTime, - silent: true - }); - this.dispatchFMToast(toast, true); - } - } else if (toast && typeof toast.onEnd === 'function') { - toast.onEnd(); - } - }); - } - } - componentWillUnmount() { - megaChat.off(`onChatToast.toaster${this.uid}`); - megaChat.off(`onChatToastFlush.toaster${this.uid}`); - if (this.bpcListener) { - mBroadcaster.removeListener(this.bpcListener); - } - if (this.timeout) { - clearTimeout(this.timeout); - } - this.endToastIntervals(); - } - render() { - const { - hidden, - isRootToaster, - showDualNotifications - } = this.props; - const { - toast, - fmToastId, - persistentToast - } = this.state; - return !hidden && !fmToastId && react0().createElement("div", { - ref: this.domRef, - className: `chat-toast-bar ${isRootToaster ? 'toaster-root' : ''}` - }, showDualNotifications && persistentToast && react0().createElement(ChatToastMsg, { - toast: persistentToast, - isRootToaster, - usePersistentStyle: true, - onClose: p => this.onClose(p) - }), toast && react0().createElement(ChatToastMsg, { - toast, - isRootToaster, - isDualToast: !!persistentToast, - onClose: p => this.onClose(p) - })); - } -} -class ChatToastMsg extends react0().Component { - constructor(...args) { - super(...args); - this.state = { - value: '' - }; - } - componentDidMount() { - const { - toast, - onClose - } = this.props; - if (toast.updater && typeof toast.updater === 'function') { - toast.updater(); - this.updateInterval = setInterval(() => { - toast.updater(); - const value = toast.render(); - if (!value) { - return onClose(toast.options && toast.options.persistent); - } - if (value !== this.state.value) { - this.setState({ - value - }); - } - }, 250); - } - const value = toast.render(); - if (value) { - this.setState({ - value - }); - } else { - onClose(toast.options && toast.options.persistent); - } - } - componentWillUnmount() { - if (this.updateInterval) { - clearInterval(this.updateInterval); - } - } - render() { - const { - toast, - isRootToaster, - isDualToast, - usePersistentStyle, - onClose - } = this.props; - const { - value - } = this.state; - if (usePersistentStyle && toast.options.persistent) { - return react0().createElement("div", { - className: `${NAMESPACE} chat-persistent-toast` - }, value || toast.render()); - } - const closeButton = toast.close && react0().createElement(_ui_buttons3__.$, { - className: "chat-toast-close", - icon: "sprite-fm-mono icon-close-component", - onClick: onClose - }); - const icon = toast.icon && react0().createElement("i", { - className: toast.icon - }); - if (isRootToaster) { - return react0().createElement("div", { - className: `${NAMESPACE} chat-toast-wrapper root-toast` - }, react0().createElement("div", { - className: "toast-value-wrapper" - }, icon, react0().createElement("div", { - className: "toast-value" - }, value || toast.render())), closeButton); - } - return react0().createElement("div", { - className: `${NAMESPACE} chat-toast-wrapper theme-light-forced ${isDualToast ? 'dual-toast' : ''}` - }, react0().createElement("div", { - className: "toast-value" - }, value || toast.render())); - } -} - -}, - -77 -(_, EXP_, REQ_) { - -"use strict"; - -// EXPORTS -REQ_.d(EXP_, { - A: () => composedTextArea -}); - -// EXTERNAL MODULE: external "React" -const React_ = REQ_(594); -const REaCt = REQ_.n(React_); -;// ./js/chat/ui/whosTyping.jsx - -class WhosTyping extends REaCt().Component { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - this.state = { - currentlyTyping: {} - }; - } - componentDidMount() { - const { - chatRoom - } = this.props; - chatRoom.rebind('onParticipantTyping.whosTyping', (e, user_handle, bCastCode) => { - if (user_handle === u_handle) { - return; - } - const u_h = user_handle; - if (u_h === u_handle) { - return; - } else if (!M.u[u_h]) { - return; - } - const currentlyTyping = { - ...this.state.currentlyTyping - }; - if (currentlyTyping[u_h]) { - currentlyTyping[u_h].abort(); - } - if (bCastCode === 1) { - const timer = tSleep(5); - timer.then(() => { - this.stoppedTyping(u_h); - }); - currentlyTyping[u_h] = timer; - this.setState({ - currentlyTyping - }); - } else { - this.stoppedTyping(u_h); - } - this.forceUpdate(); - }); - } - componentWillUnmount() { - this.props.chatRoom.off('onParticipantTyping.whosTyping'); - } - stoppedTyping(u_h) { - if (this.domRef.current) { - const { - currentlyTyping - } = this.state; - if (currentlyTyping[u_h]) { - const newState = { - ...currentlyTyping - }; - if (!newState[u_h].aborted) { - newState[u_h].abort(); - } - delete newState[u_h]; - this.setState({ - currentlyTyping: newState - }); - } - } - } - render() { - const users = Object.keys(this.state.currentlyTyping); - if (users.length > 0) { - const names = users.map(u_h => M.getNameByHandle(u_h)).filter(String); - let namesDisplay = ""; - let areMultipleUsersTyping = false; - if (names.length > 1) { - areMultipleUsersTyping = true; - namesDisplay = [names.splice(0, names.length - 1).join(", "), names[0]]; - } else { - areMultipleUsersTyping = false; - namesDisplay = [names[0]]; - } - return REaCt().createElement("div", { - ref: this.domRef, - className: "typing-block" - }, REaCt().createElement("div", { - className: "typing-text" - }, areMultipleUsersTyping ? l[8872].replace("%1", namesDisplay[0]).replace("%2", namesDisplay[1]) : l[8629].replace("%1", namesDisplay[0])), REaCt().createElement("div", { - className: "typing-bounce" - }, REaCt().createElement("div", { - className: "typing-bounce1" - }), REaCt().createElement("div", { - className: "typing-bounce2" - }), REaCt().createElement("div", { - className: "typing-bounce3" - }))); - } - return null; - } -} -// EXTERNAL MODULE: ./js/chat/ui/typingArea.jsx + 1 modules -const typingArea = REQ_(795); -// EXTERNAL MODULE: ./js/ui/buttons.jsx -const buttons = REQ_(994); -// EXTERNAL MODULE: ./js/ui/dropdowns.jsx -const dropdowns = REQ_(911); -;// ./js/chat/ui/composedTextArea.jsx - - - - - -const ComposedTextArea = ({ - chatRoom, - parent, - containerRef, - typingAreaText, - onTypingAreaChanged -}) => REaCt().createElement("div", { - className: "chat-textarea-block" -}, REaCt().createElement(WhosTyping, { - chatRoom -}), REaCt().createElement(typingArea.T, { - chatRoom, - className: "main-typing-area", - containerRef, - disabled: chatRoom.isReadOnly(), - persist: true, - text: typingAreaText, - onValueChanged: onTypingAreaChanged, - onUpEditPressed: () => { - const keys = chatRoom.messagesBuff.messages.keys(); - for (let i = keys.length; i--;) { - const message = chatRoom.messagesBuff.messages[keys[i]]; - const contact = M.u[message.userId]; - if (!contact) { - continue; - } - if (message.isEditable() && !message.requiresManualRetry && !message.deleted && (!message.type || message instanceof Message) && (!message.isManagement || !message.isManagement())) { - parent.historyPanel.editMessage(message.messageId); - return true; - } - } - return false; - }, - onResized: () => { - parent.historyPanel.handleWindowResize(); - }, - onConfirm: messageContents => { - const { - messagesListScrollable - } = parent.historyPanel; - if (messageContents && messageContents.length > 0) { - if (!chatRoom.scrolledToBottom) { - chatRoom.scrolledToBottom = true; - parent.lastScrollPosition = 0; - chatRoom.rebind('onMessagesBuffAppend.pull', () => { - if (messagesListScrollable) { - messagesListScrollable.scrollToBottom(false); - delay('messagesListScrollable', () => { - messagesListScrollable.enable(); - }, 1500); - } - }); - chatRoom.sendMessage(messageContents); - messagesListScrollable == null || messagesListScrollable.disable(); - messagesListScrollable == null || messagesListScrollable.scrollToBottom(true); - } else { - chatRoom.sendMessage(messageContents); - } - } - } -}, REaCt().createElement(buttons.$, { - className: "popup-button left", - icon: "sprite-fm-mono icon-add", - disabled: chatRoom.isReadOnly() -}, REaCt().createElement(dropdowns.Dropdown, { - className: "wide-dropdown attach-to-chat-popup light", - noArrow: "true", - positionMy: "left top", - positionAt: "left bottom", - vertOffset: 4, - wrapper: "#fmholder" -}, REaCt().createElement("div", { - className: "dropdown info-txt" -}, l[23753] || 'Send...'), REaCt().createElement(dropdowns.DropdownItem, { - className: "link-button", - icon: "sprite-fm-mono icon-cloud", - label: l[19794] || 'My Cloud Drive', - disabled: mega.paywall, - onClick: () => chatRoom.trigger('openAttachCloudDialog') -}), REaCt().createElement(dropdowns.DropdownItem, { - className: "link-button", - icon: "sprite-fm-mono icon-session-history", - label: l[19795] || 'My computer', - disabled: mega.paywall, - onClick: () => chatRoom.uploadFromComputer() -}), !is_eplusplus && !is_chatlink && !chatRoom.isNote && REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("hr", null), REaCt().createElement(dropdowns.DropdownItem, { - className: "link-button", - icon: "sprite-fm-mono icon-send-contact", - label: l.share_contact_button, - onClick: () => chatRoom.trigger('openSendContactDialog') -})))))); -const composedTextArea = ComposedTextArea; - -}, - -251 -(_, EXP_, REQ_) { - -"use strict"; -REQ_.r(EXP_); -REQ_.d(EXP_, { -Avatar: () => Avatar, -ContactAwareName: () => ContactAwareName, -ContactButton: () => ContactButton, -ContactCard: () => ContactCard, -ContactFingerprint: () => ContactFingerprint, -ContactItem: () => ContactItem, -ContactPickerDialog: () => ContactPickerDialog, -ContactPickerWidget: () => ContactPickerWidget, -ContactPresence: () => ContactPresence, -ContactVerified: () => ContactVerified, -LastActivity: () => LastActivity, -MAX_FREQUENTS: () => MAX_FREQUENTS, -MembersAmount: () => MembersAmount -}); -const _extends0__ = REQ_(168); -const react1__ = REQ_(594); -const react1 = REQ_.n(react1__); -const _mixins2__ = REQ_(137); -const _ui_utils_jsx3__ = REQ_(314); -const _ui_perfectScrollbar_jsx4__ = REQ_(486); -const _ui_buttons_jsx5__ = REQ_(994); -const _ui_dropdowns_jsx6__ = REQ_(911); -const _contactsPanel_contactsPanel_jsx7__ = REQ_(173); -const _ui_modalDialogs8__ = REQ_(318); -const _link_jsx9__ = REQ_(280); -const _updateObserver_jsx10__ = REQ_(501); - - - - - - - - - - - - -const MAX_FREQUENTS = 3; -const closeDropdowns = () => { - document.dispatchEvent(new Event('closeDropdowns')); -}; -class ContactButton extends _mixins2__.u9 { - constructor(props) { - super(props); - this.dropdownItemGenerator = this.dropdownItemGenerator.bind(this); - } - customIsEventuallyVisible() { - if (this.props.chatRoom) { - return this.props.chatRoom.isCurrentlyActive; - } - return -1; - } - dropdownItemGenerator() { - let { - contact, - dropdowns, - chatRoom, - dropdownRemoveButton - } = this.props; - dropdowns = dropdowns ? dropdowns : []; - const moreDropdowns = []; - moreDropdowns.push(react1().createElement("div", { - className: "dropdown-avatar rounded", - key: "mainContactInfo", - onClick: () => { - if (contact.c === 2) { - loadSubPage('fm/account'); - } - if (contact.c === 1) { - loadSubPage(`fm/chat/contacts/${ contact.u}`); - } - } - }, react1().createElement(Avatar, { - className: "avatar-wrapper context-avatar", - chatRoom, - contact, - hideVerifiedBadge: "true" - }), react1().createElement("div", { - className: "dropdown-user-name" - }, react1().createElement("div", { - className: "name" - }, react1().createElement(ContactAwareName, { - overflow: true, - contact - }), react1().createElement(ContactPresence, { - className: "small", - contact - })), contact && (megaChat.FORCE_EMAIL_LOADING || contact.c === 1 || contact.c === 2) && react1().createElement("span", { - className: "email" - }, contact.m)))); - moreDropdowns.push(react1().createElement(ContactFingerprint, { - key: "fingerprint", - contact - })); - if (dropdowns.length && contact.c !== 2) { - moreDropdowns.push(dropdowns); - moreDropdowns.push(react1().createElement("hr", { - key: "top-separator" - })); - } - if (contact.u === u_handle) { - moreDropdowns.push(react1().createElement(_ui_dropdowns_jsx6__.DropdownItem, { - key: "view0", - icon: "sprite-fm-mono icon-user-filled", - label: l[187], - onClick: () => loadSubPage('fm/account') - })); - } - if (contact.c === 1) { - const startAudioCall = () => { - megaChat.createAndShowPrivateRoom(contact.u).then(room => { - room.setActive(); - room.startAudioCall(); - }); - }; - if (megaChat.currentlyOpenedChat && megaChat.currentlyOpenedChat === contact.u) { - moreDropdowns.push(react1().createElement("div", { - key: "startAudioVideoCall", - "data-simpletipposition": "top", - className: "simpletip", - "data-simpletip": !megaChat.hasSupportForCalls ? l.call_not_suported : '' - }, react1().createElement(_ui_dropdowns_jsx6__.DropdownItem, { - disabled: !megaChat.hasSupportForCalls, - key: "startCall", - className: "sprite-fm-mono-before icon-arrow-right-before", - icon: "sprite-fm-mono icon-phone", - submenu: megaChat.hasSupportForCalls, - label: l[19125] - }), react1().createElement("div", { - className: "dropdown body submenu", - key: "dropdownGroup" - }, react1().createElement("div", null, react1().createElement(_ui_dropdowns_jsx6__.DropdownItem, { - key: "startAudio", - icon: "sprite-fm-mono icon-phone", - disabled: !megaChat.hasSupportForCalls, - label: l[1565], - onClick: startAudioCall - })), react1().createElement("div", null, react1().createElement(_ui_dropdowns_jsx6__.DropdownItem, { - key: "startVideo", - icon: "sprite-fm-mono icon-video-call-filled", - disabled: !megaChat.hasSupportForCalls, - label: l[1566], - onClick: () => { - megaChat.createAndShowPrivateRoom(contact.u).then(room => { - room.setActive(); - room.startVideoCall(); - }); - } - }))))); - } else { - moreDropdowns.push(react1().createElement(_ui_dropdowns_jsx6__.DropdownItem, { - key: "startChat", - icon: "sprite-fm-mono icon-chat", - label: l[5885], - onClick: () => { - loadSubPage(`fm/chat/p/${ contact.u}`); - } - })); - } - moreDropdowns.push(react1().createElement("hr", { - key: "files-separator" - })); - moreDropdowns.push(react1().createElement(_ui_dropdowns_jsx6__.DropdownItem, { - key: "send-files-item", - icon: "sprite-fm-mono icon-send-files", - label: l[6834], - disabled: mega.paywall, - onClick: () => { - megaChat.openChatAndSendFilesDialog(contact.u); - } - })); - moreDropdowns.push(react1().createElement(_ui_dropdowns_jsx6__.DropdownItem, { - key: "share-item", - icon: "sprite-fm-mono icon-folder-outgoing-share", - label: l[6775], - onClick: () => { - openCopyShareDialog(contact.u); - } - })); - } else if (!is_chatlink && !is_eplusplus && (!contact.c || contact.c === 2 && contact.u !== u_handle)) { - moreDropdowns.push(react1().createElement(_ui_dropdowns_jsx6__.DropdownItem, { - key: "view2", - icon: "sprite-fm-mono icon-add", - label: l[101], - onClick: () => { - const isAnonymousUser = !u_handle || u_type !== 3; - const ADD_CONTACT = 'addContact'; - if (is_chatlink && isAnonymousUser) { - megaChat.loginOrRegisterBeforeJoining(undefined, undefined, undefined, true); - if (localStorage.getItem(ADD_CONTACT) === null) { - localStorage.setItem(ADD_CONTACT, JSON.stringify({ - u: contact.u, - unixTime: unixtime() - })); - } - } else { - loadingDialog.show(); - M.syncContactEmail(contact.u, true).then(email => { - if (Object.values(M.opc || {}).some(cr => cr.m === email)) { - closeDialog(); - msgDialog('warningb', '', l[17545]); - } else { - M.inviteContact(M.u[u_handle].m, email); - const title = l[150]; - const msg = l[5898].replace('[X]', email); - closeDialog(); - msgDialog('info', title, msg.replace('[X]', email)); - } - }).catch(() => { - const { - chatRoom - } = this.props; - const { - u: userHandle - } = contact; - if (chatRoom.call) { - return mBroadcaster.sendMessage('meetings:ephemeralAdd', userHandle); - } - const name = M.getNameByHandle(userHandle); - return msgDialog('info', '', l.ephemeral_title ? l.ephemeral_title.replace('%1', name) : `${name} is using an ephemeral session.`, l.ephemeral_info); - }).finally(() => loadingDialog.hide()); - } - } - })); - } - if (u_attr && contact.u !== u_handle) { - if (moreDropdowns.length > 0 && !(moreDropdowns.length === 2 && moreDropdowns[1] && moreDropdowns[1].key === "fingerprint")) { - moreDropdowns.push(react1().createElement("hr", { - key: "nicknames-separator" - })); - } - moreDropdowns.push(react1().createElement(_ui_dropdowns_jsx6__.DropdownItem, { - key: "set-nickname", - icon: "sprite-fm-mono icon-rename", - label: contact.nickname === '' ? l.set_nickname_label : l.edit_nickname_label, - onClick: () => nicknames.setNicknameDialog.init(contact.u) - })); - } - if (dropdownRemoveButton && dropdownRemoveButton.length) { - moreDropdowns.push(react1().createElement("hr", { - key: "remove-separator" - })); - moreDropdowns.push(dropdownRemoveButton); - } - return moreDropdowns; - } - render() { - let { - label = '', - className = '', - contact, - dropdownIconClasses = [], - verticalOffset, - dropdownDisabled, - noLoading, - noContextMenu - } = this.props; - let dropdownPosition = "left top"; - let vertOffset = 0; - let horizOffset = -30; - if (!contact) { - return null; - } - if (label) { - className = `user-card-name ${className}${className.includes('message') ? '' : ' selectable-txt'}`; - dropdownIconClasses = ''; - dropdownPosition = 'left bottom'; - vertOffset = 25; - horizOffset = 0; - } - if (typeof verticalOffset !== 'undefined') { - vertOffset = verticalOffset; - } - if (!contact.name && !contact.m && !noLoading && this.isLoadingContactInfo()) { - label = react1().createElement("em", { - className: "contact-name-loading" - }); - className = `contact-button-loading ${className}`; - } - return noContextMenu ? react1().createElement("div", { - className: "user-card-name light selectable-txt" - }, label) : react1().createElement(_ui_buttons_jsx5__.$, { - className, - icon: dropdownIconClasses, - disabled: dropdownDisabled, - label - }, react1().createElement(_ui_dropdowns_jsx6__.Dropdown, { - className: "context contact-card-dropdown", - positionMy: dropdownPosition, - positionAt: dropdownPosition, - vertOffset, - horizOffset, - dropdownItemGenerator: this.dropdownItemGenerator, - noArrow: true - })); - } -} -ContactButton.defaultProps = { - 'manualDataChangeTracking': true, - 'skipQueuedUpdatesOnResize': true -}; -class ContactVerified extends _mixins2__.w9 { - attachRerenderCallbacks() { - this.addDataStructListenerForProperties(this.props.contact, ['fingerprint']); - } - render() { - if (is_chatlink) { - return null; - } - const {contact} = this.props; - if (!contact) { - return null; - } - if (u_authring && u_authring.Ed25519) { - const verifyState = u_authring.Ed25519[contact.u] || {}; - if (verifyState.method >= authring.AUTHENTICATION_METHOD.FINGERPRINT_COMPARISON) { - return react1().createElement("div", { - className: ` - user-card-verified - ${this.props.className || ''} - ` - }); - } - } else if (!pubEd25519[contact.u]) { - crypt.getPubEd25519(contact.u).then(() => { - if (pubEd25519[contact.u]) { - this.safeForceUpdate(); - } - }); - } - return null; - } -} -ContactVerified.defaultProps = { - 'manualDataChangeTracking': true, - 'skipQueuedUpdatesOnResize': true -}; -class ContactPresence extends _mixins2__.w9 { - constructor(...args) { - super(...args); - this.domRef = react1().createRef(); - } - attachRerenderCallbacks() { - this.addDataStructListenerForProperties(this.props.contact, ['presence']); - } - render() { - const { - contact, - className - } = this.props; - if (!contact || !contact.c) { - return null; - } - return react1().createElement("div", { - ref: this.domRef, - className: ` - user-card-presence - ${megaChat.userPresenceToCssClass(contact.presence)} - ${className || ''} - ` - }); - } -} -ContactPresence.defaultProps = { - manualDataChangeTracking: true, - skipQueuedUpdatesOnResize: true -}; -const LastActivity = (0,_mixins2__.Zz)(_updateObserver_jsx10__.Y)((() => class LastActivity extends _mixins2__.u9 { - attachRerenderCallbacks() { - this._attachRerenderCbContacts(['ats', 'lastGreen', 'presence']); - } - shouldComponentUpdate() { - return true; - } - render() { - const { - contact, - showLastGreen - } = this.props; - if (!contact) { - return null; - } - const lastActivity = !contact.ats || contact.lastGreen > contact.ats ? contact.lastGreen : contact.ats; - const SECONDS = Date.now() / 1000 - lastActivity; - const timeToLast = SECONDS > 3888000 ? l[20673] : time2last(lastActivity, true); - const hasActivityStatus = showLastGreen && contact.presence <= 2 && lastActivity; - return react1().createElement("span", null, hasActivityStatus ? (l[19994] || 'Last seen %s').replace('%s', timeToLast) : M.onlineStatusClass(contact.presence)[0]); - } -})()); -class ContactAwareName extends _mixins2__.u9 { - render() { - const { - contact, - emoji, - overflow - } = this.props; - if (!contact || !M.u[contact.u || contact.h]) { - return null; - } - const name = M.getNameByHandle(contact.u || contact.h); - if (emoji || overflow) { - const EmojiComponent = overflow ? _ui_utils_jsx3__.sp : _ui_utils_jsx3__.zT; - return react1().createElement(EmojiComponent, this.props, name); - } - return react1().createElement("span", null, name); - } -} -class MembersAmount extends _mixins2__.u9 { - render() { - const { - chatRoom - } = this.props; - return react1().createElement("span", null, mega.icu.format(l[20233], Object.keys(chatRoom.members).length)); - } -} -class ContactFingerprint extends _mixins2__.w9 { - constructor(...args) { - super(...args); - this.domRef = react1().createRef(); - } - attachRerenderCallbacks() { - this.addDataStructListenerForProperties(this.props.contact, ['fingerprint']); - } - render() { - const { - contact, - className - } = this.props; - if (!contact || !contact.u || is_chatlink) { - return null; - } - const infoBlocks = []; - userFingerprint(contact.u, (fingerprints) => { - fingerprints.forEach((v, k) => { - infoBlocks.push(react1().createElement("span", { - key: `fingerprint-${ k}` - }, v)); - }); - }); - let verifyButton = null; - if (contact.c === 1 && u_authring && u_authring.Ed25519) { - const verifyState = u_authring.Ed25519[contact.u] || {}; - if (typeof verifyState.method === "undefined" || verifyState.method < authring.AUTHENTICATION_METHOD.FINGERPRINT_COMPARISON) { - verifyButton = react1().createElement(_ui_buttons_jsx5__.$, { - className: "dropdown-verify active", - label: l.verify_credentials, - icon: "sprite-fm-mono icon-key", - onClick: () => { - closeDropdowns(); - fingerprintDialog(contact.u); - } - }); - } - } - return infoBlocks.length ? react1().createElement("div", { - ref: this.domRef, - className: ` - dropdown-fingerprint - ${className || ''} - ` - }, react1().createElement("div", { - className: "contact-fingerprint-title" - }, react1().createElement("span", null, l[6872])), react1().createElement("div", { - className: "contact-fingerprint-txt selectable-txt" - }, infoBlocks), verifyButton) : null; - } -} -ContactFingerprint.defaultProps = { - 'manualDataChangeTracking': true, - 'skipQueuedUpdatesOnResize': true -}; -class Avatar extends _mixins2__.u9 { - render() { - const self = this; - const {contact} = this.props; - if (!contact) { - return null; - } - if (!contact.m && contact.email) { - contact.m = contact.email; - } - const avatarMeta = useravatar.generateContactAvatarMeta(contact); - let classes = `${this.props.className ? this.props.className : ' avatar-wrapper small-rounded-avatar' } ${ contact.u } in-chat`; - classes += " chat-avatar"; - let displayedAvatar; - let verifiedElement = null; - if (!this.props.hideVerifiedBadge && !is_chatlink) { - verifiedElement = react1().createElement(ContactVerified, { - contact: this.props.contact, - className: this.props.verifiedClassName - }); - } - const extraProps = {}; - if (this.props.simpletip) { - classes += " simpletip"; - if (this.props.simpletip === true) { - extraProps['data-simpletip'] = M.getNameByHandle(contact.h || contact.u) || megaChat.plugins.userHelper.SIMPLETIP_USER_LOADER; - } else { - extraProps['data-simpletip'] = this.props.simpletip; - } - if (this.props.simpletipWrapper) { - extraProps['data-simpletipwrapper'] = this.props.simpletipWrapper; - } - if (this.props.simpletipOffset) { - extraProps['data-simpletipoffset'] = this.props.simpletipOffset; - } - if (this.props.simpletipPosition) { - extraProps['data-simpletipposition'] = this.props.simpletipPosition; - } - if (this.props.simpletipClass) { - extraProps['data-simpletip-class'] = this.props.simpletipClass; - } - } - if (avatarMeta.type === "image") { - displayedAvatar = react1().createElement("div", (0,_extends0__.A)({ - className: classes, - style: this.props.style - }, extraProps, { - onClick: self.props.onClick ? e => { - closeDropdowns(); - self.props.onClick(e); - } : self.onClick - }), verifiedElement, react1().createElement("img", { - src: avatarMeta.avatar, - style: this.props.imgStyles - })); - } else { - classes += ` color${ avatarMeta.avatar.colorIndex}`; - const isLoading = self.isLoadingContactInfo(); - if (isLoading) { - classes += " default-bg"; - } - displayedAvatar = react1().createElement("div", (0,_extends0__.A)({ - className: classes, - style: this.props.style - }, extraProps, { - onClick: self.props.onClick ? e => { - closeDropdowns(); - self.props.onClick(e); - } : self.onClick - }), verifiedElement, react1().createElement("span", null, isLoading ? "" : avatarMeta.avatar.letters)); - } - return displayedAvatar; - } -} -Avatar.defaultProps = { - 'manualDataChangeTracking': true, - 'skipQueuedUpdatesOnResize': true -}; -class ContactCard extends _mixins2__.u9 { - attachRerenderCallbacks() { - this._attachRerenderCbContacts(['presence']); - } - specShouldComponentUpdate(nextProps, nextState) { - const foundKeys = Object.keys(this.props); - if (foundKeys.includes('dropdowns')) { - array.remove(foundKeys, 'dropdowns', true); - } - let shouldUpdate; - if (foundKeys.length) { - const k = foundKeys[0]; - shouldUpdate = shallowEqual(nextProps[k], this.props[k]); - } - if (!shouldUpdate) { - shouldUpdate = shallowEqual(nextState, this.state); - } - if (!shouldUpdate && this.state.props.dropdowns && nextProps.state.dropdowns && this.state.props.dropdowns.map && nextProps.state.dropdowns.map) { - const oldKeys = this.state.props.dropdowns.map(child => child.key); - const newKeys = nextProps.state.dropdowns.map(child => child.key); - if (!shallowEqual(oldKeys, newKeys)) { - shouldUpdate = true; - } - } - return shouldUpdate; - } - render() { - let _this$props$chatRoom; - const { - contact - } = this.props; - if (!contact) { - return null; - } - const pres = megaChat.userPresenceToCssClass(contact.presence); - let username = (this.props.namePrefix || '') + (M.getNameByHandle(contact.u) || contact.m); - if (contact.u === u_handle) { - username += ` (${escapeHTML(l[8885])})`; - } - let escapedUsername = react1().createElement(_ui_utils_jsx3__.sp, null, username); - const dropdowns = this.props.dropdowns || []; - const noContextMenu = this.props.noContextMenu || ''; - const noContextButton = this.props.noContextButton || ''; - const dropdownRemoveButton = this.props.dropdownRemoveButton || []; - const highlightSearchValue = this.props.highlightSearchValue || false; - const emailTooltips = this.props.emailTooltips || false; - const searchValue = this.props.searchValue || ""; - let usernameBlock; - if (!noContextMenu) { - usernameBlock = react1().createElement(ContactButton, { - key: "lnk", - dropdowns, - noContextMenu, - contact, - className: "light", - label: escapedUsername, - chatRoom: this.props.chatRoom, - dropdownRemoveButton, - verticalOffset: 0 - }); - } else { - if (highlightSearchValue && searchValue.length > 0) { - const matches = []; - const regex = new RegExp(RegExpEscape(searchValue), 'gi'); - let result; - while (result = regex.exec(username)) { - matches.push({ - idx: result.index, - str: result[0] - }); - } - if (matches.length > 0) { - escapedUsername = react1().createElement(_ui_utils_jsx3__.P9, null, megaChat.highlight(megaChat.html(username), matches, true)); - } - } - usernameBlock = emailTooltips ? react1().createElement("div", { - className: "user-card-name light simpletip selectable-txt", - "data-simpletip": contact.m, - "data-simpletipposition": "top" - }, escapedUsername) : react1().createElement("div", { - className: "user-card-name light selectable-txt" - }, escapedUsername); - } - let userCard = null; - const className = this.props.className || ''; - userCard = className.includes('short') ? react1().createElement("div", { - className: "user-card-data" - }, usernameBlock, react1().createElement("div", { - className: "user-card-status" - }, this.props.isInCall ? react1().createElement("div", { - className: "audio-call" - }, react1().createElement("i", { - className: "sprite-fm-mono icon-phone" - })) : null, react1().createElement(LastActivity, { - contact, - showLastGreen: this.props.showLastGreen - }))) : react1().createElement("div", { - className: "user-card-data" - }, usernameBlock, react1().createElement(ContactPresence, { - contact, - className: this.props.presenceClassName - }), this.props.isInCall ? react1().createElement("div", { - className: "audio-call" - }, react1().createElement("i", { - className: "sprite-fm-mono icon-phone" - })) : null, react1().createElement("div", { - className: "user-card-email selectable-txt" - }, contact.m)); - return react1().createElement("div", { - className: ` - contacts-info body - ${pres === 'offline' ? 'offline' : ''} - ${className || ''} - `, - style: this.props.style, - onClick: ev => { - let _this$props$onClick, _this$props; - return (_this$props$onClick = (_this$props = this.props).onClick) == null ? void 0 : _this$props$onClick.call(_this$props, contact, ev); - }, - onDoubleClick: ev => { - let _this$props$onDoubleC, _this$props2; - return (_this$props$onDoubleC = (_this$props2 = this.props).onDoubleClick) == null ? void 0 : _this$props$onDoubleC.call(_this$props2, contact, ev); - } - }, this.props.withSelfNote ? react1().createElement("div", { - className: ` - note-chat-signifier - ${(_this$props$chatRoom = this.props.chatRoom) != null && _this$props$chatRoom.hasMessages() ? '' : 'note-chat-empty'} - ` - }, react1().createElement("i", { - className: "sprite-fm-mono icon-file-text-thin-outline note-chat-icon" - })) : react1().createElement(Avatar, { - className: "avatar-wrapper small-rounded-avatar", - contact, - chatRoom: this.props.chatRoom - }), is_chatlink || noContextButton ? null : react1().createElement(ContactButton, { - key: "button", - dropdowns, - dropdownIconClasses: this.props.dropdownIconClasses || '', - disabled: this.props.dropdownDisabled, - noContextMenu, - contact, - className: this.props.dropdownButtonClasses, - dropdownRemoveButton, - noLoading: this.props.noLoading, - chatRoom: this.props.chatRoom, - verticalOffset: 0 - }), this.props.selectable ? react1().createElement("div", { - className: "user-card-tick-wrap" - }, react1().createElement("i", { - className: "sprite-fm-mono icon-check" - })) : null, megaChat.WITH_SELF_NOTE && this.props.withSelfNote ? react1().createElement("div", { - className: "user-card-data" - }, react1().createElement("div", { - className: "user-card-name light selectable-txt note-chat-label" - }, l.note_label), react1().createElement("div", { - className: "user-card-status" - })) : userCard); - } -} -ContactCard.defaultProps = { - dropdownButtonClasses: "tiny-button", - dropdownIconClasses: "tiny-icon icons-sprite grey-dots", - presenceClassName: '', - manualDataChangeTracking: true, - skipQueuedUpdatesOnResize: true -}; -class ContactItem extends _mixins2__.u9 { - render() { - const self = this; - const {contact} = this.props; - if (!contact) { - return null; - } - const username = this.props.namePrefix ? this.props.namePrefix : `${ M.getNameByHandle(contact.u)}`; - return react1().createElement("div", { - className: "selected-contact-card short" - }, react1().createElement("div", { - className: "remove-contact-bttn", - onClick: e => { - if (self.props.onClick) { - self.props.onClick(contact, e); - } - } - }, react1().createElement("i", { - className: "tiny-icon small-cross" - })), react1().createElement(Avatar, { - contact, - className: "avatar-wrapper small-rounded-avatar", - hideVerifiedBadge: true, - chatRoom: this.props.chatRoom - }), react1().createElement("div", { - className: "user-card-data simpletip", - "data-simpletip": username || megaChat.plugins.userHelper.SIMPLETIP_USER_LOADER, - "data-simpletipposition": "top" - }, react1().createElement(ContactButton, { - noContextMenu: this.props.noContextMenu, - contact, - className: "light", - label: react1().createElement(_ui_utils_jsx3__.zT, null, username), - chatRoom: this.props.chatRoom - }))); - } -} -ContactItem.defaultProps = { - 'manualDataChangeTracking': true, - 'skipQueuedUpdatesOnResize': true -}; -class ContactPickerWidget extends _mixins2__.w9 { - constructor(...args) { - super(...args); - this.contactLinkListener = null; - this.domRef = react1().createRef(); - this.state = { - searchValue: '', - selected: this.props.selected || [], - publicLink: M.account && M.account.contactLink || undefined - }; - this.normalize = input => ChatSearch._normalize_str(String(input || '').toLowerCase()); - this.onSearchChange = ev => { - this.setState({ - searchValue: ev.target.value - }); - }; - this.renderParticipantsList = () => { - const { - contacts, - emailTooltips, - onSelected - } = this.props; - const { - selected - } = this.state; - const $$list = contacts.map(handle => { - const added = selected.includes(handle); - return react1().createElement(ContactCard, { - key: handle, - className: ` - contacts-search short - ${added ? 'selected' : ''} - `, - contact: M.u[handle], - selectable: true, - emailTooltips, - noContextButton: true, - noContextMenu: true, - onClick: () => { - this.setState({ - selected: added ? selected.filter(h => h !== handle) : [...selected, handle] - }, () => onSelected(this.state.selected)); - } - }); - }); - return react1().createElement(_ui_perfectScrollbar_jsx4__.O, { - className: "contacts-search-scroll", - selected, - contacts - }, react1().createElement("div", { - className: "contacts-search-subsection" - }, react1().createElement("div", { - className: "contacts-list-header" - }, megaChat.activeCall ? l.call_participants : l[16217]), react1().createElement("div", { - className: "contacts-search-list" - }, $$list))); - }; - } - renderInviteWarning() { - const { - chatRoom - } = this.props; - const { - activeCall - } = megaChat; - if (!activeCall || activeCall.chatRoom.chatId !== chatRoom.chatId || !activeCall.sfuClient.callLimits || !activeCall.sfuClient.callLimits.usr || chatRoom.getCallParticipants().length < activeCall.sfuClient.callLimits.usr) { - return null; - } - return react1().createElement("div", { - className: "picker-user-limit-banner" - }, activeCall.organiser === u_handle ? (0,_ui_utils_jsx3__.lI)(l.invite_limit_banner_organiser, '[A]', _link_jsx9__.A, { - onClick() { - window.open(`${getBaseUrl()}/pro`, '_blank', 'noopener,noreferrer'); - eventlog(500263); - } - }) : l.invite_limit_banner_host); - } - componentDidMount() { - super.componentDidMount(); - setContactLink(this.domRef && this.domRef.current); - this.contactLinkListener = mBroadcaster.addListener('contact:setContactLink', publicLink => this.state.publicLink ? null : this.setState({ - publicLink - })); - } - componentDidUpdate() { - const self = this; - if (self.scrollToLastSelected && self.psSelected) { - self.scrollToLastSelected = false; - self.psSelected.scrollToPercentX(100, false); - } - if (self.searchContactsScroll) { - self.searchContactsScroll.reinitialise(); - } - } - UNSAFE_componentWillMount() { - if (super.UNSAFE_componentWillMount) { - super.UNSAFE_componentWillMount(); - } - const self = this; - if (self.props.multiple) { - $(document.body).rebind(`keypress.contactPicker${ self.getUniqueId()}`, (e) => { - const keyCode = e.which || e.keyCode; - if (keyCode === 13) { - if (self.state.selected) { - e.preventDefault(); - e.stopPropagation(); - closeDropdowns(); - if (self.props.onSelectDone) { - self.props.onSelectDone(self.state.selected); - } - } - } - }); - } - } - componentWillUnmount() { - super.componentWillUnmount(); - const self = this; - delete self._foundFrequents; - if (self.props.multiple) { - $(document.body).off(`keypress.contactPicker${ self.getUniqueId()}`); - } - if (this.contactLinkListener) { - mBroadcaster.removeListener(this.contactLinkListener); - } - } - _eventuallyAddContact(v, contacts, selectableContacts, forced) { - const self = this; - const withSelfNote = this.props.withSelfNote && v.u === u_handle; - if (v.u === u_handle && !this.props.step && !withSelfNote) { - return false; - } - if (!forced && v.c !== 1 && v.u !== u_handle) { - return false; - } - if (self.props.exclude && self.props.exclude.indexOf(v.u) > -1) { - return false; - } - let isDisabled = false; - if (!self.wasMissingKeysForContacts) { - self.wasMissingKeysForContacts = {}; - } - if (!self.wasMissingKeysForContacts[v.u] && (!pubCu25519[v.u] || !pubEd25519[v.u])) { - self.wasMissingKeysForContacts[v.u] = true; - ChatdIntegration._ensureKeysAreLoaded(undefined, [v.u]).always(() => { - if (self.isMounted()) { - self.safeForceUpdate(); - } - }); - isDisabled = true; - return true; - } else if (self.wasMissingKeysForContacts[v.u] && (!pubCu25519[v.u] || !pubEd25519[v.u])) { - return false; - } - if (self.state.searchValue && self.state.searchValue.length > 0) { - const searchValue = this.normalize(this.state.searchValue); - const { - name, - nickname, - fullname, - u, - m - } = { - ...v, - name: withSelfNote ? l.note_label : v.name - }; - const matches = [name, nickname, fullname, M.getNameByHandle(u), !this.props.skipMailSearch && m].some(field => this.normalize(field).includes(searchValue)); - if (!matches) { - return false; - } - } - let selectedClass = ""; - if (self.state.selected && self.state.selected.indexOf(v.u) !== -1) { - selectedClass = "selected"; - } - contacts.push(react1().createElement(ContactCard, { - withSelfNote, - disabled: isDisabled, - contact: v, - chatRoom: withSelfNote && megaChat.getNoteChat(), - className: `contacts-search short ${ selectedClass }${isDisabled ? " disabled" : ""}`, - noContextButton: "true", - selectable: selectableContacts, - onClick: self.props.readOnly ? () => {} : contact => { - if (isDisabled) { - return false; - } - const contactHash = contact.u; - if (contactHash === self.lastClicked && new Date() - self.clickTime < 500 && !self.props.disableDoubleClick || !self.props.multiple) { - if (self.props.onSelected) { - self.props.onSelected([contactHash]); - } - self.props.onSelectDone([contactHash]); - closeDropdowns(); - return; - } else { - const selected = clone(self.state.selected || []); - if (selected.indexOf(contactHash) === -1) { - selected.push(contactHash); - self.scrollToLastSelected = true; - if (self.props.onSelected) { - self.props.onSelected(selected); - } - } else { - if (selected.indexOf(contactHash) >= 0) { - array.remove(selected, contactHash); - } - if (self.props.onSelected) { - self.props.onSelected(selected); - } - } - self.setState({ - selected - }); - if (self.props.selectCleanSearchRes) { - self.setState({ - 'searchValue': '' - }); - } - if (self.props.autoFocusSearchField) { - let _self$contactSearchFi; - (_self$contactSearchFi = self.contactSearchField) == null || _self$contactSearchFi.focus(); - } - } - self.clickTime = new Date(); - self.lastClicked = contactHash; - }, - noContextMenu: true, - searchValue: self.state.searchValue, - highlightSearchValue: self.props.highlightSearchValue, - emailTooltips: self.props.emailTooltips, - key: v.u - })); - if (typeof this.props.onEventuallyUpdated === 'function') { - this.props.onEventuallyUpdated(); - } - return true; - } - render() { - const self = this; - let contacts = []; - const frequentContacts = []; - let extraClasses = ""; - const contactsSelected = []; - let multipleContacts = null; - let selectableContacts = false; - let selectFooter = null; - let selectedContacts = false; - const isSearching = !!self.state.searchValue; - megaChat.getNoteChat(); - const onAddContact = e => { - e.preventDefault(); - e.stopPropagation(); - contactAddDialog(); - if (this.props.onClose) { - this.props.onClose(); - } - }; - if (self.props.readOnly) { - const sel = self.state.selected || []; - for (let i = 0; i < sel.length; i++) { - const v = sel[i]; - contactsSelected.push(react1().createElement(ContactItem, { - contact: M.u[v], - key: v, - chatRoom: self.props.chatRoom - })); - } - } else if (self.props.multiple) { - selectableContacts = true; - const onSelectDoneCb = e => { - e.preventDefault(); - e.stopPropagation(); - closeDropdowns(); - if (self.props.onSelectDone) { - self.props.onSelectDone(self.state.selected); - } - }; - let onContactSelectDoneCb = contact => { - const contactHash = contact.u; - if (contactHash === self.lastClicked && new Date() - self.clickTime < 500) { - if (self.props.onSelected) { - self.props.onSelected([contactHash]); - } - self.props.onSelectDone([contactHash]); - return; - } else { - const selected = clone(self.state.selected || []); - if (selected.indexOf(contactHash) === -1) { - selected.push(contactHash); - self.scrollToLastSelected = true; - if (self.props.onSelected) { - self.props.onSelected(selected); - } - } else { - if (selected.indexOf(contactHash) >= 0) { - array.remove(selected, contactHash); - } - if (self.props.onSelected) { - self.props.onSelected(selected); - } - } - self.setState({ - selected - }); - if (self.props.selectCleanSearchRes) { - self.setState({ - 'searchValue': '' - }); - } - if (self.props.autoFocusSearchField) { - let _self$contactSearchFi2; - (_self$contactSearchFi2 = self.contactSearchField) == null || _self$contactSearchFi2.focus(); - } - } - self.clickTime = new Date(); - self.lastClicked = contactHash; - }; - const selectedWidthSize = self.props.selectedWidthSize || 54; - const selectedWidth = self.state.selected.length * selectedWidthSize; - if (!self.state.selected || self.state.selected.length === 0) { - selectedContacts = false; - const emptySelectionMsg = self.props.emptySelectionMsg || l[8889]; - multipleContacts = react1().createElement("div", { - className: "horizontal-contacts-list" - }, react1().createElement("div", { - className: "contacts-list-empty-txt" - }, self.props.nothingSelectedButtonLabel ? self.props.nothingSelectedButtonLabel : emptySelectionMsg)); - } else { - selectedContacts = true; - onContactSelectDoneCb = onContactSelectDoneCb.bind(self); - const sel2 = self.state.selected || []; - for (let i2 = 0; i2 < sel2.length; i2++) { - const v2 = sel2[i2]; - contactsSelected.push(react1().createElement(ContactItem, { - key: v2, - chatRoom: self.props.chatRoom || false, - contact: M.u[v2], - noContextMenu: true, - onClick: onContactSelectDoneCb - })); - } - multipleContacts = react1().createElement("div", { - className: "horizontal-contacts-list" - }, react1().createElement(_ui_perfectScrollbar_jsx4__.O, { - className: "perfectScrollbarContainer selected-contact-block horizontal-only", - selected: this.state.selected, - ref (psSelected) { - self.psSelected = psSelected; - } - }, react1().createElement("div", { - className: "select-contact-centre", - style: { - width: selectedWidth - } - }, contactsSelected))); - } - if (self.props.selectFooter) { - selectFooter = react1().createElement("footer", null, react1().createElement("button", { - className: "mega-button", - onClick: onAddContact.bind(self) - }, react1().createElement("span", null, l[71])), react1().createElement("div", { - className: "footer-spacing" - }), react1().createElement("button", { - className: `mega-button ${selectedContacts ? '' : 'disabled'}`, - onClick (e) { - if (self.state.selected.length > 0) { - onSelectDoneCb(e); - } - } - }, react1().createElement("span", null, this.props.multipleSelectedButtonLabel ? this.props.multipleSelectedButtonLabel : l[8890]))); - } - } - const alreadyAdded = {}; - let hideFrequents = !self.props.readOnly && !self.state.searchValue && frequentContacts.length > 0; - let frequentsLoading = false; - if (this.props.readOnly || this.props.disableFrequents) { - hideFrequents = true; - this._foundFrequents = []; - } else if (!self._foundFrequents) { - frequentsLoading = true; - this._foundFrequents = []; - megaChat.getFrequentContacts().then(res => { - this._foundFrequents = res.slice(Math.max(res.length - 30, 0), res.length).reverse(); - }).catch(dump).finally(() => { - if (this.isMounted()) { - this.safeForceUpdate(); - } - }); - } - for (let i = this._foundFrequents.length, total = 0; total < MAX_FREQUENTS && i--;) { - const v = this._foundFrequents[i]; - if (v.userId in M.u && this._eventuallyAddContact(M.u[v.userId], frequentContacts, selectableContacts)) { - alreadyAdded[v.userId] = 1; - total++; - } - } - self.props.contacts.forEach((v) => { - alreadyAdded[v.h] || self._eventuallyAddContact(v, contacts, selectableContacts); - }); - const sortFn = M.getSortByNameFn2(1); - contacts.sort((a, b) => { - return b.props.withSelfNote - a.props.withSelfNote || sortFn(a.props.contact, b.props.contact); - }); - if (Object.keys(alreadyAdded).length === 0) { - hideFrequents = true; - } - const innerDivStyles = {}; - if (this.props.showMeAsSelected) { - self._eventuallyAddContact(M.u[u_handle], contacts, selectableContacts, true); - } - let noOtherContacts = false; - if (contacts.length === 0 || !_contactsPanel_contactsPanel_jsx7__.A.hasContacts() && this.props.step !== 1) { - noOtherContacts = true; - let noContactsMsg = ""; - if (M.u.length < 2) { - noContactsMsg = l[8877]; - } else { - noContactsMsg = l[8878]; - } - if (hideFrequents) { - contacts = react1().createElement("em", null, noContactsMsg); - } - } - const haveContacts = isSearching || frequentContacts.length !== 0 || !noOtherContacts; - let contactsList; - if (haveContacts) { - if (frequentContacts.length === 0 && noOtherContacts) { - if (self.props.newEmptySearchResult) { - contactsList = react1().createElement("div", { - className: "chat-contactspicker-no-contacts flex flex-column flex-center searching mt-2" - }, react1().createElement("div", { - className: "section-icon sprite-fm-mono icon-contacts" - }), react1().createElement("div", { - className: "fm-empty-cloud-txt small" - }, l[8674])); - } else { - contactsList = react1().createElement("div", { - className: "chat-contactspicker-no-contacts flex flex-column mt-2" - }, react1().createElement("div", { - className: "contacts-list-header" - }, l[165]), react1().createElement("div", { - className: "flex flex-1 flex-column flex-center" - }, react1().createElement("div", { - className: "section-icon sprite-fm-mono icon-contacts" - }), react1().createElement("div", { - className: "fm-empty-cloud-txt small" - }, l[784]), react1().createElement("div", { - className: "fm-empty-description small" - }, l[19115]))); - } - } else { - contactsList = react1().createElement(_ui_perfectScrollbar_jsx4__.O, { - ref: ref => { - self.searchContactsScroll = ref; - }, - className: "contacts-search-scroll", - selected: this.state.selected, - changedHashProp: this.props.changedHashProp, - contacts, - frequentContacts, - searchValue: this.state.searchValue - }, react1().createElement(react1().Fragment, null, react1().createElement("div", { - className: "contacts-search-subsection", - style: { - display: hideFrequents ? 'none' : '' - } - }, react1().createElement("div", { - className: "contacts-list-header" - }, l[20141]), frequentsLoading ? react1().createElement("div", { - className: "loading-spinner" - }, "...") : react1().createElement("div", { - className: "contacts-search-list", - style: innerDivStyles - }, frequentContacts)), contacts.length > 0 ? react1().createElement("div", { - className: "contacts-search-subsection" - }, react1().createElement("div", { - className: "contacts-list-header" - }, frequentContacts && frequentContacts.length === 0 ? this.props.readOnly ? l[16217] : l[165] : l[165]), react1().createElement("div", { - className: "contacts-search-list", - style: innerDivStyles - }, contacts)) : null)); - } - } else if (self.props.newNoContact) { - multipleContacts = ""; - contactsList = react1().createElement("div", { - className: "chat-contactspicker-no-contacts flex flex-column flex-center mt-2" - }, react1().createElement("div", { - className: "section-icon sprite-fm-mono icon-contacts" - }), react1().createElement("div", { - className: "fm-empty-cloud-txt small" - }, l[784]), react1().createElement("div", { - className: "fm-empty-description small" - }, l[19115])); - extraClasses += " no-contacts"; - } else { - contactsList = react1().createElement("div", { - className: "chat-contactspicker-no-contacts flex flex-column flex-center mt-16" - }, react1().createElement("div", { - className: "section-icon sprite-fm-mono icon-contacts" - }), react1().createElement("div", { - className: "fm-empty-cloud-txt small" - }, l[784]), react1().createElement("div", { - className: "fm-empty-description small" - }, l[19115]), react1().createElement("button", { - className: "mega-button positive large fm-empty-button", - onClick: () => { - contactAddDialog(); - self.props.onClose == null || self.props.onClose(); - } - }, react1().createElement("span", null, l[101])), react1().createElement("div", { - className: ` - ${this.state.publicLink ? '' : 'loading'} - empty-share-public - ` - }, react1().createElement("i", { - className: "sprite-fm-mono icon-link-circle" - }), react1().createElement(_ui_utils_jsx3__.P9, null, l[19111]))); - extraClasses += " no-contacts"; - } - const totalContactsNum = contacts.length + frequentContacts.length; - const searchPlaceholderMsg = mega.icu.format(l.search_contact_placeholder, totalContactsNum); - return react1().createElement("div", { - ref: this.domRef, - className: ` - ${this.props.className || ''} - ${extraClasses} - ` - }, this.props.topButtons && react1().createElement("div", { - className: "contacts-search-buttons" - }, this.props.topButtons.map(button => { - const { - key, - icon, - className, - title, - onClick - } = button || {}; - return react1().createElement("div", { - key, - className: "button-wrapper", - onClick: e => { - closeDropdowns(); - onClick(e); - } - }, react1().createElement(_ui_buttons_jsx5__.$, { - className: ` - ${className || ''} - ${key === 'newChatLink' ? 'branded-blue' : ''} - mega-button - `, - icon, - label: title - })); - })), multipleContacts, !this.props.readOnly && haveContacts && !this.props.hideSearch && react1().createElement(react1().Fragment, null, react1().createElement("div", { - className: ` - contacts-search-header - ${this.props.headerClasses} - ` - }, react1().createElement("i", { - className: "sprite-fm-mono icon-preview-reveal" - }), react1().createElement("input", { - autoFocus: true, - type: "search", - placeholder: searchPlaceholderMsg, - ref: nodeRef => { - this.contactSearchField = nodeRef; - }, - onChange: this.onSearchChange, - value: this.state.searchValue - }), react1().createElement("div", { - className: ` - search-result-clear - ${this.state.searchValue && this.state.searchValue.length > 0 ? '' : 'hidden'} - `, - onClick: () => { - this.setState({ - searchValue: '' - }, () => { - let _this$contactSearchFi; - return (_this$contactSearchFi = this.contactSearchField) == null ? void 0 : _this$contactSearchFi.focus(); - }); - } - }, react1().createElement("i", { - className: "sprite-fm-mono icon-close-component" - })))), this.props.inviteWarningLabel && this.props.chatRoom && this.renderInviteWarning(), !this.props.readOnly && haveContacts && !this.props.hideSearch && react1().createElement("div", { - className: "contacts-search-header-separator" - }), this.props.participantsList ? this.renderParticipantsList() : contactsList, selectFooter, this.props.showAddContact && _contactsPanel_contactsPanel_jsx7__.A.hasContacts() ? react1().createElement("div", { - className: "contacts-search-bottom" - }, react1().createElement(_ui_buttons_jsx5__.$, { - className: "mega-button action positive", - icon: "sprite-fm-mono icon-add-circle", - label: l[71], - onClick: () => { - let _this$props$onAddCont, _this$props3; - contactAddDialog(); - closeDropdowns(); - (_this$props$onAddCont = (_this$props3 = this.props).onAddContact) == null || _this$props$onAddCont.call(_this$props3); - } - })) : null); - } -} -ContactPickerWidget.defaultProps = { - multipleSelectedButtonLabel: false, - singleSelectedButtonLabel: false, - nothingSelectedButtonLabel: false, - allowEmpty: false, - disableFrequents: false, - skipMailSearch: false, - autoFocusSearchField: true, - selectCleanSearchRes: true, - disableDoubleClick: false, - newEmptySearchResult: false, - newNoContact: false, - emailTooltips: false -}; -class ContactPickerDialog extends _mixins2__.w9 { - constructor(...args) { - super(...args); - this.dialogName = 'contact-picker-dialog'; - } - componentDidMount() { - super.componentDidMount(); - M.safeShowDialog(this.dialogName, () => $(`.${this.dialogName}`)); - } - componentWillUnmount() { - super.componentWillUnmount(); - if ($.dialog === this.dialogName) { - closeDialog(); - } - } - render() { - const { - active, - allowEmpty, - className, - exclude, - megaChat, - multiple, - multipleSelectedButtonLabel, - name, - nothingSelectedButtonLabel, - selectFooter, - singleSelectedButtonLabel, - inviteWarningLabel, - chatRoom, - onClose, - onSelectDone - } = this.props; - return react1().createElement(_ui_modalDialogs8__.A.ModalDialog, { - name, - className: ` - ${className} - ${this.dialogName} - contacts-search - `, - onClose - }, react1().createElement(ContactPickerWidget, { - active, - allowEmpty, - className: "popup contacts-search small-footer", - contacts: M.u, - exclude, - megaChat, - multiple, - multipleSelectedButtonLabel, - nothingSelectedButtonLabel, - selectFooter, - singleSelectedButtonLabel, - inviteWarningLabel, - chatRoom, - onClose, - onSelectDone - })); - } -} - -}, - -173 -(_, EXP_, REQ_) { - -"use strict"; - -// EXPORTS -REQ_.d(EXP_, { - A: () => ContactsPanel -}); - -// EXTERNAL MODULE: external "React" -const React_ = REQ_(594); -const REaCt = REQ_.n(React_); -// EXTERNAL MODULE: ./js/chat/mixins.js -const mixins = REQ_(137); -// EXTERNAL MODULE: ./js/ui/buttons.jsx -const buttons = REQ_(994); -;// ./js/chat/ui/contactsPanel/navigation.jsx - - - -const Navigation = ({ - view, - receivedRequestsCount -}) => { - const { - VIEW, - LABEL - } = ContactsPanel; - return REaCt().createElement("div", { - className: "contacts-navigation" - }, REaCt().createElement("ul", null, Object.keys(VIEW).map(key => { - let activeClass = view === VIEW[key] ? 'active' : ''; - if (view === VIEW.PROFILE && VIEW[key] === VIEW.CONTACTS) { - activeClass = 'active'; - } - if (VIEW[key] !== VIEW.PROFILE) { - return REaCt().createElement("li", { - key, - onClick: () => { - let page = key.toLowerCase().split("_")[0]; - page = page === 'contacts' ? '' : page; - loadSubPage(`fm/chat/contacts/${page}`); - } - }, REaCt().createElement(buttons.$, { - className: ` - mega-button - action - ${activeClass} - `, - receivedRequestsCount - }, REaCt().createElement("span", null, LABEL[key]), receivedRequestsCount > 0 && VIEW[key] === VIEW.RECEIVED_REQUESTS && REaCt().createElement("div", { - className: "notifications-count" - }, receivedRequestsCount > 9 ? '9+' : receivedRequestsCount))); - } - return null; - }))); -}; -const navigation = Navigation; -// EXTERNAL MODULE: ./js/ui/utils.jsx -const utils = REQ_(314); -;// ./js/chat/ui/contactsPanel/nil.jsx - - - -class Nil extends REaCt().Component { - componentDidMount() { - setContactLink(); - } - render() { - const { - title - } = this.props; - return REaCt().createElement("div", { - className: "fm-empty-section fm-empty-contacts" - }, REaCt().createElement("div", { - className: "fm-empty-pad" - }, REaCt().createElement("i", { - className: "section-icon sprite-fm-mono icon-contacts" - }), REaCt().createElement("div", { - className: "fm-empty-cloud-txt" - }, title), REaCt().createElement("div", { - className: "fm-empty-description" - }, l[19115]), REaCt().createElement(buttons.$, { - className: "mega-button positive large fm-empty-button", - onClick: () => contactAddDialog() - }, REaCt().createElement("span", null, l[71])), REaCt().createElement("div", { - className: "empty-share-public" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-link-circle" - }), REaCt().createElement(utils.P9, null, l[19111])))); - } -} -// EXTERNAL MODULE: ./js/ui/jsx/fm/fmView.jsx + 10 modules -const fmView = REQ_(701); -// EXTERNAL MODULE: ./js/chat/ui/contacts.jsx -const contacts = REQ_(251); -// EXTERNAL MODULE: ./js/ui/jsx/fm/nodes/genericNodePropsComponent.jsx + 1 modules -const genericNodePropsComponent = REQ_(984); -;// ./js/ui/jsx/fm/nodes/columns/columnContactName.jsx - - - - -class ColumnContactName extends genericNodePropsComponent.B { - constructor(...args) { - super(...args); - this.Mail = (0,utils.T9)(() => REaCt().createElement("span", { - className: "contact-item-email" - }, this.props.nodeAdapter.props.node.m)); - } - static get label() { - return l[86]; - } - get name() { - const { - nodeAdapter, - node - } = this.props; - if (nodeAdapter.nodeProps) { - return nodeAdapter.nodeProps.title; - } - return M.getNameByEmail(node.m); - } - _renderAvatar() { - const { - nodeAdapter - } = this.props; - const { - node - } = nodeAdapter.props; - if (nodeAdapter.nodeProps || node.name !== '') { - return REaCt().createElement(contacts.Avatar, { - contact: node, - className: "avatar-wrapper box-avatar" - }); - } else if (node.name === '') { - return REaCt().createElement(utils.P9, null, useravatar.contact(node.m, 'box-avatar')); - } - return null; - } - render() { - return REaCt().createElement("td", null, this._renderAvatar(), REaCt().createElement("div", { - className: "contact-item" - }, REaCt().createElement("div", { - className: "contact-item-user" - }, REaCt().createElement(utils.sp, null, this.name)), REaCt().createElement(this.Mail, null)), REaCt().createElement("div", { - className: "clear" - })); - } -} -ColumnContactName.sortable = true; -ColumnContactName.id = "name"; -ColumnContactName.megatype = "name"; -;// ./js/ui/jsx/fm/nodes/columns/columnContactStatus.jsx - - -class ColumnContactStatus extends genericNodePropsComponent.B { - static get label() { - return l[89]; - } - render() { - const { - nodeAdapter - } = this.props; - const {onlineStatus} = nodeAdapter.nodeProps; - return REaCt().createElement("td", { - megatype: ColumnContactStatus.megatype, - className: ColumnContactStatus.megatype - }, REaCt().createElement("div", { - className: "contact-item" - }, REaCt().createElement("div", { - className: "contact-item-status" - }, REaCt().createElement("div", { - className: `user-card-presence ${ onlineStatus[1]}` - }), onlineStatus[0]))); - } -} -ColumnContactStatus.sortable = true; -ColumnContactStatus.id = "status"; -ColumnContactStatus.megatype = "status"; -;// ./js/ui/jsx/fm/nodes/columns/columnContactLastInteraction.jsx - - -class ColumnContactLastInteraction extends genericNodePropsComponent.B { - constructor(...args) { - super(...args); - this.getLastInteractionIcon = handle => { - const { - interactions - } = this.props; - const interaction = interactions[handle]; - const { - type, - time - } = interaction || { - type: undefined, - time: undefined - }; - return REaCt().createElement("i", { - className: ` - sprite-fm-mono - ${parseInt(type, 10) === 0 ? 'icon-cloud' : ''} - ${parseInt(type, 10) === 1 ? 'icon-chat' : ''} - ${!time ? 'icon-minimise' : ''} - ` - }); - }; - this.getLastInteractionTime = handle => { - const { - interactions - } = this.props; - const interaction = interactions[handle]; - return interaction ? time2last(interaction.time) : l[1051]; - }; - } - static get label() { - return l[5904]; - } - render() { - const { - nodeAdapter - } = this.props; - const { - node - } = nodeAdapter.props; - return REaCt().createElement("td", { - megatype: ColumnContactLastInteraction.megatype, - className: ColumnContactLastInteraction.megatype - }, REaCt().createElement("div", { - className: "contact-item" - }, REaCt().createElement("div", { - className: "contact-item-time" - }, this.getLastInteractionIcon(node.h), this.getLastInteractionTime(node.h)))); - } -} -ColumnContactLastInteraction.sortable = true; -ColumnContactLastInteraction.id = "interaction"; -ColumnContactLastInteraction.megatype = "interaction"; -;// ./js/ui/jsx/fm/nodes/columns/columnContactVerifiedStatus.jsx - - - -class ColumnContactVerifiedStatus extends genericNodePropsComponent.B { - constructor(...args) { - super(...args); - this.getFingerPrintDialogLink = handle => { - const onVerifyContactClicked = handle => { - ContactsPanel.verifyCredentials(this.props.contacts[handle]); - }; - return REaCt().createElement("div", { - className: "verify-contact-link-container" - }, REaCt().createElement("div", { - className: "verify-contact-link", - onClick: () => onVerifyContactClicked(handle) - }, l.verify_credentials)); - }; - } - render() { - const { - nodeAdapter - } = this.props; - const { - node - } = nodeAdapter.props; - return REaCt().createElement("td", { - megatype: ColumnContactVerifiedStatus.megatype, - className: ColumnContactVerifiedStatus.megatype - }, REaCt().createElement("div", { - className: "contact-item" - }, REaCt().createElement("div", { - className: "contact-item-verification" - }, ContactsPanel.isVerified(this.props.contacts[node.h]) ? ColumnContactVerifiedStatus.verifiedLabel : this.getFingerPrintDialogLink(node.h)))); - } -} -ColumnContactVerifiedStatus.sortable = true; -ColumnContactVerifiedStatus.id = "verification"; -ColumnContactVerifiedStatus.megatype = "verification"; -ColumnContactVerifiedStatus.label = REaCt().createElement(REaCt().Fragment, null, l.contact_ver_verification, "\xA0", REaCt().createElement("i", { - className: "simpletip sprite-fm-mono contacts-verification-icon icon-info", - "data-simpletip": l.contact_ver_tooltip_content, - "data-simpletip-class": "contacts-verification-icon-simpletip" -})); -ColumnContactVerifiedStatus.verifiedLabel = REaCt().createElement("div", { - className: "verified-contact-label-container" -}, REaCt().createElement("i", { - className: "small-icon icons-sprite tiny-green-tick" -}), l[6776]); -// EXTERNAL MODULE: ./js/ui/dropdowns.jsx -const dropdowns = REQ_(911); -// EXTERNAL MODULE: ./js/chat/ui/meetings/call.jsx + 11 modules -const call = REQ_(3); -// EXTERNAL MODULE: ./js/chat/ui/conversations.jsx + 21 modules -const conversations = REQ_(732); -;// ./js/chat/ui/contactsPanel/contextMenu.jsx - - - - - - - -class ContextMenu extends REaCt().Component { - constructor(...args) { - super(...args); - this.EVENT_CLOSE = new Event('closeDropdowns'); - this.close = callback => { - if (callback && typeof callback === 'function' && !M.isInvalidUserStatus()) { - callback(); - } - document.dispatchEvent(this.EVENT_CLOSE); - }; - this.handleSetNickname = handle => this.close(() => nicknames.setNicknameDialog.init(handle)); - this.handleAddContact = handle => { - M.syncContactEmail(handle, true).then(email => { - const OPC = Object.values(M.opc); - const ALREADY_SENT = OPC && OPC.length && OPC.some(opc => opc.m === email); - this.close(() => { - if (ALREADY_SENT) { - return msgDialog('warningb', '', l[17545]); - } - msgDialog('info', l[150], l[5898]); - M.inviteContact(M.u[u_handle].m, email); - }); - }).catch(nop); - }; - } - render() { - const { - contact, - selected, - withProfile - } = this.props; - if (ContactsPanel.hasRelationship(contact)) { - return REaCt().createElement(REaCt().Fragment, null, withProfile && REaCt().createElement("div", { - className: "dropdown-avatar rounded", - onClick: e => { - e.stopPropagation(); - loadSubPage(`fm/chat/contacts/${contact.h}`); - } - }, REaCt().createElement(contacts.Avatar, { - contact, - className: "avatar-wrapper context-avatar" - }), REaCt().createElement("div", { - className: "dropdown-profile" - }, REaCt().createElement("span", null, REaCt().createElement(utils.zT, null, M.getNameByHandle(contact.u))), REaCt().createElement(contacts.ContactPresence, { - contact - }))), REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-chat", - label: l[8632], - onClick: () => this.close(() => { - if (selected && selected.length) { - return megaChat.createAndShowGroupRoomFor(selected, '', { - keyRotation: true, - createChatLink: false - }); - } - loadSubPage(`fm/chat/p/${contact.u}`); - megaChat.trigger(conversations.qY.NAV_RENDER_VIEW, conversations.Vw.CHATS); - }) - }), REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-send-files", - label: l[6834], - onClick: () => this.close(() => megaChat.openChatAndSendFilesDialog(contact.u)) - }), REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-folder-outgoing-share", - label: l[5631], - onClick: () => this.close(() => openCopyShareDialog(contact.u)) - }), REaCt().createElement("div", { - "data-simpletipposition": "top", - className: "simpletip", - "data-simpletip": !megaChat.hasSupportForCalls ? l.call_not_suported : '' - }, REaCt().createElement(dropdowns.DropdownItem, { - submenu: megaChat.hasSupportForCalls, - disabled: !navigator.onLine || !megaChat.hasSupportForCalls, - icon: "sprite-fm-mono icon-phone", - className: "sprite-fm-mono-before icon-arrow-right-before", - label: l[19125] - }), REaCt().createElement("div", { - className: "dropdown body submenu" - }, REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-phone", - disabled: !navigator.onLine || !megaChat.hasSupportForCalls, - label: l[5896], - onClick: () => (0,call.dQ)().then(() => this.close(() => megaChat.createAndShowPrivateRoom(contact.u).then(room => { - room.setActive(); - room.startAudioCall(); - }))).catch(() => d && console.warn('Already in a call.')) - }), REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-video-call-filled", - disabled: !navigator.onLine || !megaChat.hasSupportForCalls, - label: l[5897], - onClick: () => (0,call.dQ)().then(() => this.close(() => megaChat.createAndShowPrivateRoom(contact.u).then(room => { - room.setActive(); - room.startVideoCall(); - }))).catch(() => d && console.warn('Already in a call.')) - }))), REaCt().createElement("hr", null), withProfile && REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-my-account", - label: l[5868], - onClick: () => loadSubPage(`fm/chat/contacts/${contact.u}`) - }), REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-rename", - label: contact.nickname === '' ? l.set_nickname_label : l.edit_nickname_label, - onClick: () => this.handleSetNickname(contact.u) - }), REaCt().createElement("hr", null), REaCt().createElement(dropdowns.DropdownItem, { - submenu: true, - icon: "sprite-fm-mono icon-key", - className: "sprite-fm-mono-before icon-arrow-right-before", - label: l[6872] - }), REaCt().createElement("div", { - className: "dropdown body white-context-menu submenu" - }, ContactsPanel.isVerified(contact) ? REaCt().createElement(dropdowns.DropdownItem, { - label: l[742], - onClick: () => this.close(() => ContactsPanel.resetCredentials(contact)) - }) : REaCt().createElement(dropdowns.DropdownItem, { - label: l[1960], - onClick: () => this.close(() => ContactsPanel.verifyCredentials(contact)) - })), REaCt().createElement("div", { - className: "dropdown-credentials" - }, ContactsPanel.getUserFingerprint(contact.u)), REaCt().createElement("hr", null), REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-disable", - label: l[1001], - disabled: !!contact.b, - className: "", - onClick: () => this.close(() => fmremove(contact.u)) - })); - } - return REaCt().createElement(REaCt().Fragment, null, REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-disabled-filled", - label: l[71], - onClick: () => this.handleAddContact(contact.u) - }), REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-rename", - label: contact.nickname === '' ? l.set_nickname_label : l.edit_nickname_label, - onClick: () => this.handleSetNickname(contact.u) - })); - } -} -;// ./js/ui/jsx/fm/nodes/columns/columnContactButtons.jsx - - - - - - - -class ColumnContactButtons extends genericNodePropsComponent.B { - render() { - const { - nodeAdapter - } = this.props; - const { - node, - selected - } = nodeAdapter.props; - const handle = node.h; - return REaCt().createElement("td", { - megatype: ColumnContactButtons.megatype, - className: ColumnContactButtons.megatype - }, REaCt().createElement("div", { - className: "contact-item" - }, REaCt().createElement("div", { - className: "contact-item-controls" - }, REaCt().createElement(buttons.$, { - className: "mega-button action simpletip", - icon: "sprite-fm-mono icon-phone", - attrs: { - 'data-simpletip': !megaChat.hasSupportForCalls ? l.unsupported_browser_audio : l[5896] - }, - disabled: !navigator.onLine || !megaChat.hasSupportForCalls, - onClick: () => M.isInvalidUserStatus() || (0,call.dQ)().then(() => megaChat.createAndShowPrivateRoom(handle).then(room => { - room.setActive(); - room.startAudioCall(); - })).catch(() => d && console.warn('Already in a call.')) - }), REaCt().createElement(buttons.$, { - className: "mega-button action simpletip", - icon: "sprite-fm-mono icon-chat", - attrs: { - 'data-simpletip': l[8632] - }, - onClick: () => { - loadSubPage(`fm/chat/p/${handle}`); - megaChat.trigger(conversations.qY.NAV_RENDER_VIEW, conversations.Vw.CHATS); - } - }), REaCt().createElement(buttons.$, { - className: "mega-button action simpletip", - icon: "sprite-fm-mono icon-send-files", - attrs: { - 'data-simpletip': l[6834] - }, - onClick: () => M.isInvalidUserStatus() || megaChat.openChatAndSendFilesDialog(handle) - }), REaCt().createElement(buttons.$, { - ref: node => { - this.props.onContextMenuRef(handle, node); - }, - className: "mega-button action contact-more", - icon: "sprite-fm-mono icon-options" - }, REaCt().createElement(dropdowns.Dropdown, { - className: "context", - noArrow: true, - positionMy: "left bottom", - positionAt: "right bottom", - positionLeft: this.props.contextMenuPosition || null, - horizOffset: 4, - onActiveChange: opened => { - this.props.onActiveChange(opened); - } - }, REaCt().createElement(ContextMenu, { - contact: node, - selected, - withProfile: true - })))))); - } -} -ColumnContactButtons.sortable = false; -ColumnContactButtons.id = "grid-url-header-nw"; -ColumnContactButtons.label = ""; -ColumnContactButtons.megatype = "grid-url-header-nw"; -// EXTERNAL MODULE: ./js/chat/ui/updateObserver.jsx -const updateObserver = REQ_(501); -;// ./js/chat/ui/contactsPanel/contactList.jsx - - - - - - - - - - -class ContactList extends mixins.w9 { - constructor(props) { - super(props); - this.domRef = REaCt().createRef(); - this.contextMenuRefs = []; - this.state = { - selected: [], - searchValue: null, - interactions: {}, - contextMenuPosition: null - }; - this.onSelected = this.onSelected.bind(this); - this.onHighlighted = this.onHighlighted.bind(this); - this.onExpand = this.onExpand.bind(this); - this.onAttachClicked = this.onAttachClicked.bind(this); - this.getLastInteractions = this.getLastInteractions.bind(this); - } - getLastInteractions() { - const { - contacts - } = this.props; - const promises = []; - const push = handle => { - promises.push(Promise.resolve(getLastInteractionWith(handle, true, true)).then(ts => [ts, handle])); - }; - for (const handle in contacts) { - if (contacts[handle].c === 1) { - push(handle); - } - } - Promise.allSettled(promises).then(res => { - if (this.isMounted()) { - const interactions = {}; - for (let i = res.length; i--;) { - if (res[i].status !== 'fulfilled') { - if (d && res[i].reason !== false) { - console.warn('getLastInteractions', res[i].reason); - } - } else { - const [ts, u] = res[i].value; - const [type, time] = ts.split(':'); - interactions[u] = { - u, - type, - time - }; - } - } - this.setState({ - interactions - }); - } - }).catch(ex => { - console.error("Failed to handle last interactions!", ex); - }); - } - handleContextMenu(ev, handle) { - ev.preventDefault(); - ev.persist(); - if (this.state.selected.length > 1) { - return null; - } - const $$REF = this.contextMenuRefs[handle]; - if ($$REF && $$REF.isMounted()) { - let _$$REF$domRef; - const refNodePosition = ((_$$REF$domRef = $$REF.domRef) == null ? void 0 : _$$REF$domRef.current) && $$REF.domRef.current.getBoundingClientRect().x; - this.setState({ - contextMenuPosition: ev.clientX > refNodePosition ? null : ev.clientX - }, () => $$REF.onClick(ev)); - } - } - onSelected(handle) { - this.setState({ - 'selected': handle - }); - } - onHighlighted(handle) { - this.setState({ - 'highlighted': handle - }); - } - onExpand(handle) { - loadSubPage(`/fm/chat/contacts/${ handle}`); - } - onAttachClicked() { - if (this.state.selected[0]) { - this.onExpand(this.state.selected[0]); - } - } - componentDidMount() { - super.componentDidMount(); - this.getLastInteractions(); - } - render() { - const { - contacts - } = this.props; - if (contacts && contacts.length > 1) { - return REaCt().createElement("div", { - ref: this.domRef, - className: "contacts-list" - }, REaCt().createElement(fmView.A, { - dataSource: contacts, - customFilterFn: r => { - return r.c === 1; - }, - currentlyViewedEntry: "contacts", - onSelected: this.onSelected, - onHighlighted: this.onHighlighted, - searchValue: this.state.searchValue, - onExpand: this.onExpand, - onAttachClicked: this.onAttachClicked, - viewMode: 0, - currentdirid: "contacts", - megaListItemHeight: 59, - headerContainerClassName: "contacts-table contacts-table-head", - containerClassName: "contacts-table contacts-table-results", - onContextMenu: (ev, handle) => this.handleContextMenu(ev, handle), - listAdapterColumns: [ColumnContactName, ColumnContactStatus, [ColumnContactLastInteraction, { - interactions: this.state.interactions - }], [ColumnContactVerifiedStatus, { - contacts - }], [ColumnContactButtons, { - onContextMenuRef: (handle, node) => { - this.contextMenuRefs[handle] = node; - }, - onActiveChange: opened => { - if (!opened) { - this.setState({ - contextMenuPosition: null - }); - } - }, - contextMenuPosition: this.state.contextMenuPosition - }]], - initialSortBy: ['status', 'asc'], - fmConfigSortEnabled: true, - fmConfigSortId: "contacts", - NilComponent: REaCt().createElement(Nil, { - title: l[5737] - }) - })); - } - return REaCt().createElement(Nil, { - title: l[5737] - }); - } -} -ContactList.updateListener = 'getLastInteractions'; -ContactList.updateInterval = 6e4; -const contactList = (0,mixins.Zz)(updateObserver.Y)(ContactList); -;// ./js/ui/jsx/fm/nodes/columns/columnContactRequestsEmail.jsx - - - -class ColumnContactRequestsEmail extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - } - static get label() { - return l[95]; - } - render() { - const { - nodeAdapter, - currView - } = this.props; - const { - node - } = nodeAdapter.props; - return REaCt().createElement("td", { - ref: this.domRef - }, currView && currView === 'opc' ? REaCt().createElement("span", null, REaCt().createElement("i", { - className: "sprite-fm-uni icon-send-requests" - })) : REaCt().createElement(utils.P9, null, useravatar.contact(node.m, 'box-avatar')), REaCt().createElement("div", { - className: "contact-item" - }, REaCt().createElement("div", { - className: "contact-item-user" - }, node.m)), REaCt().createElement("div", { - className: "clear" - })); - } -} -ColumnContactRequestsEmail.sortable = true; -ColumnContactRequestsEmail.id = "email"; -ColumnContactRequestsEmail.megatype = "email"; -;// ./js/ui/jsx/fm/nodes/columns/columnContactRequestsTs.jsx - - -class ColumnContactRequestsTs extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - } - static get label() { - return l[19506]; - } - render() { - const { - nodeAdapter - } = this.props; - const { - node - } = nodeAdapter.props; - let timestamp = node.rts || node.ts; - if (timestamp) { - timestamp = time2last(timestamp); - } else { - timestamp = node.dts ? l[6112] : ""; - } - return REaCt().createElement("td", { - ref: this.domRef - }, REaCt().createElement("div", { - className: "contact-item" - }, REaCt().createElement("div", { - className: "contact-item-time" - }, timestamp)), REaCt().createElement("div", { - className: "clear" - })); - } -} -ColumnContactRequestsTs.sortable = true; -ColumnContactRequestsTs.id = "ts"; -ColumnContactRequestsTs.megatype = "ts"; -;// ./js/ui/jsx/fm/nodes/columns/columnContactRequestsRcvdBtns.jsx - - - -class ColumnContactRequestsRcvdBtns extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - } - render() { - const { - nodeAdapter - } = this.props; - const { - node - } = nodeAdapter.props; - return REaCt().createElement("td", { - ref: this.domRef, - megatype: ColumnContactRequestsRcvdBtns.megatype, - className: ColumnContactRequestsRcvdBtns.megatype - }, REaCt().createElement("div", { - className: "contact-item-controls" - }, REaCt().createElement(buttons.$, { - className: "mega-button action contact-reject", - icon: "sprite-fm-mono icon-close-component", - label: l[20981], - onClick: () => this.props.onReject(node.p) - }), REaCt().createElement(buttons.$, { - className: "mega-button action contact-block", - icon: "sprite-fm-mono icon-disable", - label: l[20980], - onClick: () => this.props.onBlock(node.p) - }), REaCt().createElement(buttons.$, { - className: "mega-button action contact-accept", - icon: "sprite-fm-mono icon-check", - label: l[5856], - onClick: () => this.props.onAccept(node.p) - }))); - } -} -ColumnContactRequestsRcvdBtns.sortable = true; -ColumnContactRequestsRcvdBtns.id = "grid-url-header-nw"; -ColumnContactRequestsRcvdBtns.label = ""; -ColumnContactRequestsRcvdBtns.megatype = "grid-url-header-nw contact-controls-container"; -;// ./js/chat/ui/contactsPanel/receivedRequests.jsx - - - - - - - -const ReceivedRequests = ({ - received -}) => { - const nameOrEmailColumn = received.mixed ? ColumnContactName : [ColumnContactRequestsEmail, { - currView: "ipc" - }]; - return REaCt().createElement("div", { - className: "contacts-list" - }, REaCt().createElement(fmView.A, { - sortFoldersFirst: false, - dataSource: received.data, - customFilterFn: r => { - return !r.dts; - }, - currentlyViewedEntry: "ipc", - onSelected: nop, - onHighlighted: nop, - onExpand: nop, - onAttachClicked: nop, - viewMode: 0, - currentdirid: "ipc", - megaListItemHeight: 59, - headerContainerClassName: "contacts-table requests-table contacts-table-head", - containerClassName: "contacts-table requests-table contacts-table-results", - listAdapterColumns: [nameOrEmailColumn, [ColumnContactRequestsTs, { - label: l[19505] - }], [ColumnContactRequestsRcvdBtns, { - onReject: handle => { - M.denyPendingContactRequest(handle).catch(dump); - }, - onBlock: handle => { - M.ignorePendingContactRequest(handle).catch(dump); - }, - onAccept: handle => { - M.acceptPendingContactRequest(handle).catch(dump); - } - }]], - keyProp: "p", - nodeAdapterProps: { - 'className': node => { - return ` - ${node.dts || node.s && node.s === 3 ? 'deleted' : ''} - ${node.s && node.s === 1 ? 'ignored' : ''} - `; - } - }, - NilComponent: () => { - return REaCt().createElement(Nil, { - title: l[6196] - }); - }, - initialSortBy: ['email', 'asc'], - fmConfigSortEnabled: true, - fmConfigSortId: "ipc" - })); -}; -const receivedRequests = ReceivedRequests; -;// ./js/ui/jsx/fm/nodes/columns/columnContactRequestsSentBtns.jsx - - - -class ColumnContactRequestsSentBtns extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - this.reinviteAllowed = rts => { - const UTC_DATE_NOW = Math.floor(Date.now() / 1000); - return UTC_DATE_NOW > rts + 1209600; - }; - } - render() { - const { - nodeAdapter - } = this.props; - const { - node - } = nodeAdapter.props; - return REaCt().createElement("td", { - ref: this.domRef, - megatype: ColumnContactRequestsSentBtns.megatype, - className: ColumnContactRequestsSentBtns.megatype - }, REaCt().createElement("div", { - className: "contact-item-controls contact-request-sent" - }, !node.dts && this.reinviteAllowed(node.rts) && REaCt().createElement(buttons.$, { - className: "mega-button action", - icon: "sprite-fm-mono icon-rewind", - label: l[5861], - onClick: () => this.props.onReinvite(node.m) - }), !node.dts && REaCt().createElement(buttons.$, { - className: "mega-button action contact-reject", - icon: "sprite-fm-mono icon-close-component", - label: l.msg_dlg_cancel, - onClick: () => this.props.onReject(node.m) - }))); - } -} -ColumnContactRequestsSentBtns.sortable = true; -ColumnContactRequestsSentBtns.id = "grid-url-header-nw"; -ColumnContactRequestsSentBtns.label = ""; -ColumnContactRequestsSentBtns.megatype = "grid-url-header-nw contact-controls-container"; -;// ./js/ui/jsx/fm/nodes/columns/columnContactRequestsRts.jsx - - -class ColumnContactRequestsRts extends ColumnContactRequestsTs { - static get label() { - return l[19506]; - } -} -ColumnContactRequestsRts.sortable = true; -ColumnContactRequestsRts.id = "rts"; -ColumnContactRequestsRts.megatype = "rts"; -;// ./js/chat/ui/contactsPanel/sentRequests.jsx - - - - - - -const SentRequests = ({ - sent -}) => { - return REaCt().createElement("div", { - className: "contacts-list" - }, REaCt().createElement(fmView.A, { - sortFoldersFirst: false, - dataSource: sent, - currentlyViewedEntry: "opc", - onSelected: nop, - onHighlighted: nop, - onExpand: nop, - onAttachClicked: nop, - viewMode: 0, - currentdirid: "opc", - megaListItemHeight: 59, - headerContainerClassName: "contacts-table requests-table contacts-table-head", - containerClassName: "contacts-table requests-table contacts-table-results", - listAdapterColumns: [[ColumnContactRequestsEmail, { - currView: "opc" - }], ColumnContactRequestsRts, [ColumnContactRequestsSentBtns, { - onReject: email => { - M.cancelPendingContactRequest(email).catch(ex => { - if (ex === EARGS) { - msgDialog('info', '', 'This pending contact is already deleted.'); - } else { - tell(ex); - } - }); - }, - onReinvite: email => { - M.reinvitePendingContactRequest(email).then(() => contactsInfoDialog(l[19126], email, l[19127])).catch(tell); - } - }]], - NilComponent: () => { - return REaCt().createElement(Nil, { - title: l[6196] - }); - }, - listAdapterOpts: { - 'className': node => node.dts && ' disabled' - }, - keyProp: "p", - initialSortBy: ['email', 'asc'], - fmConfigSortEnabled: true, - fmConfigSortMap: { - 'rts': 'rTimeStamp' - }, - fmConfigSortId: "opc" - })); -}; -const sentRequests = SentRequests; -// EXTERNAL MODULE: ./js/ui/jsx/fm/nodes/columns/columnFavIcon.jsx -const columnFavIcon = REQ_(161); -;// ./js/ui/jsx/fm/nodes/columns/columnSharedFolderName.jsx - - -class ColumnSharedFolderName extends genericNodePropsComponent.B { - static get label() { - return l[86]; - } - render() { - const { - nodeAdapter - } = this.props; - const { - node - } = nodeAdapter.props; - return REaCt().createElement("td", { - megatype: ColumnSharedFolderName.megatype, - className: ColumnSharedFolderName.megatype - }, REaCt().createElement("div", { - className: "item-type-icon-90 icon-folder-incoming-90 sprite-fm-uni-after icon-warning-after" - }), REaCt().createElement("div", { - className: "shared-folder-info-block" - }, REaCt().createElement("div", { - className: "shared-folder-name" - }, missingkeys[node.h] ? l[8686] : nodeAdapter.nodeProps.title), REaCt().createElement("div", { - className: "shared-folder-info" - }, fm_contains(node.tf, node.td)))); - } -} -ColumnSharedFolderName.sortable = true; -ColumnSharedFolderName.id = "name"; -ColumnSharedFolderName.megatype = "name"; -;// ./js/ui/jsx/fm/nodes/columns/columnSharedFolderAccess.jsx - - -class ColumnSharedFolderAccess extends genericNodePropsComponent.B { - static get label() { - return l[5906]; - } - render() { - const { - nodeAdapter - } = this.props; - return REaCt().createElement("td", { - megatype: ColumnSharedFolderAccess.megatype, - className: ColumnSharedFolderAccess.megatype - }, REaCt().createElement("div", { - className: "shared-folder-access" - }, REaCt().createElement("i", { - className: ` - sprite-fm-mono - ${nodeAdapter.nodeProps.incomingShareData.accessIcon} - ` - }), REaCt().createElement("span", null, nodeAdapter.nodeProps.incomingShareData.accessLabel))); - } -} -ColumnSharedFolderAccess.sortable = true; -ColumnSharedFolderAccess.id = 'access'; -ColumnSharedFolderAccess.megatype = 'access'; -;// ./js/ui/jsx/fm/nodes/columns/columnSharedFolderButtons.jsx - - - -class ColumnSharedFolderButtons extends genericNodePropsComponent.B { - render() { - const { - nodeAdapter - } = this.props; - const { - node - } = nodeAdapter.props; - const handle = node.h; - return REaCt().createElement("td", { - megatype: ColumnSharedFolderButtons.megatype, - className: ColumnSharedFolderButtons.megatype - }, REaCt().createElement("div", { - className: "contact-item" - }, REaCt().createElement("div", { - className: "contact-item-controls" - }, REaCt().createElement(buttons.$, { - className: "mega-button action contact-more", - icon: "sprite-fm-mono icon-options", - onClick: (button, e) => { - e.persist(); - $.selected = [handle]; - e.preventDefault(); - e.stopPropagation(); - e.delegateTarget = $(e.target).parents('td')[0]; - e.currentTarget = $(e.target).parents('tr'); - if (!$(e.target).hasClass('active')) { - M.contextMenuUI(e, 1); - $(this).addClass('active'); - } else { - $.hideContextMenu(); - $(e.target).removeClass('active'); - } - } - })))); - } -} -ColumnSharedFolderButtons.sortable = true; -ColumnSharedFolderButtons.id = "grid-url-header-nw"; -ColumnSharedFolderButtons.label = ""; -ColumnSharedFolderButtons.megatype = "grid-url-header-nw"; -// EXTERNAL MODULE: ./js/chat/ui/link.jsx -const ui_link = REQ_(280); -;// ./js/chat/ui/contactsPanel/contactProfile.jsx - - - - - - - - - - - - - - - - -class ContactProfile extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - this.state = { - selected: [], - loading: true - }; - this.onAttachClicked = () => { - const { - selected - } = this.state; - if (selected[0]) { - this.onExpand(selected[0]); - } - }; - this.onExpand = handle => loadSubPage(`fm/${handle}`); - this.Breadcrumb = () => { - const { - handle - } = this.props; - return REaCt().createElement("div", { - className: "profile-breadcrumb" - }, REaCt().createElement("ul", null, REaCt().createElement("li", null, REaCt().createElement(ui_link.A, { - to: "/fm/chat/contacts" - }, ContactsPanel.LABEL.CONTACTS), REaCt().createElement("i", { - className: "sprite-fm-mono icon-arrow-right" - })), REaCt().createElement("li", null, REaCt().createElement(utils.zT, null, M.getNameByHandle(handle))))); - }; - this.Credentials = () => { - const { - handle - } = this.props; - const contact = M.u[handle]; - if (handle && contact && contact.c === 1) { - const IS_VERIFIED = ContactsPanel.isVerified(contact); - return REaCt().createElement("div", { - className: "profile-credentials" - }, REaCt().createElement("span", { - className: "credentials-head" - }, l[6872]), REaCt().createElement("div", { - className: "credentials-fingerprints" - }, ContactsPanel.getUserFingerprint(handle)), REaCt().createElement("button", { - className: ` - mega-button - small - ${IS_VERIFIED ? '' : 'positive'} - `, - onClick: () => ContactsPanel[IS_VERIFIED ? 'resetCredentials' : 'verifyCredentials'](contact) - }, IS_VERIFIED ? l[742] : l.verify_credentials)); - } - return null; - }; - this.handleContextMenu = (e, handle) => { - e.persist(); - e.preventDefault(); - e.stopPropagation(); - e.delegateTarget = e.target.tagName === "TR" ? e.target : $(e.target).parents('tr')[0]; - e.currentTarget = $(e.delegateTarget); - $.selected = [handle]; - M.contextMenuUI(e, 1); - }; - } - UNSAFE_componentWillMount() { - if (super.UNSAFE_componentWillMount) { - super.UNSAFE_componentWillMount(); - } - const { - handle - } = this.props; - if (handle) { - const contact = M.u[handle]; - if (contact) { - this._listener = contact.addChangeListener(() => { - if (contact && contact.c === 1) { - this.safeForceUpdate(); - } else { - loadSubPage("/fm/chat/contacts"); - return 0xDEAD; - } - }); - } - } - } - componentWillUnmount() { - super.componentWillUnmount(); - if (this._listener) { - const { - handle - } = this.props; - const contact = M.u[handle]; - contact.removeChangeListener(this._listener); - } - } - componentDidMount() { - super.componentDidMount(); - dbfetch.geta(Object.keys(M.c.shares || {}), new MegaPromise()).finally(() => { - if (this.isMounted()) { - this.setState({ - 'loading': false - }); - } - }); - } - getSharedFoldersView() { - return this.state.loading ? null : REaCt().createElement(fmView.A, { - currentlyViewedEntry: this.props.handle, - onSelected: handle => this.setState({ - selected: handle - }), - onHighlighted: nop, - searchValue: this.state.searchValue, - onExpand: this.onExpand, - onAttachClicked: this.onAttachClicked, - viewMode: 0, - currentdirid: "shares", - megaListItemHeight: 65, - headerContainerClassName: "grid-table-header", - containerClassName: "grid-table shared-with-me", - onContextMenu: (ev, handle) => this.handleContextMenu(ev, handle), - listAdapterColumns: [columnFavIcon.$, [ColumnSharedFolderName, { - 'label': `${l.shared_folders_from.replace('%NAME', M.getNameByHandle(this.props.handle))}` - }], ColumnSharedFolderAccess, ColumnSharedFolderButtons] - }); - } - render() { - const { - handle - } = this.props; - if (handle) { - const contact = M.u[handle]; - if (!contact || contact.c !== 1) { - return REaCt().createElement(Nil, { - title: l.contact_not_found - }); - } - const HAS_RELATIONSHIP = ContactsPanel.hasRelationship(contact); - return REaCt().createElement("div", { - ref: this.domRef, - className: "contacts-profile" - }, REaCt().createElement(this.Breadcrumb, null), REaCt().createElement("div", { - className: "profile-content" - }, REaCt().createElement("div", { - className: "profile-head" - }, HAS_RELATIONSHIP && REaCt().createElement(this.Credentials, null), REaCt().createElement(contacts.Avatar, { - contact, - className: "profile-photo avatar-wrapper contacts-medium-avatar" - }), REaCt().createElement("div", { - className: "profile-info" - }, REaCt().createElement("h2", null, REaCt().createElement(utils.zT, null, M.getNameByHandle(handle)), REaCt().createElement(contacts.ContactPresence, { - contact - })), REaCt().createElement("span", null, contact.m)), HAS_RELATIONSHIP && REaCt().createElement("div", { - className: "profile-controls" - }, REaCt().createElement(buttons.$, { - className: "mega-button round simpletip", - icon: "sprite-fm-mono icon-chat-filled", - attrs: { - 'data-simpletip': l[8632] - }, - onClick: () => { - loadSubPage(`fm/chat/p/${handle}`); - megaChat.trigger(conversations.qY.NAV_RENDER_VIEW, conversations.Vw.CHATS); - } - }), REaCt().createElement(buttons.$, { - className: "mega-button round simpletip", - icon: "sprite-fm-mono icon-send-files", - attrs: { - 'data-simpletip': l[6834] - }, - onClick: () => { - if (M.isInvalidUserStatus()) { - return; - } - megaChat.openChatAndSendFilesDialog(handle); - } - }), REaCt().createElement(buttons.$, { - className: "mega-button round", - icon: "sprite-fm-mono icon-options" - }, REaCt().createElement(dropdowns.Dropdown, { - className: "context", - noArrow: true, - positionMy: "left bottom", - positionAt: "right bottom", - horizOffset: 4 - }, REaCt().createElement(ContextMenu, { - contact - }))))), REaCt().createElement("div", { - className: "profile-shared-folders" - }, this.getSharedFoldersView()))); - } - return null; - } -} -;// ./js/chat/ui/contactsPanel/contactsPanel.jsx - - - - - - - -class ContactsPanel extends mixins.w9 { - get view() { - switch (megaChat.routingSubSection) { - case null: - return ContactsPanel.VIEW.CONTACTS; - case "contact": - return ContactsPanel.VIEW.PROFILE; - case "received": - return ContactsPanel.VIEW.RECEIVED_REQUESTS; - case "sent": - return ContactsPanel.VIEW.SENT_REQUESTS; - default: - console.error("Shouldn't happen."); - return false; - } - } - constructor(props) { - super(props); - this.domRef = REaCt().createRef(); - this.requestReceivedListener = null; - this.state = { - receivedRequestsCount: 0 - }; - this.handleToggle = ({ - keyCode - }) => { - if (keyCode === 27) { - const HAS_DIALOG_OPENED = $.dialog || ['.contact-nickname-dialog', '.fingerprint-dialog', '.context'].some(selector => { - const dialog = document.querySelector(selector); - return dialog && dialog.offsetHeight > 0; - }); - return HAS_DIALOG_OPENED ? keyCode : loadSubPage('fm/chat'); - } - }; - this.handleAcceptAllRequests = () => { - const { - data: received - } = this.props.received; - const receivedKeys = Object.keys(received || {}); - if (receivedKeys.length) { - for (let i = receivedKeys.length; i--;) { - M.acceptPendingContactRequest(receivedKeys[i]).catch(dump); - } - } - }; - this.renderView = () => { - const { - contacts, - received, - sent - } = this.props; - const { - view - } = this; - switch (view) { - case ContactsPanel.VIEW.CONTACTS: - return REaCt().createElement(contactList, { - contacts - }); - case ContactsPanel.VIEW.PROFILE: - return REaCt().createElement(ContactProfile, { - handle: view === ContactsPanel.VIEW.PROFILE && megaChat.routingParams - }); - case ContactsPanel.VIEW.RECEIVED_REQUESTS: - return REaCt().createElement(receivedRequests, { - received - }); - case ContactsPanel.VIEW.SENT_REQUESTS: - return REaCt().createElement(sentRequests, { - sent - }); - } - }; - this.state.receivedRequestsCount = Object.keys(M.ipc).length; - } - componentWillUnmount() { - super.componentWillUnmount(); - document.documentElement.removeEventListener(ContactsPanel.EVENTS.KEYDOWN, this.handleToggle); - if (this.requestReceivedListener) { - mBroadcaster.removeListener(this.requestReceivedListener); - } - } - componentDidMount() { - super.componentDidMount(); - document.documentElement.addEventListener(ContactsPanel.EVENTS.KEYDOWN, this.handleToggle); - this.requestReceivedListener = mBroadcaster.addListener('fmViewUpdate:ipc', () => this.setState({ - receivedRequestsCount: Object.keys(M.ipc).length - })); - } - render() { - const { - view, - state - } = this; - const { - receivedRequestsCount - } = state; - return REaCt().createElement("div", { - ref: this.domRef, - className: "contacts-panel" - }, REaCt().createElement(navigation, { - view, - contacts: this.props.contacts, - receivedRequestsCount - }), view !== ContactsPanel.VIEW.PROFILE && REaCt().createElement("div", { - className: "contacts-actions" - }, view === ContactsPanel.VIEW.RECEIVED_REQUESTS && receivedRequestsCount > 1 && REaCt().createElement("button", { - className: "mega-button action", - onClick: this.handleAcceptAllRequests - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-check" - }), REaCt().createElement("span", null, l[19062]))), REaCt().createElement("div", { - className: "contacts-content" - }, this.renderView())); - } -} -ContactsPanel.EVENTS = { - KEYDOWN: 'keydown' -}; -ContactsPanel.VIEW = { - CONTACTS: 0x00, - RECEIVED_REQUESTS: 0x01, - SENT_REQUESTS: 0x02, - PROFILE: 0x03 -}; -ContactsPanel.LABEL = { - CONTACTS: l[165], - RECEIVED_REQUESTS: l[5863], - SENT_REQUESTS: l[5862] -}; -ContactsPanel.hasContacts = () => M.u.some(contact => contact.c === 1); -ContactsPanel.hasRelationship = contact => contact && contact.c === 1; -ContactsPanel.isVerified = contact => { - if (contact && contact.u) { - const { - u: handle - } = contact; - const verificationState = u_authring.Ed25519[handle] || {}; - return verificationState.method >= authring.AUTHENTICATION_METHOD.FINGERPRINT_COMPARISON; - } - return null; -}; -ContactsPanel.verifyCredentials = contact => { - if (contact.c === 1 && u_authring && u_authring.Ed25519) { - const verifyState = u_authring.Ed25519[contact.u] || {}; - if (typeof verifyState.method === "undefined" || verifyState.method < authring.AUTHENTICATION_METHOD.FINGERPRINT_COMPARISON) { - fingerprintDialog(contact.u); - } - } -}; -ContactsPanel.resetCredentials = contact => { - if (M.isInvalidUserStatus()) { - return; - } - authring.resetFingerprintsForUser(contact.u).then(() => contact.trackDataChange()).catch(dump); -}; -ContactsPanel.getUserFingerprint = handle => { - const $$FINGERPRINT = []; - userFingerprint(handle, fingerprints => { - for (let i = 0; i < fingerprints.length; i++) { - $$FINGERPRINT.push(REaCt().createElement("span", { - key: i - }, fingerprints[i])); - } - }); - return $$FINGERPRINT; -}; - -}, - -438 -(_, EXP_, REQ_) { - -"use strict"; - -// EXPORTS -REQ_.d(EXP_, { - $h: () => ConversationPanels, - Yk: () => EmptyConvPanel, - e4: () => allContactsInChat, - zV: () => excludedParticipants -}); - -// UNUSED EXPORTS: ConversationPanel, ConversationRightArea, JoinCallNotification - -// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/applyDecoratedDescriptor.js -const applyDecoratedDescriptor = REQ_(793); -// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/extends.js -const esm_extends = REQ_(168); -// EXTERNAL MODULE: external "React" -const React_ = REQ_(594); -const REaCt = REQ_.n(React_); -// EXTERNAL MODULE: ./js/ui/utils.jsx -const utils = REQ_(314); -// EXTERNAL MODULE: ./js/chat/mixins.js -const mixins = REQ_(137); -// EXTERNAL MODULE: ./js/ui/buttons.jsx -const buttons = REQ_(994); -// EXTERNAL MODULE: ./js/ui/modalDialogs.jsx + 1 modules -const modalDialogs = REQ_(318); -;// ./js/ui/jsx/fm/viewModeSelector.jsx - -const VIEW_MODE = { - 'GRID': 1, - 'LIST': undefined -}; -const ViewModeSelector = ({ - viewMode, - onChange -}) => { - return REaCt().createElement("div", { - className: "chat-fm-view-mode-selector" - }, REaCt().createElement("i", { - className: ` - sprite-fm-mono - icon-view-medium-list - ${viewMode ? '' : 'active'} - `, - title: l[5553], - onClick: () => onChange == null ? void 0 : onChange(VIEW_MODE.LIST) - }), REaCt().createElement("i", { - className: ` - sprite-fm-mono - icon-view-grid - ${viewMode ? " active" : ""} - `, - title: l[5552], - onClick: () => onChange == null ? void 0 : onChange(VIEW_MODE.GRID) - })); -}; -const viewModeSelector = ViewModeSelector; -;// ./js/ui/jsx/fm/breadcrumbs.jsx - - -class Breadcrumbs extends mixins.w9 { - constructor(props) { - super(props); - this.domRef = REaCt().createRef(); - this.state = { - 'breadcrumbDropdownVisible': false - }; - this.onGlobalClickHandler = this.onGlobalClickHandler.bind(this); - this.onBreadcrumbNodeClick = this.onBreadcrumbNodeClick.bind(this); - } - getBreadcrumbNodeText(nodeId, prevNodeId) { - const backupsId = M.BackupsId || 'backups'; - switch (nodeId) { - case M.RootID: - return l[164]; - case M.RubbishID: - return l[167]; - case backupsId: - return l.restricted_folder_button; - case 'shares': - return prevNodeId && M.d[prevNodeId] ? M.d[prevNodeId].m : l[5589]; - default: - return M.d[nodeId] && M.d[nodeId].name; - } - } - getBreadcrumbDropdownContents(items) { - const contents = []; - for (const item of items) { - if (!item.name) { - continue; - } - contents.push(REaCt().createElement("a", { - className: "crumb-drop-link", - key: `drop_link_${ item.nodeId}`, - onClick: e => this.onBreadcrumbNodeClick(e, item.nodeId) - }, REaCt().createElement("i", { - className: `sprite-fm-mono icon24 ${{ - 'cloud-drive': 'icon-cloud', - 'backups': 'icon-database-filled', - 's4-object-storage': 'icon-bucket-triangle-thin-solid', - 's4-buckets': 'icon-bucket-outline' - }[item.type] || 'folder'}` - }), REaCt().createElement("span", null, item.name))); - } - return contents; - } - onBreadcrumbNodeClick(e, nodeId) { - e.preventDefault(); - e.stopPropagation(); - if (this._clickToHideListener) { - this.removeGlobalClickHandler(); - this.setState({ - 'breadcrumbDropdownVisible': false - }); - } - this.props.onNodeClick(nodeId); - } - customIsEventuallyVisible() { - return true; - } - onGlobalClickHandler(e) { - let _this$domRef; - const node = (_this$domRef = this.domRef) == null ? void 0 : _this$domRef.current; - if (node.contains(e.target) || node === e.target) { - return; - } - if (this._clickToHideListener) { - this.removeGlobalClickHandler(); - } - this.setState({ - 'breadcrumbDropdownVisible': false - }); - } - removeGlobalClickHandler() { - this._clickToHideListener = false; - document.body.removeEventListener("click", this.onGlobalClickHandler); - } - componentDidUpdate() { - super.componentDidUpdate(); - if (this.state.breadcrumbDropdownVisible) { - if (!this._clickToHideListener) { - this._clickToHideListener = true; - document.body.addEventListener("click", this.onGlobalClickHandler); - } - } else if (this._clickToHideListener) { - this.removeGlobalClickHandler(); - } - } - componentWillUnmount() { - super.componentWillUnmount(); - this.removeGlobalClickHandler(); - } - render() { - const { - className, - highlighted, - currentlyViewedEntry, - isSearch, - path - } = this.props; - const breadcrumb = []; - const extraPathItems = []; - let breadcrumbDropdownContents = []; - const entryId = isSearch ? highlighted[0] : currentlyViewedEntry; - if (entryId !== undefined) { - (path || M.getPath(entryId)).forEach((nodeId, k, path) => { - let breadcrumbClasses = ''; - let folderType = 'folder'; - if (nodeId === M.RootID) { - breadcrumbClasses += " cloud-drive"; - } else { - breadcrumbClasses += " folder"; - } - if (nodeId.length === 11 && M.u[nodeId]) { - return; - } - if (nodeId === "shares") { - breadcrumbClasses += " shared-with-me"; - } - const prevNodeId = path[k - 1]; - let nodeName = this.getBreadcrumbNodeText(nodeId, prevNodeId); - if ('utils' in s4) { - const data = s4.utils.getBreadcrumbsData(nodeId); - if (data) { - ({ - type: folderType, - localeName: nodeName - } = data); - } - } - if (!nodeName) { - return; - } - ((nodeId, k) => { - if (k < 4) { - breadcrumb.unshift(REaCt().createElement("a", { - className: `fm-breadcrumbs contains-directories ${ breadcrumbClasses}`, - key: nodeId, - onClick: e => this.onBreadcrumbNodeClick(e, nodeId) - }, REaCt().createElement("span", { - className: `right-arrow-bg simpletip`, - "data-simpletip": nodeName - }, REaCt().createElement("span", { - className: "selectable-txt" - }, nodeName)), k !== 0 && REaCt().createElement("i", { - className: "next-arrow sprite-fm-mono icon-arrow-right icon16" - }))); - } else { - folderType = nodeId === M.RootID ? 'cloud-drive' : folderType; - if (M.BackupsId && nodeId === M.BackupsId) { - folderType = 'backups'; - } - extraPathItems.push({ - name: nodeName, - type: folderType, - nodeId - }); - } - })(nodeId, k); - }); - if (extraPathItems.length > 0) { - breadcrumbDropdownContents = this.getBreadcrumbDropdownContents(extraPathItems); - } - } - return REaCt().createElement("div", { - ref: this.domRef, - className: ` - fm-breadcrumbs-wrapper - ${className || ''} - ` - }, REaCt().createElement("div", { - className: "fm-breadcrumbs-block" - }, breadcrumbDropdownContents.length ? REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("div", { - className: "crumb-overflow-link" - }, REaCt().createElement("a", { - className: "breadcrumb-dropdown-link dropdown", - onClick: () => { - this.setState({ - breadcrumbDropdownVisible: !this.state.breadcrumbDropdownVisible - }); - } - }, REaCt().createElement("i", { - className: "menu-icon sprite-fm-mono icon-options icon16" - })), REaCt().createElement("i", { - className: "sprite-fm-mono icon-arrow-right icon16" - })), breadcrumb) : breadcrumb), breadcrumbDropdownContents.length ? REaCt().createElement("div", { - className: this.state.breadcrumbDropdownVisible ? 'breadcrumb-dropdown active' : 'breadcrumb-dropdown' - }, breadcrumbDropdownContents) : ''); - } -} -// EXTERNAL MODULE: ./js/ui/jsx/fm/fmView.jsx + 10 modules -const fmView = REQ_(701); -;// ./js/ui/cloudBrowserModalDialog.jsx - - - - - -const MIN_SEARCH_LENGTH = 2; -class CloudBrowserDialog extends modalDialogs.A.SafeShowDialogController { - static getFilterFunction(customFilterFn) { - return tryCatch(n => { - if (n.s4 && n.p === M.RootID && M.getS4NodeType(n) === 'container') { - return false; - } - if (!n.name || missingkeys[n.h] || M.getNodeShare(n).down) { - return false; - } - return !customFilterFn || customFilterFn(n); - }); - } - constructor(props) { - super(props); - this.domRef = REaCt().createRef(); - this.dialogName = 'attach-cloud-dialog'; - this.state = { - 'isActiveSearch': false, - 'selected': [], - 'highlighted': [], - 'currentlyViewedEntry': M.RootID, - 'selectedTab': M.RootID, - 'searchValue': '', - 'searchText': '' - }; - this.onAttachClicked = this.onAttachClicked.bind(this); - this.onClearSearchIconClick = this.onClearSearchIconClick.bind(this); - this.onPopupDidMount = this.onPopupDidMount.bind(this); - this.onSearchChange = this.onSearchChange.bind(this); - this.onSearchIconClick = this.onSearchIconClick.bind(this); - this.onSelected = this.onSelected.bind(this); - this.onHighlighted = this.onHighlighted.bind(this); - this.handleTabChange = this.handleTabChange.bind(this); - this.onViewModeSwitch = this.onViewModeSwitch.bind(this); - this.onBreadcrumbNodeClick = this.onBreadcrumbNodeClick.bind(this); - this.onExpand = this.onExpand.bind(this); - } - onViewModeSwitch(newMode) { - const currentViewMode = mega.config.get('cbvm') | 0; - if (newMode === currentViewMode) { - return; - } - mega.config.set('cbvm', newMode); - this.forceUpdate(); - } - getHeaderButtonsClass() { - const classes = ['fm-header-buttons']; - if (this.state.isActiveSearch) { - classes.push('active-search'); - } - return classes.join(' '); - } - getSearchIconClass() { - const classes = ['sprite-fm-mono', 'icon-preview-reveal']; - if (this.state.isActiveSearch && this.state.searchText.length > 0) { - classes.push('disabled'); - } - return classes.join(' '); - } - onSearchIconClick() { - const isActiveSearch = !this.state.isActiveSearch; - if (isActiveSearch) { - this.searchInput.focus(); - this.setState({ - isActiveSearch - }); - } - } - onClearSearchIconClick() { - this.setState({ - 'isActiveSearch': false, - 'searchValue': '', - 'searchText': '', - 'currentlyViewedEntry': this.state.selectedTab - }); - } - handleTabChange(selectedTab) { - const s4Cn = selectedTab === 's4' && M.tree.s4 && Object.keys(M.tree.s4); - this.clearSelectionAndHighlight(); - this.setState({ - selectedTab, - currentlyViewedEntry: s4Cn && s4Cn.length === 1 ? s4Cn[0] : selectedTab, - searchValue: '', - searchText: '', - isLoading: false - }); - } - onSearchBlur() { - if (this.state.searchText === '') { - this.setState({ - 'isActiveSearch': false - }); - } - } - onSearchChange(e) { - const searchValue = e.target.value; - const newState = { - searchText: searchValue, - nodeLoading: searchValue.length >= MIN_SEARCH_LENGTH - }; - if (searchValue && searchValue.length >= MIN_SEARCH_LENGTH) { - this.setState(newState); - delay('cbd:search-proc', this.searchProc.bind(this), 500); - return; - } - if (this.state.currentlyViewedEntry === 'search' && (!searchValue || searchValue.length < MIN_SEARCH_LENGTH)) { - newState.currentlyViewedEntry = this.state.selectedTab; - newState.searchValue = undefined; - } - this.setState(newState); - this.clearSelectionAndHighlight(); - } - searchProc() { - const { - searchText - } = this.state; - const newState = { - nodeLoading: true - }; - if (searchText && searchText.length >= MIN_SEARCH_LENGTH) { - this.setState(newState); - M.fmSearchNodes(searchText).then(() => { - newState.nodeLoading = false; - newState.searchValue = searchText; - newState.currentlyViewedEntry = 'search'; - this.setState(newState); - this.clearSelectionAndHighlight(); - }); - } - } - onSelected(nodes) { - this.setState({ - 'selected': nodes - }); - this.props.onSelected(nodes); - } - onHighlighted(nodes) { - this.setState({ - 'highlighted': nodes - }); - if (this.props.onHighlighted) { - this.props.onHighlighted(nodes); - } - } - clearSelectionAndHighlight() { - this.onSelected([]); - this.onHighlighted([]); - if (selectionManager) { - selectionManager.clear_selection(); - } - } - onPopupDidMount(elem) { - this.domNode = elem; - } - onAttachClicked() { - this.props.onAttachClicked(); - } - onBreadcrumbNodeClick(nodeId) { - if (nodeId === 'shares' || nodeId === 's4') { - return this.handleTabChange(nodeId); - } - if (M.getNodeByHandle(nodeId).t) { - const nodeRoot = M.getNodeRoot(nodeId); - this.setState({ - selectedTab: nodeRoot === "contacts" ? 'shares' : nodeRoot, - currentlyViewedEntry: nodeId, - selected: [], - searchValue: '', - searchText: '' - }); - } - } - onExpand(nodeId) { - this.setState({ - 'currentlyViewedEntry': nodeId, - 'searchValue': '', - 'searchText': '', - 'selected': [], - 'highlighted': [] - }); - } - render() { - assert(this.dialogBecameVisible); - const self = this; - const viewMode = mega.config.get('cbvm') | 0; - const classes = `add-from-cloud ${self.props.className} dialog-template-tool `; - let folderIsHighlighted = false; - let share = false; - let isS4Cn = false; - const isSearch = this.state.currentlyViewedEntry === 'search'; - const entryId = isSearch ? self.state.highlighted[0] : self.state.currentlyViewedEntry; - const filterFn = CloudBrowserDialog.getFilterFunction(this.props.customFilterFn); - const isIncomingShare = M.getNodeRoot(entryId) === "shares"; - this.state.highlighted.forEach(nodeId => { - if (M.getNodeByHandle(nodeId).t) { - folderIsHighlighted = true; - if (M.tree.s4 && M.tree.s4[nodeId]) { - isS4Cn = true; - } - } - share = M.getNodeShare(nodeId); - }); - const buttons = [{ - "label": this.props.cancelLabel, - "key": "cancel", - "onClick": e => { - e.preventDefault(); - e.stopPropagation(); - if (this.props.onCancel) { - this.props.onCancel(this); - } - this.props.onClose(this); - } - }]; - if (folderIsHighlighted) { - const { - highlighted - } = this.state; - const className = `${share && share.down ? 'disabled' : ''}`; - const highlightedNode = highlighted && highlighted.length && highlighted[0]; - const allowAttachFolders = this.props.allowAttachFolders && !isIncomingShare && !isS4Cn; - buttons.push({ - "label": this.props.openLabel, - "key": "select", - className: `positive ${className} ${highlighted.length > 1 ? 'disabled' : ''}`, - onClick: e => { - e.preventDefault(); - e.stopPropagation(); - if (highlighted.length > 1) { - return; - } - this.setState({ - currentlyViewedEntry: highlightedNode - }); - this.clearSelectionAndHighlight(); - this.setState({ - selected: [], - searchValue: '', - searchText: '', - highlighted: [] - }); - } - }, allowAttachFolders ? { - "label": l[8023], - "key": "attach", - className: `positive ${ className}`, - onClick: () => { - this.props.onClose(); - onIdle(() => { - const createPublicLink = h => { - M.createPublicLink(h).then(({ - link - }) => this.props.room.sendMessage(link)); - }; - const frs = []; - const files = []; - for (let i = 0; i < highlighted.length; i++) { - const node = M.getNodeByHandle(highlighted[i]); - if (node && M.isFileNode(node)) { - if (!M.getNodeShare(node).down) { - files.push(node); - } - } else if (mega.fileRequestCommon.storage.isDropExist(highlighted[i]).length) { - frs.push(highlighted[i]); - } else { - createPublicLink(highlighted[i]); - } - } - if (files.length) { - this.props.onSelected(files); - this.props.onAttachClicked(); - } - if (frs.length) { - const fldName = frs.length > 1 ? l[17626] : l[17403].replace('%1', escapeHTML(M.getNameByHandle(frs[0])) || l[1049]); - msgDialog('confirmation', l[1003], fldName, l[18229], e => { - if (e) { - mega.fileRequest.removeList(frs).then(() => { - for (let i = 0; i < frs.length; i++) { - createPublicLink(frs[i]); - } - }).catch(dump); - } - }); - } - }); - } - } : null); - } - if (!folderIsHighlighted || this.props.folderSelectable && (!this.props.noShareFolderAttach || !(isIncomingShare && folderIsHighlighted))) { - buttons.push({ - "label": this.props.selectLabel, - "key": "select", - "className": `positive ${ this.state.selected.length === 0 || share && share.down || isS4Cn ? "disabled" : ""}`, - "onClick": e => { - if (this.state.selected.length > 0) { - this.props.onSelected(this.state.selected); - this.props.onAttachClicked(); - } - e.preventDefault(); - e.stopPropagation(); - } - }); - } - let clearSearchBtn = null; - if (self.state.searchText.length >= MIN_SEARCH_LENGTH) { - clearSearchBtn = REaCt().createElement("i", { - className: "sprite-fm-mono icon-close-component", - onClick: () => { - self.onClearSearchIconClick(); - } - }); - } - const breadcrumbPath = M.getPath(entryId); - return REaCt().createElement(modalDialogs.A.ModalDialog, { - title: self.props.title || l[8011], - className: classes + (isSearch && this.state.selected.length > 0 ? 'has-breadcrumbs-bottom' : '') + this.dialogName, - onClose: () => { - self.props.onClose(self); - }, - dialogName: "add-from-cloud-dialog dialog-template-tool", - popupDidMount: self.onPopupDidMount, - buttons - }, REaCt().createElement("section", { - ref: this.domRef, - className: "content" - }, REaCt().createElement("div", { - className: "content-block" - }, REaCt().createElement("div", { - className: "fm-dialog-tabs" - }, REaCt().createElement("div", { - className: ` - fm-dialog-tab cloud - ${self.state.selectedTab === M.RootID ? 'active' : ''} - `, - onClick: () => self.handleTabChange(M.RootID) - }, l[164]), REaCt().createElement("div", { - className: ` - fm-dialog-tab incoming - ${self.state.selectedTab === 'shares' ? 'active' : ''} - `, - onClick: () => self.handleTabChange('shares') - }, l[5542]), REaCt().createElement("div", { - className: ` - fm-dialog-tab s4 - ${self.state.selectedTab === 's4' ? 'active' : ''} - ${u_attr.s4 ? '' : 'hidden'} - `, - onClick: () => self.handleTabChange('s4') - }, l.obj_storage), REaCt().createElement("div", { - className: "clear" - })), REaCt().createElement("div", { - className: "fm-picker-header" - }, REaCt().createElement("div", { - className: self.getHeaderButtonsClass() - }, REaCt().createElement(viewModeSelector, { - viewMode, - onChange: this.onViewModeSwitch - }), REaCt().createElement("div", { - className: "fm-files-search" - }, REaCt().createElement("i", { - className: self.getSearchIconClass(), - onClick: () => { - self.onSearchIconClick(); - } - }), REaCt().createElement("input", { - ref: input => { - this.searchInput = input; - }, - type: "search", - placeholder: l[102], - value: self.state.searchText, - onChange: self.onSearchChange, - onBlur: () => { - self.onSearchBlur(); - } - }), clearSearchBtn), REaCt().createElement("div", { - className: "clear" - })), !isSearch && REaCt().createElement(Breadcrumbs, { - className: "add-from-cloud", - nodeId: entryId, - path: breadcrumbPath, - onNodeClick: this.onBreadcrumbNodeClick, - isSearch, - highlighted: this.state.highlighted, - currentlyViewedEntry: this.state.currentlyViewedEntry - })), REaCt().createElement(fmView.A, { - nodeLoading: this.state.nodeLoading, - sortFoldersFirst: true, - currentlyViewedEntry: this.state.currentlyViewedEntry, - customFilterFn: filterFn, - folderSelectNotAllowed: this.props.folderSelectNotAllowed, - folderSelectable: this.props.folderSelectable, - onSelected: this.onSelected, - onHighlighted: this.onHighlighted, - onAttachClicked: this.onAttachClicked, - initialSelected: this.state.selected, - initialHighlighted: this.state.highlighted, - searchValue: this.state.searchValue, - minSearchLength: MIN_SEARCH_LENGTH, - onExpand: this.onExpand, - viewMode, - initialSortBy: ['name', 'asc'], - fmConfigSortEnabled: true, - fmConfigSortId: "cbd" - }), isSearch && breadcrumbPath.length > 0 && REaCt().createElement("div", { - className: ` - fm-breadcrumbs-wrapper add-from-cloud breadcrumbs-bottom - ` - }, REaCt().createElement("div", { - className: "fm-breadcrumbs-block" - }, REaCt().createElement(Breadcrumbs, { - nodeId: entryId, - path: breadcrumbPath, - onNodeClick: this.onBreadcrumbNodeClick, - isSearch, - highlighted: this.state.highlighted, - currentlyViewedEntry: this.state.currentlyViewedEntry - }), REaCt().createElement("div", { - className: "clear" - })))))); - } -} -CloudBrowserDialog.defaultProps = { - 'selectLabel': l[8023], - 'openLabel': l[1710], - 'cancelLabel': l.msg_dlg_cancel, - 'hideable': true, - 'className': '' -}; -Object.defineProperty(mega, 'CloudBrowserDialog', { - value: CloudBrowserDialog -}); -const cloudBrowserModalDialog = { - CloudBrowserDialog -}; -// EXTERNAL MODULE: ./js/chat/chatRoom.jsx + 1 modules -const chat_chatRoom = REQ_(553); -;// ./js/ui/historyRetentionDialog.jsx - - - - -const LIMIT = { - CHARS: 2, - HOURS: 24, - DAYS: 31, - WEEKS: 4, - MONTHS: 12 -}; -class HistoryRetentionDialog extends React_.Component { - constructor(props) { - super(props); - this.dialogName = 'msg-retention-dialog'; - this.inputRef = REaCt().createRef(); - this.state = { - selectedTimeFormat: chat_chatRoom.RETENTION_FORMAT.HOURS, - timeRange: undefined - }; - this.handleRadioChange = e => { - const selectedTimeFormat = e.target.value; - this.setState(prevState => ({ - selectedTimeFormat, - timeRange: this.filterTimeRange(prevState.timeRange, selectedTimeFormat) - })); - }; - this.handleOnTimeCheck = e => { - const checkingValue = e.type === 'paste' ? e.clipboardData.getData('text') : e.key; - if (e.keyCode !== 8 && isNaN(checkingValue)) { - e.preventDefault(); - } - }; - this.handleOnTimeChange = e => { - const timeValue = e.target.value; - this.setState(prevState => ({ - timeRange: this.filterTimeRange(timeValue, prevState.selectedTimeFormat) - })); - }; - const { - chatRoom - } = props; - this.state.timeRange = chatRoom.getRetentionTimeFormatted(); - if (this.state.timeRange === 0) { - this.state.timeRange = ''; - } - this.state.selectedTimeFormat = chatRoom.getRetentionFormat(); - this.state.selectedTimeFormat = this.state.selectedTimeFormat === chat_chatRoom.RETENTION_FORMAT.DISABLED ? chat_chatRoom.RETENTION_FORMAT.HOURS : this.state.selectedTimeFormat; - } - hasInput() { - return this.state.timeRange && parseInt(this.state.timeRange, 10) >= 1; - } - getMaxTimeRange(selectedTimeFormat) { - switch (selectedTimeFormat) { - case chat_chatRoom.RETENTION_FORMAT.HOURS: - return LIMIT.HOURS; - case chat_chatRoom.RETENTION_FORMAT.DAYS: - return LIMIT.DAYS; - case chat_chatRoom.RETENTION_FORMAT.WEEKS: - return LIMIT.WEEKS; - case chat_chatRoom.RETENTION_FORMAT.MONTHS: - return LIMIT.MONTHS; - } - } - getParsedLabel(label, timeRange) { - timeRange = timeRange ? parseInt(timeRange, 10) : this.getMaxTimeRange(label); - switch (label) { - case chat_chatRoom.RETENTION_FORMAT.HOURS: - return mega.icu.format(l.plural_hour, timeRange); - case chat_chatRoom.RETENTION_FORMAT.DAYS: - return mega.icu.format(l.plural_day, timeRange); - case chat_chatRoom.RETENTION_FORMAT.WEEKS: - return mega.icu.format(l.plural_week, timeRange); - case chat_chatRoom.RETENTION_FORMAT.MONTHS: - return mega.icu.format(l.plural_month, timeRange); - } - } - filterTimeRange(timeRange, selectedTimeFormat) { - if (timeRange.length > LIMIT.CHARS) { - return timeRange.substring(0, LIMIT.CHARS); - } - timeRange = parseInt(timeRange, 10); - if (timeRange === 0 || isNaN(timeRange)) { - return ''; - } - return Math.min(this.getMaxTimeRange(selectedTimeFormat), timeRange); - } - handleOnSubmit(e) { - if (!this.hasInput()) { - return; - } - e.preventDefault(); - e.stopPropagation(); - const { - chatRoom, - onClose - } = this.props; - const { - selectedTimeFormat, - timeRange - } = this.state; - let time = 0; - switch (selectedTimeFormat) { - case chat_chatRoom.RETENTION_FORMAT.HOURS: - time = hoursToSeconds(Number(timeRange)); - break; - case chat_chatRoom.RETENTION_FORMAT.DAYS: - time = daysToSeconds(Number(timeRange)); - break; - case chat_chatRoom.RETENTION_FORMAT.WEEKS: - time = daysToSeconds(Number(timeRange) * 7); - break; - case chat_chatRoom.RETENTION_FORMAT.MONTHS: - time = daysToSeconds(Number(timeRange) * 30); - break; - } - chatRoom.setRetention(time); - onClose(); - } - renderCustomRadioButton() { - return [chat_chatRoom.RETENTION_FORMAT.HOURS, chat_chatRoom.RETENTION_FORMAT.DAYS, chat_chatRoom.RETENTION_FORMAT.WEEKS, chat_chatRoom.RETENTION_FORMAT.MONTHS].map(label => { - return REaCt().createElement(CustomRadioButton, { - checked: this.state.selectedTimeFormat === label, - label: this.getParsedLabel(label, this.state.timeRange), - name: "time-selector", - value: label, - onChange: this.handleRadioChange, - key: label - }); - }); - } - componentDidMount() { - M.safeShowDialog(this.dialogName, () => { - $(document.body).rebind('keydown.historyRetentionDialog', e => { - const key = e.keyCode || e.which; - if (key === 13) { - this.handleOnSubmit(e); - } - }); - }); - } - componentWillUnmount() { - $(document.body).off('keydown.historyRetentionDialog'); - if ($.dialog === this.dialogName) { - closeDialog(); - } - } - render() { - const { - chatRoom, - onClose - } = this.props; - const { - selectedTimeFormat, - timeRange - } = this.state; - return REaCt().createElement(modalDialogs.A.ModalDialog, (0,esm_extends.A)({}, this.state, { - chatRoom, - onClose, - dialogName: this.dialogName, - dialogType: "tool", - onClick: () => this.inputRef.current.focus() - }), REaCt().createElement("header", null, REaCt().createElement("h2", { - id: "msg-retention-dialog-title" - }, l[23434])), REaCt().createElement("section", { - className: "content" - }, REaCt().createElement("div", { - className: "content-block" - }, REaCt().createElement("p", null, l[23435])), REaCt().createElement("div", { - className: "content-block form" - }, REaCt().createElement("div", { - className: "form-section" - }, REaCt().createElement("span", { - className: "form-section-placeholder" - }, this.getParsedLabel(selectedTimeFormat, timeRange)), REaCt().createElement("input", { - type: "number", - min: "0", - step: "1", - max: this.getMaxTimeRange(selectedTimeFormat), - className: "form-section-time", - placeholder: this.getMaxTimeRange(selectedTimeFormat), - ref: this.inputRef, - autoFocus: true, - value: timeRange, - onChange: this.handleOnTimeChange, - onKeyPress: this.handleOnTimeCheck, - onPaste: this.handleOnTimeCheck - })), REaCt().createElement("div", { - className: "form-section" - }, REaCt().createElement("div", { - className: "form-section-radio" - }, this.renderCustomRadioButton())))), REaCt().createElement("footer", null, REaCt().createElement("div", { - className: "footer-container" - }, REaCt().createElement("button", { - className: "mega-button", - onClick: onClose - }, REaCt().createElement("span", null, l.msg_dlg_cancel)), REaCt().createElement("button", { - className: ` - mega-button positive - ${this.hasInput() ? '' : 'disabled'} - `, - onClick: e => this.handleOnSubmit(e) - }, REaCt().createElement("span", null, l[726]))))); - } -} -function CustomRadioButton({ - checked = false, - label, - name, - value, - onChange -}) { - return REaCt().createElement("label", { - key: value, - className: "radio-txt" - }, label, REaCt().createElement("div", { - className: `custom-radio small green-active ${ checked ? "radioOn" : "radioOff"}` - }, REaCt().createElement("input", { - type: "radio", - name, - value, - checked, - onChange - }))); -} -// EXTERNAL MODULE: ./js/ui/dropdowns.jsx -const dropdowns = REQ_(911); -// EXTERNAL MODULE: ./js/chat/ui/contacts.jsx -const ui_contacts = REQ_(251); -// EXTERNAL MODULE: ./js/ui/perfectScrollbar.jsx -const perfectScrollbar = REQ_(486); -;// ./js/ui/accordion.jsx -const React = REQ_(594); - -class AccordionPanel extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = React.createRef(); - } - render() { - const { - accordionClass, - expanded, - title, - className, - children, - onToggle - } = this.props; - return React.createElement("div", { - ref: this.domRef, - className: ` - chat-dropdown - container - ${accordionClass || ''} - ` - }, React.createElement("div", { - className: ` - chat-dropdown - header - ${expanded ? 'expanded' : ''} - `, - onClick: onToggle - }, React.createElement("span", null, title), React.createElement("i", { - className: "sprite-fm-mono icon-arrow-down" - })), expanded ? React.createElement("div", { - className: ` - chat-dropdown - content - have-animation - ${className | ''} - ` - }, children) : null); - } -} -class Accordion extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = React.createRef(); - this.state = { - expandedPanel: this.props.expandedPanel - }; - } - onToggle(e, key) { - const obj = {}; - obj[key] = !(this.state.expandedPanel || {})[key]; - this.setState({ - 'expandedPanel': obj - }); - this.props.onToggle && this.props.onToggle(key); - } - render() { - const self = this; - const classes = `accordion-panels ${ self.props.className ? self.props.className : ''}`; - const accordionPanels = []; - let x = 0; - React.Children.forEach(self.props.children, child => { - if (!child) { - return; - } - if (child.type.name === 'AccordionPanel' || child.type.name && child.type.name.indexOf('AccordionPanel') > -1) { - accordionPanels.push(React.cloneElement(child, { - key: child.key, - expanded: !!self.state.expandedPanel[child.key], - accordion: self, - onToggle (e) { - self.onToggle(e, child.key); - } - })); - } else { - accordionPanels.push(React.cloneElement(child, { - key: x++, - accordion: self - })); - } - }); - return React.createElement("div", { - ref: this.domRef, - className: classes - }, accordionPanels); - } -} - -;// ./js/chat/ui/participantsList.jsx - - -const DropdownsUI = REQ_(911); -const ContactsUI = REQ_(251); -const PerfectScrollbar = REQ_(486).O; -class ParticipantsList extends mixins.w9 { - constructor(props) { - super(props); - this.domRef = REaCt().createRef(); - this.state = { - 'scrollPositionY': 0, - 'scrollHeight': 144 - }; - this.doResizesOnComponentUpdate = SoonFc(10, function () { - let _self$domRef; - const self = this; - if (!self.isMounted()) { - return; - } - let fitHeight = self.contactsListScroll.getContentHeight(); - if (!fitHeight) { - return null; - } - const $node = $((_self$domRef = self.domRef) == null ? void 0 : _self$domRef.current); - const $parentContainer = $node.closest('.chat-right-pad'); - const maxHeight = $parentContainer.outerHeight(true) - $('.chat-right-head', $parentContainer).outerHeight(true) - 72; - if (fitHeight < $('.buttons-block', $parentContainer).outerHeight(true)) { - fitHeight = Math.max(fitHeight, 53); - } else if (maxHeight < fitHeight) { - fitHeight = Math.max(maxHeight, 53); - } - fitHeight = Math.min(self.calculateListHeight($parentContainer), fitHeight); - const $contactsList = $('.chat-contacts-list', $parentContainer); - if ($contactsList.height() !== fitHeight) { - $('.chat-contacts-list', $parentContainer).height(fitHeight); - if (self.contactsListScroll) { - self.contactsListScroll.reinitialise(); - } - } - if (self.state.scrollHeight !== fitHeight) { - self.setState({ - 'scrollHeight': fitHeight - }); - } - self.onUserScroll(); - }); - } - onUserScroll() { - if (!this.contactsListScroll) { - return; - } - const scrollPosY = this.contactsListScroll.getScrollPositionY(); - if (this.state.scrollPositionY !== scrollPosY) { - this.setState({ - 'scrollPositionY': scrollPosY - }); - } - } - calculateListHeight($parentContainer) { - const room = this.props.chatRoom; - return ($parentContainer ? $parentContainer : $('.conversationsApp')).outerHeight() - 144 - 10 - (room.type === "public" && room.observers > 0 ? 48 : 0) - (room.isReadOnly() ? 12 : 0); - } - componentDidUpdate() { - const self = this; - if (!self.isMounted()) { - return; - } - if (!self.contactsListScroll) { - return null; - } - self.doResizesOnComponentUpdate(); - } - render() { - const { - chatRoom - } = this.props; - if (!chatRoom) { - return null; - } - return REaCt().createElement("div", { - ref: this.domRef, - className: "chat-contacts-list" - }, REaCt().createElement(PerfectScrollbar, { - chatRoom, - members: chatRoom.members, - ref: ref => { - this.contactsListScroll = ref; - }, - disableCheckingVisibility: true, - onUserScroll: SoonFc(this.onUserScroll.bind(this), 76), - requiresUpdateOnResize: true, - isVisible: chatRoom.isCurrentlyActive, - options: { - suppressScrollX: true - } - }, REaCt().createElement(ParticipantsListInner, { - chatRoom, - members: chatRoom.members, - scrollPositionY: this.state.scrollPositionY, - scrollHeight: this.state.scrollHeight, - disableCheckingVisibility: true - }))); - } -} -ParticipantsList.defaultProps = { - 'requiresUpdateOnResize': true, - 'contactCardHeight': 36 -}; -class ParticipantsListInner extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - } - render() { - const room = this.props.chatRoom; - const {contactCardHeight} = this.props; - const {scrollPositionY} = this.props; - const {scrollHeight} = this.props; - const { - OPERATOR, - FULL, - READONLY - } = ChatRoom.MembersSet.PRIVILEGE_STATE; - if (!room) { - return null; - } - if (!room.isCurrentlyActive) { - return false; - } - const contacts = room.getParticipantsExceptMe(); - const contactsList = []; - const firstVisibleUserNum = Math.floor(scrollPositionY / contactCardHeight); - const visibleUsers = Math.ceil(scrollHeight / contactCardHeight); - const contactListInnerStyles = { - 'height': contacts.length * contactCardHeight - }; - if ((room.type === "group" || room.type === "public") && !room.stateIsLeftOrLeaving() && room.members.hasOwnProperty(u_handle)) { - contacts.unshift(u_handle); - contactListInnerStyles.height += contactCardHeight; - } - const onRemoveClicked = contactHash => { - room.trigger('onRemoveUserRequest', [contactHash]); - }; - const onSetPrivClicked = (contactHash, priv) => { - if (room.members[contactHash] !== priv) { - room.trigger('alterUserPrivilege', [contactHash, priv]); - } - }; - for (let i = 0; i < contacts.length; i++) { - const contactHash = contacts[i]; - if (!(contactHash in M.u)) { - continue; - } - const contact = M.u[contactHash]; - if (i < firstVisibleUserNum || i > firstVisibleUserNum + visibleUsers) { - continue; - } - const dropdowns = []; - let dropdownIconClasses = "small-icon tiny-icon icons-sprite grey-dots"; - const dropdownRemoveButton = []; - if (room.type === "public" || room.type === "group" && room.members) { - if (room.iAmOperator() && contactHash !== u_handle) { - dropdownRemoveButton.push(REaCt().createElement(DropdownsUI.DropdownItem, { - className: "red", - key: "remove", - icon: "sprite-fm-mono icon-disabled-filled", - label: l[8867], - onClick: onRemoveClicked.bind(this, contactHash) - })); - } - if (room.iAmOperator()) { - dropdowns.push(REaCt().createElement("div", { - key: "setPermLabel", - className: "dropdown-items-info" - }, l[8868])); - dropdowns.push(REaCt().createElement(DropdownsUI.DropdownItem, { - key: "privOperator", - icon: "sprite-fm-mono icon-admin-outline", - label: l[8875], - className: ` - tick-item - ${room.members[contactHash] === OPERATOR ? 'active' : ''} - `, - disabled: contactHash === u_handle, - onClick: () => onSetPrivClicked(contactHash, OPERATOR) - })); - dropdowns.push(REaCt().createElement(DropdownsUI.DropdownItem, { - key: "privFullAcc", - icon: "sprite-fm-mono icon-chat", - className: ` - tick-item - ${room.members[contactHash] === FULL ? 'active' : ''} - `, - disabled: contactHash === u_handle, - label: l[8874], - onClick: () => onSetPrivClicked(contactHash, FULL) - })); - dropdowns.push(REaCt().createElement(DropdownsUI.DropdownItem, { - key: "privReadOnly", - icon: "sprite-fm-mono icon-read-only", - className: ` - tick-item - ${room.members[contactHash] === READONLY ? 'active' : ''} - `, - disabled: contactHash === u_handle, - label: l[8873], - onClick: () => onSetPrivClicked(contactHash, READONLY) - })); - } - const baseClassName = 'sprite-fm-mono'; - switch (room.members[contactHash]) { - case OPERATOR: - dropdownIconClasses = `${baseClassName} icon-admin`; - break; - case FULL: - dropdownIconClasses = `${baseClassName} icon-chat-filled`; - break; - case READONLY: - dropdownIconClasses = `${baseClassName} icon-read-only`; - break; - default: - break; - } - contactsList.push(REaCt().createElement(ContactsUI.ContactCard, { - key: contact.u, - contact, - chatRoom: room, - className: "right-chat-contact-card", - dropdownPositionMy: "left top", - dropdownPositionAt: "left top", - dropdowns, - dropdownDisabled: contactHash === u_handle || is_chatlink || is_eplusplus, - dropdownButtonClasses: "contacts-icon", - dropdownRemoveButton, - dropdownIconClasses, - noLoading: true, - isInCall: room.uniqueCallParts && room.uniqueCallParts[contactHash], - style: { - width: 234, - position: 'absolute', - top: i * contactCardHeight - } - })); - } - } - return REaCt().createElement("div", { - ref: this.domRef, - className: "chat-contacts-list-inner default-bg", - style: contactListInnerStyles - }, contactsList); - } -} -ParticipantsListInner.defaultProps = { - requiresUpdateOnResize: true, - contactCardHeight: 32, - scrollPositionY: 0, - scrollHeight: 128, - chatRoom: undefined -}; - -// EXTERNAL MODULE: ./js/chat/ui/messages/generic.jsx + 14 modules -const generic = REQ_(890); -;// ./js/chat/ui/sharedFilesAccordionPanel.jsx - -let _dec, _class; -const sharedFilesAccordionPanel_React = REQ_(594); - - -class SharedFileItem extends mixins.u9 { - render() { - const self = this; - const {message} = this.props; - const contact = Message.getContactForMessage(message); - const name = M.getNameByHandle(contact.u); - const timestamp = time2date(message.delay); - const {node} = this.props; - const {icon} = this.props; - return sharedFilesAccordionPanel_React.createElement("div", { - className: `chat-shared-block ${ self.props.isLoading ? "is-loading" : ""}`, - key: `${message.messageId }_${ node.h}`, - onClick: () => this.props.isPreviewable ? M.viewMediaFile(node) : M.addDownload([node]), - onDoubleClick: () => M.addDownload([node]) - }, sharedFilesAccordionPanel_React.createElement("div", { - className: `icon-or-thumb ${thumbnails.has(node.fa) ? "thumb" : ""}` - }, sharedFilesAccordionPanel_React.createElement("div", { - className: `item-type-icon-90 icon-${icon}-90` - }), sharedFilesAccordionPanel_React.createElement("div", { - className: "img-wrapper", - id: this.props.imgId - }, sharedFilesAccordionPanel_React.createElement("img", { - alt: "", - src: thumbnails.get(node.fa) || "" - }))), sharedFilesAccordionPanel_React.createElement("div", { - className: "chat-shared-info" - }, sharedFilesAccordionPanel_React.createElement("span", { - className: "txt" - }, node.name), sharedFilesAccordionPanel_React.createElement("span", { - className: "txt small" - }, sharedFilesAccordionPanel_React.createElement(utils.zT, null, name)), sharedFilesAccordionPanel_React.createElement("span", { - className: "txt small grey" - }, timestamp))); - } -} -const SharedFilesAccordionPanel = (_dec = utils.Ay.SoonFcWrap(350), _class = class SharedFilesAccordionPanel extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = sharedFilesAccordionPanel_React.createRef(); - } - eventuallyRenderThumbnails() { - if (this.allShownNodes) { - const pending = []; - const nodes = new Map(this.allShownNodes); - const render = n => { - if (thumbnails.has(n.fa)) { - const src = thumbnails.get(n.fa); - const batch = [...nodes.get(n.fa)]; - for (let i = batch.length; i--;) { - const n = batch[i]; - let img = document.getElementById(`sharedFiles!${n.ch}`); - if (img && (img = img.querySelector('img'))) { - img.src = src; - if (img = Object(img.parentNode).parentNode) { - img.classList.add('thumb'); - } - } - } - return true; - } - }; - for (const [, [n]] of nodes) { - if (!render(n)) { - pending.push(n); - } - } - this.allShownNodes.clear(); - if (pending.length) { - fm_thumbnails('standalone', pending, render); - } - } - } - UNSAFE_componentWillMount() { - this.allShownNodes = new MapSet(); - } - componentWillUnmount() { - super.componentWillUnmount(); - delete this.allShownNodes; - } - componentDidUpdate() { - this.eventuallyRenderThumbnails(); - } - render() { - const self = this; - const room = self.props.chatRoom; - const mb = room.messagesBuff; - let contents = null; - let currentPage = mb.sharedFilesPage; - const startPos = currentPage * 12; - const endPos = startPos + 12; - let totalPages = mb.haveMoreSharedFiles ? "..." : Math.ceil(mb.sharedFiles.length / 12); - totalPages = mb.sharedFiles.length && !totalPages ? 1 : totalPages; - const haveMore = mb.haveMoreSharedFiles || currentPage + 1 < totalPages; - const files = []; - if (!mb.haveMoreSharedFiles && currentPage === totalPages) { - currentPage = mb.sharedFilesPage = Math.max(totalPages - 1, 0); - } - if (this.props.expanded) { - let prev = null; - let next = null; - if (currentPage > 0) { - prev = sharedFilesAccordionPanel_React.createElement("div", { - className: "chat-share-nav button prev", - onClick () { - mb.sharedFilesPage--; - self.safeForceUpdate(); - } - }); - } - if (haveMore) { - next = sharedFilesAccordionPanel_React.createElement("div", { - className: "chat-share-nav button next", - onClick () { - if (self.isLoadingMore) { - return; - } - if (mb.sharedFiles.length < endPos + 12) { - self.isLoadingMore = true; - mb.retrieveSharedFilesHistory(12).catch(dump).finally(() => { - self.isLoadingMore = false; - mb.sharedFilesPage++; - if (!mb.haveMoreSharedFiles && mb.sharedFilesPage > totalPages) { - mb.sharedFilesPage = totalPages - 1; - } - Soon(() => { - self.safeForceUpdate(); - }); - }); - } else { - mb.sharedFilesPage++; - } - Soon(() => { - self.safeForceUpdate(); - }); - } - }); - } - if (!mb.sharedFilesLoadedOnce) { - mb.retrieveSharedFilesHistory(12).then(() => this.safeForceUpdate()).catch(dump); - } - let sharedNodesContainer = null; - if (mb.isRetrievingSharedFiles && !self.isLoadingMore) { - sharedNodesContainer = sharedFilesAccordionPanel_React.createElement("div", { - className: "chat-dropdown empty-txt loading-initial" - }, sharedFilesAccordionPanel_React.createElement("div", { - className: "loading-spinner light small" - }, sharedFilesAccordionPanel_React.createElement("div", { - className: "main-loader" - }))); - } else if (!mb.haveMoreSharedFiles && !mb.sharedFiles.length) { - sharedNodesContainer = sharedFilesAccordionPanel_React.createElement("div", { - className: "chat-dropdown empty-txt" - }, l[19985]); - } else { - const keys = clone(mb.sharedFiles.keys()).reverse(); - for (let i = startPos; i < endPos; i++) { - var message = mb.sharedFiles[keys[i]]; - if (!message) { - continue; - } - const nodes = message.getAttachmentMeta(); - nodes.forEach((node) => { - const imgId = `sharedFiles!${ node.ch}`; - const { - icon, - showThumbnail, - isPreviewable - } = M.getMediaProperties(node); - files.push(sharedFilesAccordionPanel_React.createElement(SharedFileItem, { - message, - key: `${node.h}_${message.messageId}`, - isLoading: self.isLoadingMore, - node, - icon, - imgId, - showThumbnail, - isPreviewable, - chatRoom: room, - contact: Message.getContactForMessage(message) - })); - if (showThumbnail) { - self.allShownNodes.set(node.fa, node); - } - }); - } - sharedNodesContainer = sharedFilesAccordionPanel_React.createElement("div", null, files); - } - contents = sharedFilesAccordionPanel_React.createElement("div", { - className: "chat-dropdown content have-animation" - }, room.hasMessages() ? sharedNodesContainer : sharedFilesAccordionPanel_React.createElement("div", { - className: "chat-dropdown empty-txt" - }, l[19985]), self.isLoadingMore ? sharedFilesAccordionPanel_React.createElement("div", { - className: "loading-spinner light small" - }, sharedFilesAccordionPanel_React.createElement("div", { - className: "main-loader" - })) : null, files.length > 0 ? sharedFilesAccordionPanel_React.createElement("div", { - className: "chat-share-nav body" - }, prev, next, sharedFilesAccordionPanel_React.createElement("div", { - className: "chat-share-nav pages" - }, (l[19988] ? l[19988] : "Page %1").replace("%1", currentPage + 1))) : null); - } - return sharedFilesAccordionPanel_React.createElement("div", { - ref: this.domRef, - className: "chat-dropdown container" - }, sharedFilesAccordionPanel_React.createElement("div", { - className: ` - chat-dropdown - header - ${this.props.expanded ? 'expanded' : ''} - `, - onClick: this.props.onToggle - }, sharedFilesAccordionPanel_React.createElement("span", null, this.props.title), sharedFilesAccordionPanel_React.createElement("i", { - className: "sprite-fm-mono icon-arrow-down" - })), sharedFilesAccordionPanel_React.createElement("div", { - className: ` - chat-shared-files-container - ${this.isLoadingMore ? 'is-loading' : ''} - ` - }, contents)); - } -}, (0,applyDecoratedDescriptor.A)(_class.prototype, "eventuallyRenderThumbnails", [_dec], Object.getOwnPropertyDescriptor(_class.prototype, "eventuallyRenderThumbnails"), _class.prototype), _class); - -;// ./js/chat/ui/incomingSharesAccordionPanel.jsx -const incomingSharesAccordionPanel_React = REQ_(594); - -const SharedFolderItem = ({ - node, - isLoading -}) => { - return incomingSharesAccordionPanel_React.createElement("div", { - key: node.h, - className: ` - chat-shared-block - incoming - ${isLoading ? 'is-loading' : ''} - `, - onClick: () => M.openFolder(node.h), - onDoubleClick: () => M.openFolder(node.h) - }, incomingSharesAccordionPanel_React.createElement("div", { - className: "item-type-icon-90 icon-folder-incoming-90" - }), incomingSharesAccordionPanel_React.createElement("div", { - className: "chat-shared-info" - }, incomingSharesAccordionPanel_React.createElement("span", { - className: "txt" - }, node.name), incomingSharesAccordionPanel_React.createElement("span", { - className: "txt small" - }, fm_contains(node.tf, node.td)))); -}; -class IncSharesAccordionPanel extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = incomingSharesAccordionPanel_React.createRef(); - } - UNSAFE_componentWillMount() { - this.hadLoaded = false; - } - getContactHandle() { - const self = this; - const room = self.props.chatRoom; - const contactHandle = room.getParticipantsExceptMe()[0]; - if (!contactHandle || room.type !== "private") { - return {}; - } - return contactHandle; - } - render() { - const self = this; - const room = self.props.chatRoom; - const contactHandle = self.getContactHandle(); - let contents = null; - if (this.props.expanded) { - if (!this.hadLoaded) { - this.hadLoaded = true; - self.isLoadingMore = true; - dbfetch.geta(Object.keys(M.c.shares || {}), new MegaPromise()).always(() => { - self.isLoadingMore = false; - Soon(() => { - if (self.isComponentEventuallyVisible()) { - self.safeForceUpdate(); - } - }, 5000); - }); - } - let incomingSharesContainer = null; - const sharedFolders = M.c[contactHandle] && Object.keys(M.c[contactHandle]) || []; - if (!self.isLoadingMore && (!sharedFolders || sharedFolders.length === 0)) { - incomingSharesContainer = incomingSharesAccordionPanel_React.createElement("div", { - className: "chat-dropdown empty-txt" - }, l[19986]); - } else { - const haveMore = sharedFolders.length > 10; - const defSortFn = M.getSortByNameFn(); - sharedFolders.sort((a, b) => { - const nodeA = M.d[a]; - const nodeB = M.d[b]; - return defSortFn(nodeA, nodeB, -1); - }); - const renderNodes = []; - for (let i = 0; i < Math.min(sharedFolders.length, 10); i++) { - const nodeHandle = sharedFolders[i]; - const node = M.d[nodeHandle]; - if (!node) { - continue; - } - renderNodes.push(incomingSharesAccordionPanel_React.createElement(SharedFolderItem, { - key: node.h, - isLoading: self.isLoadingMore, - node, - chatRoom: room - })); - } - incomingSharesContainer = incomingSharesAccordionPanel_React.createElement("div", null, renderNodes, haveMore ? incomingSharesAccordionPanel_React.createElement("div", { - className: "chat-share-nav body" - }, incomingSharesAccordionPanel_React.createElement("div", { - className: "chat-share-nav show-all", - onClick () { - M.openFolder(contactHandle); - } - }, incomingSharesAccordionPanel_React.createElement("span", { - className: "item-type-icon icon-folder-incoming-24" - }, incomingSharesAccordionPanel_React.createElement("span", { - className: "item-type-icon icon-folder-incoming-24" - })), incomingSharesAccordionPanel_React.createElement("span", { - className: "txt" - }, l[19797] ? l[19797] : "Show All"))) : null); - } - contents = incomingSharesAccordionPanel_React.createElement("div", { - className: "chat-dropdown content have-animation" - }, incomingSharesContainer, self.isLoadingMore ? incomingSharesAccordionPanel_React.createElement("div", { - className: "chat-dropdown empty-txt" - }, incomingSharesAccordionPanel_React.createElement("div", { - className: "loading-spinner light small" - }, incomingSharesAccordionPanel_React.createElement("div", { - className: "main-loader" - }))) : null); - } - return incomingSharesAccordionPanel_React.createElement("div", { - ref: this.domRef, - className: "chat-dropdown container" - }, incomingSharesAccordionPanel_React.createElement("div", { - className: ` - chat-dropdown - header - ${this.props.expanded ? 'expanded' : ''} - `, - onClick: this.props.onToggle - }, incomingSharesAccordionPanel_React.createElement("span", null, this.props.title), incomingSharesAccordionPanel_React.createElement("i", { - className: "sprite-fm-mono icon-arrow-down" - })), incomingSharesAccordionPanel_React.createElement("div", { - className: ` - chat-shared-files-container - ${this.isLoadingMore ? 'is-loading' : ''} - ` - }, contents)); - } -} - -;// ./js/chat/ui/chatlinkDialog.jsx - - - - -class ChatlinkDialog extends REaCt().Component { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - this.state = { - link: l[5533], - newTopic: '' - }; - this.onClose = () => { - if (this.props.onClose) { - this.props.onClose(); - } - }; - } - retrieveChatLink(forced) { - const { - chatRoom - } = this.props; - if (is_chatlink) { - return this.setState({ - link: `${getBaseUrl()}/chat/${is_chatlink.ph}#${is_chatlink.key}` - }); - } - if (!chatRoom.topic && !forced) { - delete this.loading; - return; - } - this.loading = chatRoom.updatePublicHandle(false, true).always(() => { - this.loading = false; - if (this.domRef.current) { - this.setState({ - link: chatRoom.publicLink ? `${getBaseUrl()}/${chatRoom.publicLink}` : l[20660] - }); - } - }); - } - componentDidMount() { - this.retrieveChatLink(); - M.safeShowDialog(ChatlinkDialog.NAMESPACE, () => { - if (!this.domRef.current) { - throw new Error(`${ChatlinkDialog.NAMESPACE} dialog: component not mounted.`); - } - return $(`#${ChatlinkDialog.NAMESPACE}`); - }); - } - componentWillUnmount() { - if ($.dialog === ChatlinkDialog.NAMESPACE) { - closeDialog(); - } - } - render() { - const { - chatRoom - } = this.props; - const { - newTopic, - link - } = this.state; - const closeButton = this.loading ? null : REaCt().createElement("button", { - key: "close", - className: "mega-button negative links-button", - onClick: this.onClose - }, REaCt().createElement("span", null, l[148])); - const publicLinkDetails = chatRoom.isMeeting ? l.meeting_link_details : l[20644]; - return REaCt().createElement("div", { - ref: this.domRef - }, REaCt().createElement(modalDialogs.A.ModalDialog, (0,esm_extends.A)({}, this.state, { - id: ChatlinkDialog.NAMESPACE, - title: chatRoom.iAmOperator() && !chatRoom.topic ? chatRoom.isMeeting ? l.rename_meeting : l[9080] : '', - className: ` - chat-rename-dialog - export-chat-links-dialog - group-chat-link - ${chatRoom.topic ? '' : 'requires-topic'} - `, - onClose: this.onClose, - dialogName: "chat-link-dialog", - dialogType: chatRoom.iAmOperator() && !chatRoom.topic ? 'main' : 'graphic', - chatRoom, - popupDidMount: this.onPopupDidMount - }), chatRoom.iAmOperator() && !chatRoom.topic ? REaCt().createElement("section", { - className: "content" - }, REaCt().createElement("div", { - className: "content-block" - }, REaCt().createElement("div", { - className: "export-chat-ink-warning" - }, l[20617]), REaCt().createElement("div", { - className: "rename-input-bl", - style: { - margin: '10px auto 20px auto' - } - }, REaCt().createElement("input", { - type: "text", - name: "newTopic", - value: newTopic, - style: { - paddingLeft: 8 - }, - onChange: ev => this.setState({ - newTopic: ev.target.value - }), - onKeyPress: ev => ev.which === 13 && chatRoom.setRoomTopic(newTopic).then(() => this.retrieveChatLink(true)).catch(dump), - placeholder: l[20616], - maxLength: ChatRoom.TOPIC_MAX_LENGTH - })))) : REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("header", null, chatRoom.isMeeting ? REaCt().createElement("div", { - className: "chat-topic-icon meeting-icon" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-video-call-filled" - })) : REaCt().createElement("i", { - className: "sprite-fm-uni icon-chat-group" - }), REaCt().createElement("h2", { - id: "chat-link-dialog-title" - }, REaCt().createElement(utils.zT, null, chatRoom.getRoomTitle()))), REaCt().createElement("section", { - className: "content" - }, REaCt().createElement("div", { - className: "content-block" - }, REaCt().createElement("div", { - className: "chat-link-input" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-link-small" - }), REaCt().createElement("input", { - type: "text", - readOnly: true, - value: this.loading ? l[5533] : !chatRoom.topic ? l[20660] : link - })), REaCt().createElement("div", { - className: "info" - }, chatRoom.publicLink || is_chatlink ? publicLinkDetails : null)))), REaCt().createElement("footer", null, REaCt().createElement("div", { - className: "footer-container" - }, chatRoom.iAmOperator() && chatRoom.publicLink && REaCt().createElement("button", { - key: "deleteLink", - className: ` - mega-button - links-button - ${this.loading ? 'disabled' : ''} - `, - onClick: () => { - chatRoom.updatePublicHandle(1); - this.onClose(); - } - }, REaCt().createElement("span", null, chatRoom.isMeeting ? l.meeting_link_delete : l[20487])), chatRoom.topic ? chatRoom.publicLink || is_chatlink ? REaCt().createElement("button", { - className: ` - mega-button - positive - copy-to-clipboard - ${this.loading ? 'disabled' : ''} - `, - onClick: () => { - copyToClipboard(link, l[7654]); - if (chatRoom.isMeeting) { - eventlog(500231); - } - } - }, REaCt().createElement("span", null, l[63])) : closeButton : chatRoom.iAmOperator() ? REaCt().createElement("button", { - key: "setTopic", - className: ` - mega-button - positive - links-button - ${newTopic && newTopic.trim() ? '' : 'disabled'} - `, - onClick: () => chatRoom.setRoomTopic(newTopic).then(() => this.retrieveChatLink(true)).catch(dump) - }, REaCt().createElement("span", null, l[20615])) : closeButton)))); - } -} -ChatlinkDialog.defaultProps = { - requiresUpdateOnResize: true, - disableCheckingVisibility: true -}; -ChatlinkDialog.NAMESPACE = 'chat-link-dialog'; -;// ./js/chat/ui/pushSettingsDialog.jsx - -let _PushSettingsDialog; - - -class PushSettingsDialog extends REaCt().Component { - constructor(props) { - super(props); - this.renderOptions = () => { - return Object.keys(PushSettingsDialog.options).map(key => { - key = parseInt(key, 10) || Infinity; - return REaCt().createElement("label", { - key, - className: "radio-txt" - }, PushSettingsDialog.options[key], REaCt().createElement("div", { - className: `custom-radio small green-active ${ this.state.pushSettingsValue === key ? "radioOn" : "radioOff"}` - }, REaCt().createElement("input", { - type: "radio", - name: "time-selector", - value: key, - checked: this.state.pushSettingsValue === key, - onChange: () => this.setState({ - pushSettingsValue: key - }) - }))); - }); - }; - this.state = { - pushSettingsValue: this.props.pushSettingsValue || Infinity - }; - } - render() { - return REaCt().createElement(modalDialogs.A.ModalDialog, (0,esm_extends.A)({}, this.state, { - name: "push-settings", - title: l.dnd_mute_title, - subtitle: this.props.room.isMeeting ? l.meeting_dnd_subtitle : l[22015], - className: "push-settings-dialog", - dialogName: "push-settings-chat-dialog", - dialogType: "tool", - onClose: this.props.onClose - }), REaCt().createElement("section", { - className: "content" - }, REaCt().createElement("div", { - className: "content-block" - }, REaCt().createElement("div", null, this.renderOptions()))), REaCt().createElement("footer", null, REaCt().createElement("div", { - className: "footer-container" - }, REaCt().createElement("button", { - className: "mega-button", - onClick: this.props.onClose - }, REaCt().createElement("span", null, l.msg_dlg_cancel)), REaCt().createElement("button", { - className: "mega-button positive", - onClick: () => this.props.onConfirm(this.state.pushSettingsValue) - }, REaCt().createElement("span", null, l[726]))))); - } -} -_PushSettingsDialog = PushSettingsDialog; -PushSettingsDialog.options = { - 30: l[22012], - 60: l[19048], - 360: l[22013], - 1440: l[22014], - Infinity: l[22011] -}; -PushSettingsDialog.default = _PushSettingsDialog.options[_PushSettingsDialog.options.length - 1]; -// EXTERNAL MODULE: ./js/chat/ui/meetings/call.jsx + 11 modules -const call = REQ_(3); -// EXTERNAL MODULE: ./js/chat/ui/historyPanel.jsx + 7 modules -const historyPanel = REQ_(814); -// EXTERNAL MODULE: ./js/chat/ui/composedTextArea.jsx + 1 modules -const composedTextArea = REQ_(77); -;// ./js/chat/ui/meetings/workflow/loading.jsx - -class Loading extends REaCt().Component { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - this.PERMISSIONS = { - VIDEO: 'camera', - AUDIO: 'microphone' - }; - this.state = { - pendingPermissions: false - }; - this.queryPermissions = name => { - navigator.permissions.query({ - name - }).then(status => { - const { - name, - state - } = status; - status.onchange = () => name === 'audio_capture' && this.queryPermissions(this.PERMISSIONS.VIDEO); - if (state === 'prompt') { - return this.domRef.current && this.setState({ - pendingPermissions: name - }); - } - }).catch(ex => console.warn(`Failed to get permissions state: ${ex}`)); - }; - this.renderLoading = () => { - return REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("span", null, REaCt().createElement("i", { - className: "sprite-fm-mono icon-video-call-filled" - })), REaCt().createElement("h3", null, this.props.title || l[5533]), REaCt().createElement("div", { - className: "loading-container" - }, REaCt().createElement("div", { - className: "loading-indication" - }))); - }; - this.renderDebug = () => { - const { - chatRoom - } = this.props; - if (chatRoom && chatRoom.call) { - return REaCt().createElement("div", { - className: `${Loading.NAMESPACE}-debug` - }, REaCt().createElement("div", null, "callId: ", chatRoom.call.callId), REaCt().createElement("div", null, "roomId: ", chatRoom.roomId), REaCt().createElement("div", null, "isMeeting: ", chatRoom.isMeeting ? 'true' : 'false')); - } - }; - } - componentWillUnmount() { - megaChat.unbind(`onLocalMediaQueryError.${Loading.NAMESPACE}`); - } - componentDidMount() { - let _notify, _alarm; - document.dispatchEvent(new Event('closeDropdowns')); - if ($.dialog) { - closeDialog == null || closeDialog(); - } - mega.ui.mInfoPanel.hide(); - (_notify = notify) == null || _notify.closePopup(); - (_alarm = alarm) == null || _alarm.hideAllWarningPopups(); - document.querySelectorAll('.js-dropdown-account').forEach(({ - classList - }) => classList.contains('show') && classList.remove('show')); - const { - chatRoom - } = this.props; - const { - audio, - video - } = chatRoom.meetingsLoading; - const isVideoCall = audio && video; - if (audio && !video) { - this.queryPermissions(this.PERMISSIONS.AUDIO); - } - if (isVideoCall) { - Object.values(this.PERMISSIONS).forEach(name => this.queryPermissions(name)); - } - megaChat.rebind(`onLocalMediaQueryError.${Loading.NAMESPACE}`, (ev, { - type, - err - }) => { - if (isVideoCall && type === 'mic' && String(err).includes('dismissed')) { - this.queryPermissions(this.PERMISSIONS.VIDEO); - } - }); - } - render() { - const { - pendingPermissions - } = this.state; - return REaCt().createElement("div", { - ref: this.domRef, - className: Loading.NAMESPACE - }, REaCt().createElement("div", { - className: `${Loading.NAMESPACE}-content` - }, pendingPermissions ? REaCt().createElement("h2", null, pendingPermissions === 'audio_capture' ? l.permissions_allow_mic : l.permissions_allow_camera) : this.renderLoading()), d ? this.renderDebug() : ''); - } -} -Loading.NAMESPACE = 'meetings-loading'; -// EXTERNAL MODULE: ./js/chat/ui/meetings/button.jsx -const meetings_button = REQ_(959); -// EXTERNAL MODULE: ./js/chat/ui/meetings/workflow/preview.jsx -const preview = REQ_(485); -// EXTERNAL MODULE: ./js/chat/ui/link.jsx -const ui_link = REQ_(280); -;// ./js/chat/ui/meetings/workflow/join.jsx - - - - - - - -class Join extends REaCt().Component { - constructor(props) { - super(props); - this.state = { - preview: false, - view: Join.VIEW.INITIAL, - firstName: '', - lastName: '', - previewAudio: true, - previewVideo: false, - ephemeralDialog: false - }; - this.handleKeyDown = ({ - key - }) => { - let _this$props$onClose, _this$props; - return key && key === 'Escape' ? (_this$props$onClose = (_this$props = this.props).onClose) == null ? void 0 : _this$props$onClose.call(_this$props) : true; - }; - this.showPanels = () => { - return [document.querySelector('.nw-fm-left-icons-panel'), document.querySelector('.chat-app-container')].map(el => el && el.classList.remove('hidden')); - }; - this.hidePanels = () => { - return [document.querySelector('.nw-fm-left-icons-panel'), document.querySelector('.chat-app-container')].map(el => el && el.classList.add('hidden')); - }; - this.showConfirmationDialog = () => { - megaChat.destroy(); - return mega.ui.sendSignupLinkDialog(JSON.parse(localStorage.awaitingConfirmationAccount), () => { - delete localStorage.awaitingConfirmationAccount; - u_logout(true).then(() => location.reload()); - }); - }; - this.Ephemeral = () => { - const onCancel = () => this.setState({ - ephemeralDialog: false - }); - const msgFragments = l.ephemeral_data_lost.split(/\[A]|\[\/A]/); - return REaCt().createElement(modalDialogs.A.ModalDialog, { - name: "end-ephemeral", - dialogType: "message", - icon: "sprite-fm-uni icon-warning", - title: l.ephemeral_data_lost_title, - noCloseOnClickOutside: true, - buttons: [{ - key: 'cancel', - label: l.msg_dlg_cancel, - onClick: onCancel - }, { - key: 'continue', - label: l[507], - className: 'positive', - onClick: () => { - u_logout(true).then(() => location.reload()); - sessionStorage.guestForced = true; - } - }], - onClose: onCancel - }, REaCt().createElement("p", null, msgFragments[0], REaCt().createElement(ui_link.A, { - to: "/register", - onClick: () => loadSubPage('register') - }, msgFragments[1]), msgFragments[2])); - }; - this.Head = () => { - let _this$props$chatRoom; - return REaCt().createElement("div", { - className: `${Join.NAMESPACE}-head` - }, REaCt().createElement("div", { - className: `${Join.NAMESPACE}-logo` - }, REaCt().createElement("i", { - className: ` - sprite-fm-illustration-wide - ${mega.ui.isDarkTheme() ? 'mega-logo-dark' : 'img-mega-logo-light'} - ` - })), REaCt().createElement("h1", null, REaCt().createElement(utils.zT, null, l.you_have_invitation.replace('%1', (_this$props$chatRoom = this.props.chatRoom) == null ? void 0 : _this$props$chatRoom.topic))), isEphemeral() && REaCt().createElement("div", { - className: "ephemeral-info" - }, REaCt().createElement("i", { - className: "sprite-fm-uni icon-warning" - }), REaCt().createElement("p", null, l.ephemeral_data_store_lost))); - }; - this.Intro = () => { - const $$CONTAINER = ({ - children - }) => REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("div", { - className: `${Join.NAMESPACE}-content` - }, children), this.Chat()); - if (isEphemeral()) { - return REaCt().createElement($$CONTAINER, null, REaCt().createElement(meetings_button.A, { - className: "mega-button positive", - onClick: () => this.setState({ - ephemeralDialog: true - }) - }, l.join_as_guest), REaCt().createElement(meetings_button.A, { - className: "mega-button", - onClick: () => loadSubPage('register') - }, l[5582]), REaCt().createElement("span", null, l[5585], REaCt().createElement("a", { - href: "#", - onClick: () => mega.ui.showLoginRequiredDialog({ - minUserType: 3, - skipInitialDialog: 1 - }).done(() => this.setState({ - view: Join.VIEW.ACCOUNT - })) - }, l[171]))); - } - return REaCt().createElement($$CONTAINER, null, REaCt().createElement(meetings_button.A, { - className: "mega-button positive", - onClick: () => this.setState({ - view: Join.VIEW.GUEST - }) - }, l.join_as_guest), REaCt().createElement(meetings_button.A, { - className: "mega-button", - onClick: () => { - let _this$props$chatRoom2; - megaChat.loginOrRegisterBeforeJoining((_this$props$chatRoom2 = this.props.chatRoom) == null ? void 0 : _this$props$chatRoom2.publicChatHandle, false, true, undefined, () => this.setState({ - view: Join.VIEW.ACCOUNT - })); - } - }, l[171]), REaCt().createElement("p", null, REaCt().createElement(utils.P9, { - onClick: e => { - e.preventDefault(); - megaChat.loginOrRegisterBeforeJoining(this.props.chatRoom.publicChatHandle, true, undefined, undefined, () => this.setState({ - view: Join.VIEW.ACCOUNT - })); - } - }, l[20635]))); - }; - this.Chat = () => { - const { - chatRoom - } = this.props; - const { - preview - } = this.state; - return REaCt().createElement("div", { - className: ` - ${Join.NAMESPACE}-chat - ${preview ? 'expanded' : ''} - ` - }, REaCt().createElement("div", { - className: "chat-content" - }, REaCt().createElement("div", { - className: "chat-content-head", - onClick: () => this.setState({ - preview: !preview - }) - }, REaCt().createElement(utils.zT, null, chatRoom.topic), REaCt().createElement(meetings_button.A, { - icon: "icon-minimise" - })), preview && REaCt().createElement("div", { - className: "chat-body" - }, REaCt().createElement(historyPanel.A, { - chatRoom, - onMount: cmp => { - let _cmp$messagesListScro; - return (_cmp$messagesListScro = cmp.messagesListScrollable) == null ? void 0 : _cmp$messagesListScro.scrollToBottom(); - } - })))); - }; - this.Card = ({ - children - }) => { - const { - previewAudio, - previewVideo - } = this.state; - return REaCt().createElement("div", { - className: "card" - }, REaCt().createElement("div", { - className: "card-body" - }, children, REaCt().createElement("div", null, REaCt().createElement(ui_link.A, { - to: "https://mega.io/chatandmeetings", - target: "_blank" - }, l.how_meetings_work))), REaCt().createElement("div", { - className: "card-preview" - }, REaCt().createElement(preview.A, { - audio: previewAudio, - video: previewVideo, - context: Join.NAMESPACE, - onToggle: (audio, video) => this.setState({ - previewAudio: audio, - previewVideo: video - }) - }))); - }; - this.Field = ({ - name, - children - }) => { - let _this$state$name; - return REaCt().createElement("div", { - className: ` - mega-input - title-ontop - ${(_this$state$name = this.state[name]) != null && _this$state$name.length ? 'valued' : ''} - ` - }, REaCt().createElement("div", { - className: "mega-input-title" - }, children, REaCt().createElement("span", { - className: "required-red" - }, "*")), REaCt().createElement("input", { - type: "text", - name, - className: "titleTop required megaInputs", - placeholder: children, - value: this.state[name] || '', - maxLength: 40, - onChange: ev => this.setState({ - [name]: ev.target.value - }) - })); - }; - this.Guest = () => REaCt().createElement(this.Card, null, REaCt().createElement("h2", null, l.enter_name_join_meeting), REaCt().createElement("div", { - className: "card-fields" - }, REaCt().createElement(this.Field, { - name: "firstName" - }, l[1096]), REaCt().createElement(this.Field, { - name: "lastName" - }, l[1097])), REaCt().createElement(meetings_button.A, { - className: ` - mega-button - positive - large - ${this.state.firstName.length && this.state.lastName.length ? '' : 'disabled'} - ${this.state.joining && " loading disabled"} - `, - onClick: () => { - if (this.state.joining) { - return; - } - let { - firstName, - lastName, - previewAudio, - previewVideo - } = this.state; - firstName = firstName && firstName.trim(); - lastName = lastName && lastName.trim(); - if (firstName && lastName && firstName.length > 0 && lastName.length > 0) { - this.setState({ - 'joining': true - }); - if (this.props.chatRoom.scheduledMeeting) { - delay('chat-event-sm-guest-join', () => eventlog(99929)); - } - this.props.onJoinGuestClick(firstName, lastName, previewAudio, previewVideo); - } - } - }, l.join_chat_button)); - this.Account = () => REaCt().createElement(this.Card, null, REaCt().createElement("h4", null, l.join_meeting), REaCt().createElement(meetings_button.A, { - className: `mega-button positive large ${this.state.joining && " loading disabled"}`, - onClick: () => { - if (!this.state.joining) { - this.setState({ - 'joining': true - }); - this.props.onJoinClick(this.state.previewAudio, this.state.previewVideo); - } - } - }, l.join_chat_button)); - this.Unsupported = () => REaCt().createElement("div", { - className: "meetings-unsupported-container" - }, REaCt().createElement("i", { - className: "sprite-fm-uni icon-error" - }), REaCt().createElement("div", { - className: "unsupported-info" - }, REaCt().createElement("h3", null, l.heading_unsupported_browser), REaCt().createElement("h3", null, l.join_meeting_methods), REaCt().createElement("ul", null, REaCt().createElement("li", null, l.join_via_link), REaCt().createElement("li", null, REaCt().createElement(utils.P9, null, l.join_via_mobile.replace('[A]', '').replace('[/A]', '')))))); - this.View = view => { - switch (view) { - default: - return this.Intro(); - case Join.VIEW.GUEST: - return this.Guest(); - case Join.VIEW.ACCOUNT: - return this.Account(); - case Join.VIEW.UNSUPPORTED: - return this.Unsupported(); - } - }; - this.state.view = sessionStorage.guestForced ? Join.VIEW.GUEST : props.initialView || this.state.view; - if (localStorage.awaitingConfirmationAccount) { - this.showConfirmationDialog(); - } - } - componentDidMount() { - document.addEventListener('keydown', this.handleKeyDown); - this.hidePanels(); - megaChat._joinDialogIsShown = true; - alarm.hideAllWarningPopups(); - sessionStorage.removeItem('guestForced'); - if (!megaChat.hasSupportForCalls) { - this.setState({ - view: Join.VIEW.UNSUPPORTED - }); - } - } - componentWillUnmount() { - document.removeEventListener('keydown', this.handleKeyDown); - this.showPanels(); - megaChat._joinDialogIsShown = false; - if (this.props.onClose) { - this.props.onClose(); - } - } - render() { - const { - view, - ephemeralDialog - } = this.state; - return REaCt().createElement(utils.Ay.RenderTo, { - element: document.body - }, REaCt().createElement("div", { - className: Join.NAMESPACE - }, this.Head(), this.View(view), ephemeralDialog && REaCt().createElement(this.Ephemeral, null))); - } -} -Join.NAMESPACE = 'join-meeting'; -Join.VIEW = { - INITIAL: 0, - GUEST: 1, - ACCOUNT: 2, - UNSUPPORTED: 4 -}; -;// ./js/chat/ui/meetings/workflow/alert.jsx - - -const NAMESPACE = 'meetings-alert'; -class Alert extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - } - componentWillUnmount() { - let _this$props$onTransit, _this$props; - super.componentWillUnmount(); - (_this$props$onTransit = (_this$props = this.props).onTransition) == null || _this$props$onTransit.call(_this$props); - } - componentDidUpdate() { - let _this$props$onTransit2, _this$props2; - super.componentDidUpdate(); - (_this$props$onTransit2 = (_this$props2 = this.props).onTransition) == null || _this$props$onTransit2.call(_this$props2, this.domRef); - } - componentDidMount() { - let _this$props$onTransit3, _this$props3; - super.componentDidMount(); - (_this$props$onTransit3 = (_this$props3 = this.props).onTransition) == null || _this$props$onTransit3.call(_this$props3, this.domRef); - } - render() { - const { - type, - className, - content, - children, - offset, - onClose - } = this.props; - if (content || children) { - return REaCt().createElement("div", { - ref: this.domRef, - className: ` - ${NAMESPACE} - ${type ? `${NAMESPACE}-${type}` : ''} - ${className || ''} - `, - style: offset ? { - marginTop: `${offset}px` - } : undefined - }, REaCt().createElement("div", { - className: `${NAMESPACE}-content` - }, content || children), onClose && REaCt().createElement("span", { - className: `${NAMESPACE}-close`, - onClick: onClose - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-close-component" - }))); - } - return null; - } -} -Alert.TYPE = { - LIGHT: 'light', - NEUTRAL: 'neutral', - MEDIUM: 'medium', - HIGH: 'high', - ERROR: 'error' -}; -// EXTERNAL MODULE: ./js/chat/ui/meetings/schedule/helpers.jsx -const helpers = REQ_(110); -// EXTERNAL MODULE: ./js/chat/ui/meetings/hostsObserver.jsx -const hostsObserver = REQ_(972); -;// ./js/chat/ui/meetings/waitingRoom/waitingRoom.jsx - - - - - - -const waitingRoom_NAMESPACE = 'waiting-room'; -const VIEW = { - INTRO: 0, - ACCOUNT: 1, - GUEST: 2, - AWAIT: 3, - UNSUPPORTED: 4, - REDIRECT: 5 -}; -class WaitingRoom extends mixins.w9 { - constructor(props) { - super(props); - this.domRef = REaCt().createRef(); - this.redirectInterval = undefined; - this.state = { - view: VIEW.ACCOUNT, - call: false, - audio: false, - video: false, - firstName: '', - lastName: '', - countdown: 4, - loading: false - }; - this.renderLeaveDialog = () => msgDialog(`confirmation:!^${l.wr_leave}!${l.wr_do_not_leave}`, null, l.wr_leave_confirmation, '', cb => { - if (cb) { - delay('chat-event-wr-leave', () => eventlog(99938)); - this.doLeave(); - } - }, 1); - this.renderDeniedDialog = () => msgDialog('error', '', l.wr_denied, l.wr_denied_details, this.doLeave); - this.renderTimeoutDialog = () => msgDialog('error', '', l.wr_timeout, l.wr_timeout_details, this.doLeave); - this.renderWaitingRoomInfo = () => { - const { - chatRoom - } = this.props; - const { - nextOccurrenceStart, - nextOccurrenceEnd - } = chatRoom.scheduledMeeting || {}; - return REaCt().createElement(REaCt().Fragment, null, REaCt().createElement(utils.P9, { - tag: "h2", - content: megaChat.html(chatRoom.topic) - }), REaCt().createElement("div", { - className: `${waitingRoom_NAMESPACE}-schedule` - }, REaCt().createElement("span", null, time2date(nextOccurrenceStart / 1000, 20)), REaCt().createElement("span", null, toLocaleTime(nextOccurrenceStart), " - ", toLocaleTime(nextOccurrenceEnd)))); - }; - this.doLeave = () => this.setState({ - view: VIEW.REDIRECT - }, () => { - tSleep(this.state.countdown).then(() => this.props.onWaitingRoomLeave()); - this.redirectInterval = setInterval(() => this.setState(({ - countdown - }) => ({ - countdown: countdown > 0 ? countdown - 1 : 0 - })), 1e3); - sessionStorage.removeItem('previewMedia'); - }); - this.setInitialView = () => { - if (u_type || is_eplusplus) { - let _this$props$chatRoom; - return (_this$props$chatRoom = this.props.chatRoom) != null && _this$props$chatRoom.iAmInRoom() ? VIEW.AWAIT : VIEW.ACCOUNT; - } - return VIEW.INTRO; - }; - this.requestJoin = () => { - let _this$props$chatRoom2; - const { - audio, - video - } = this.state; - (_this$props$chatRoom2 = this.props.chatRoom) == null || _this$props$chatRoom2.joinCall(audio, video); - }; - this.Field = ({ - name, - children - }) => { - let _this$state$name; - return REaCt().createElement("div", { - className: ` - mega-input - title-ontop - ${(_this$state$name = this.state[name]) != null && _this$state$name.length ? 'valued' : ''} - ` - }, REaCt().createElement("div", { - className: "mega-input-title" - }, children, REaCt().createElement("span", { - className: "required-red" - }, "*")), REaCt().createElement("input", { - type: "text", - name, - className: "titleTop required megaInputs", - placeholder: children, - value: this.state[name] || '', - maxLength: 40, - onChange: ev => this.setState({ - [name]: ev.target.value - }) - })); - }; - this.Card = ({ - className, - children - }) => { - const { - audio, - video - } = this.state; - return REaCt().createElement("div", { - className: ` - card - ${className || ''} - ` - }, REaCt().createElement("div", { - className: "card-body" - }, children), REaCt().createElement("div", { - className: "card-preview" - }, REaCt().createElement(preview.A, { - audio, - video, - onToggle: (audio, video) => { - this.setState({ - audio, - video - }, () => { - sessionStorage.previewMedia = JSON.stringify({ - audio, - video - }); - }); - } - }))); - }; - this.Head = ({ - title - }) => { - let _this$props$chatRoom3; - return REaCt().createElement("div", { - className: `${waitingRoom_NAMESPACE}-head` - }, REaCt().createElement("div", { - className: `${waitingRoom_NAMESPACE}-logo` - }, REaCt().createElement("i", { - className: ` - sprite-fm-illustration-wide - ${mega.ui.isDarkTheme() ? 'mega-logo-dark' : 'img-mega-logo-light'} - ` - })), REaCt().createElement("h1", { - className: (megaChat.initialChatId || is_chatlink) && this.state.view !== VIEW.INTRO ? 'hidden' : '' - }, REaCt().createElement(utils.zT, null, title || l.you_have_invitation.replace('%1', (_this$props$chatRoom3 = this.props.chatRoom) == null ? void 0 : _this$props$chatRoom3.topic)))); - }; - this.Await = () => { - return REaCt().createElement(REaCt().Fragment, null, megaChat.initialChatId ? REaCt().createElement(this.Head, null) : null, REaCt().createElement(this.Card, { - className: megaChat.initialChatId ? '' : 'fit-spacing' - }, this.renderWaitingRoomInfo(), REaCt().createElement("div", { - className: `${waitingRoom_NAMESPACE}-message` - }, this.state.call ? l.wr_wait_to_admit : l.wr_wait_to_start), REaCt().createElement(meetings_button.A, { - icon: "sprite-fm-mono icon-log-out-thin-solid", - className: `${waitingRoom_NAMESPACE}-leave`, - onClick: () => this.renderLeaveDialog() - }, l.wr_leave))); - }; - this.Account = () => { - const { - loading, - audio, - video - } = this.state; - return REaCt().createElement(REaCt().Fragment, null, REaCt().createElement(this.Head, null), REaCt().createElement(this.Card, null, this.renderWaitingRoomInfo(), REaCt().createElement(meetings_button.A, { - className: ` - mega-button - positive - large - ${loading ? 'disabled' : ''} - `, - onClick: () => { - return loading ? null : this.setState({ - loading: true - }, () => { - const { - chatRoom - } = this.props; - const { - chatId, - publicChatHandle, - publicChatKey - } = chatRoom; - if (chatRoom.iAmInRoom()) { - return megaChat.routing.reinitAndOpenExistingChat(chatId, publicChatHandle).then(() => { - megaChat.getChatById(chatId).joinCall(audio, video); - }).catch(ex => console.error(`Failed to open existing room and join call: ${ex}`)); - } - megaChat.routing.reinitAndJoinPublicChat(chatId, publicChatHandle, publicChatKey).then(() => { - delete megaChat.initialPubChatHandle; - }).catch(ex => console.error(`Failed to join room: ${ex}`)); - }); - } - }, l.wr_ask_to_join), REaCt().createElement("div", null, REaCt().createElement(ui_link.A, { - to: "https://mega.io/chatandmeetings", - target: "_blank" - }, l.how_meetings_work)))); - }; - this.Redirect = () => REaCt().createElement(REaCt().Fragment, null, REaCt().createElement(this.Head, { - title: l.wr_left_heading - }), REaCt().createElement("h5", null, l.wr_left_countdown.replace('%1', this.state.countdown))); - this.Guest = () => { - const { - chatRoom - } = this.props; - const { - loading, - firstName, - lastName - } = this.state; - const isDisabled = !firstName.length || !lastName.length; - return REaCt().createElement(REaCt().Fragment, null, REaCt().createElement(this.Head, null), REaCt().createElement(this.Card, null, this.renderWaitingRoomInfo(), REaCt().createElement("div", { - className: "card-fields" - }, REaCt().createElement(this.Field, { - name: "firstName" - }, l[1096]), REaCt().createElement(this.Field, { - name: "lastName" - }, l[1097])), REaCt().createElement(meetings_button.A, { - className: ` - mega-button - positive - large - ${isDisabled || loading ? 'disabled' : ''} - `, - onClick: () => { - if (isDisabled || loading) { - return false; - } - return this.setState({ - loading: true - }, () => { - u_eplusplus(this.state.firstName, this.state.lastName).then(() => { - return megaChat.routing.reinitAndJoinPublicChat(chatRoom.chatId, chatRoom.publicChatHandle, chatRoom.publicChatKey); - }).catch(ex => d && console.error(`E++ account failure: ${ex}`)); - }); - } - }, l.wr_ask_to_join), REaCt().createElement("div", null, REaCt().createElement(ui_link.A, { - to: "https://mega.io/chatandmeetings", - target: "_blank" - }, l.how_meetings_work)))); - }; - this.Intro = () => { - const { - chatRoom - } = this.props; - return REaCt().createElement(REaCt().Fragment, null, REaCt().createElement(this.Head, null), REaCt().createElement("div", { - className: "join-meeting-content" - }, REaCt().createElement(meetings_button.A, { - className: "mega-button positive", - onClick: () => { - megaChat.loginOrRegisterBeforeJoining(chatRoom.publicChatHandle, false, true, undefined, () => this.setState({ - view: VIEW.ACCOUNT - })); - } - }, l[171]), REaCt().createElement(meetings_button.A, { - className: "mega-button", - onClick: () => this.setState({ - view: VIEW.GUEST - }) - }, l.join_as_guest), REaCt().createElement("p", null, REaCt().createElement(utils.P9, { - onClick: e => { - e.preventDefault(); - megaChat.loginOrRegisterBeforeJoining(chatRoom.publicChatHandle, true, undefined, undefined, () => this.setState({ - view: VIEW.ACCOUNT - })); - } - }, l[20635])))); - }; - this.Unsupported = () => { - let _this$props$chatRoom4; - return REaCt().createElement(REaCt().Fragment, null, REaCt().createElement(this.Head, null), REaCt().createElement("h1", null, l.you_have_invitation.replace('%1', (_this$props$chatRoom4 = this.props.chatRoom) == null ? void 0 : _this$props$chatRoom4.topic)), REaCt().createElement("div", { - className: "meetings-unsupported-container" - }, REaCt().createElement("i", { - className: "sprite-fm-uni icon-error" - }), REaCt().createElement("div", { - className: "unsupported-info" - }, REaCt().createElement("h3", null, l.heading_unsupported_browser), REaCt().createElement("h3", null, l.join_meeting_methods), REaCt().createElement("ul", null, REaCt().createElement("li", null, l.join_via_link), REaCt().createElement("li", null, REaCt().createElement(utils.P9, null, l.join_via_mobile.replace('[A]', '').replace('[/A]', ''))))))); - }; - this.renderView = view => { - switch (view) { - default: - return this.Await(); - case VIEW.INTRO: - return this.Intro(); - case VIEW.GUEST: - return this.Guest(); - case VIEW.ACCOUNT: - return this.Account(); - case VIEW.REDIRECT: - return this.Redirect(); - case VIEW.UNSUPPORTED: - return this.Unsupported(); - } - }; - this.state.call = this.props.havePendingCall; - this.state.view = megaChat.hasSupportForCalls ? this.setInitialView() : VIEW.UNSUPPORTED; - if (sessionStorage.previewMedia) { - const { - audio, - video - } = JSON.parse(sessionStorage.previewMedia); - this.state.audio = audio; - this.state.video = video; - sessionStorage.removeItem('previewMedia'); - } - } - UNSAFE_componentWillReceiveProps(nextProps) { - if (this.props.havePendingCall !== nextProps.havePendingCall) { - this.setState({ - call: nextProps.havePendingCall - }, () => this.state.view === VIEW.AWAIT && nextProps.havePendingCall && this.requestJoin()); - } - } - componentWillUnmount() { - super.componentWillUnmount(); - clearInterval(this.redirectInterval); - this.props.chatRoom.unbind(`onCallLeft.${waitingRoom_NAMESPACE}`); - this.props.chatRoom.unbind(`onModeratorAdd.${waitingRoom_NAMESPACE}`); - } - componentDidMount() { - super.componentDidMount(); - const { - chatRoom - } = this.props; - const { - call, - view - } = this.state; - if (call && view === VIEW.AWAIT) { - this.requestJoin(); - } - chatRoom.rebind(`onCallLeft.${waitingRoom_NAMESPACE}`, (ev, { - termCode - }) => { - if (termCode === SfuClient.TermCode.kKickedFromWaitingRoom) { - return this.renderDeniedDialog(); - } - if (termCode === SfuClient.TermCode.kWaitingRoomAllowTimeout) { - delay('chat-event-wr-timeout', () => eventlog(99939)); - return this.renderTimeoutDialog(); - } - }); - chatRoom.rebind(`onModeratorAdd.${waitingRoom_NAMESPACE}`, (ev, user) => { - if (user === u_handle) { - chatRoom.meetingsLoading = false; - this.requestJoin(); - } - }); - } - render() { - const { - view - } = this.state; - return REaCt().createElement(utils.Ay.RenderTo, { - element: document.body - }, REaCt().createElement("div", { - ref: this.domRef, - className: ` - ${waitingRoom_NAMESPACE} - join-meeting - ${view === VIEW.AWAIT ? `${waitingRoom_NAMESPACE}--await` : ''} - ${view === VIEW.AWAIT && !megaChat.initialChatId ? 'theme-dark-forced' : ''} - ${view === VIEW.REDIRECT ? `${waitingRoom_NAMESPACE}--redirect` : ''} - ${megaChat.initialChatId || is_chatlink ? `${waitingRoom_NAMESPACE}--chatlink-landing` : ''} - ` - }, this.renderView(view))); - } -} -// EXTERNAL MODULE: ./js/chat/ui/meetings/streamControls.jsx -const streamControls = REQ_(489); -// EXTERNAL MODULE: ./js/chat/ui/inviteParticipantsPanel.jsx -const inviteParticipantsPanel = REQ_(815); -;// ./js/chat/ui/chatOverlay.jsx - - - -const chatOverlay_NAMESPACE = 'chat-overlay'; -const ChatOverlays = { - PARTICIPANT_LIMIT: 'participants-limit' -}; -class ChatOverlay extends REaCt().Component { - constructor(...args) { - super(...args); - this.MegaLogo = () => REaCt().createElement("div", { - className: `${chatOverlay_NAMESPACE}-logo` - }, REaCt().createElement("i", { - className: `sprite-fm-illustration-wide ${mega.ui.isDarkTheme() ? 'mega-logo-dark' : 'img-mega-logo-light'}` - })); - } - renderParticipantsLimit() { - return REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("div", { - className: `${chatOverlay_NAMESPACE}-head` - }, REaCt().createElement(this.MegaLogo, null), REaCt().createElement("h1", null, l.join_call_user_limit_title)), REaCt().createElement("div", { - className: `${chatOverlay_NAMESPACE}-body` - }, REaCt().createElement("p", null, l.call_join_user_limit_banner), REaCt().createElement(buttons.$, { - className: "mega-button positive", - onClick: () => { - let _this$props$onClose, _this$props; - (_this$props$onClose = (_this$props = this.props).onClose) == null || _this$props$onClose.call(_this$props); - } - }, l.call_link_user_limit_button))); - } - render() { - const { - overlayType - } = this.props; - let body = null; - if (overlayType === ChatOverlays.PARTICIPANT_LIMIT) { - body = this.renderParticipantsLimit(); - } - if (!body) { - if (d) { - console.error('Invalid ChatOverlay', overlayType); - } - return null; - } - return REaCt().createElement(utils.Ay.RenderTo, { - element: document.body - }, REaCt().createElement("div", { - className: `${chatOverlay_NAMESPACE} ${overlayType}` - }, body)); - } -} - -;// ./js/chat/ui/conversationpanel.jsx - - -let conversationpanel_dec, _dec2, conversationpanel_class; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const ENABLE_GROUP_CALLING_FLAG = true; -const MAX_USERS_CHAT_PRIVATE = 100; -const ALERTS_BASE_OFFSET = 4; -const DISMISS_TRANSITIONS = { - NOT_SHOWN: 0, - SHOWN: 1, - DISMISSED: 2 -}; -class EndCallButton extends REaCt().Component { - constructor(...args) { - super(...args); - this.IS_MODERATOR = call.Ay.isModerator(this.props.chatRoom, u_handle); - this.LeaveButton = (0,hostsObserver.C)(({ - hasHost, - chatRoom, - confirmLeave, - onLeave - }) => { - return REaCt().createElement(dropdowns.DropdownItem, { - className: "link-button", - icon: "sprite-fm-mono icon-leave-call", - label: l.leave, - persistent: true, - onClick: () => { - const doLeave = () => hasHost(chatRoom.call ? chatRoom.call.peers.map(a => a.userHandle) : []) ? onLeave() : confirmLeave({ - title: l.assign_host_leave_call, - body: l.assign_host_leave_call_details, - cta: l.assign_host_button, - altCta: l.leave_anyway - }); - const { - recorderCid, - sfuClient - } = chatRoom.call; - return recorderCid && recorderCid === sfuClient.cid ? (0,streamControls.sX)(doLeave, () => sfuClient.recordingStop()) : doLeave(); - } - }); - }); - } - renderButton({ - label, - onClick, - children = null, - disabled - }) { - return REaCt().createElement(buttons.$, { - className: ` - link-button - light - red - dropdown-element - ${disabled ? 'disabled' : ''} - `, - icon: "small-icon colorized horizontal-red-handset", - label, - onClick: disabled ? null : onClick - }, children); - } - render() { - const { - chatRoom - } = this.props; - const { - type, - call - } = chatRoom; - if (call) { - const peers = call.peers && call.peers.length; - if (type === 'private') { - return this.renderButton({ - label: l[5884], - onClick: () => call.hangUp() - }); - } - if (this.IS_MODERATOR) { - const doEnd = () => chatRoom.endCallForAll(); - return this.renderButton({ - label: l[5884], - onClick: peers ? null : () => call.hangUp(), - children: peers && REaCt().createElement(dropdowns.Dropdown, { - className: "wide-dropdown light end-call-selector", - noArrow: "true", - vertOffset: 4, - horizOffset: 0 - }, REaCt().createElement(this.LeaveButton, { - chatRoom, - participants: chatRoom.getCallParticipants(), - onLeave: () => call.hangUp(), - onConfirmDenied: () => call.hangUp() - }), REaCt().createElement(dropdowns.DropdownItem, { - className: "link-button", - icon: "sprite-fm-mono icon-contacts", - label: l.end_for_all, - onClick: () => { - const { - recorderCid, - sfuClient - } = call; - return recorderCid && recorderCid === u_handle ? (0,streamControls._F)(doEnd, () => sfuClient.recordingStop()) : doEnd(); - } - })) - }); - } - return this.renderButton({ - label: peers ? l[5883] : l[5884], - onClick: () => call.hangUp() - }) - ; - } - if (chatRoom.havePendingGroupCall()) { - return this.IS_MODERATOR ? this.renderButton({ - label: l.end_call_for_all, - onClick: () => msgDialog('confirmation', null, l.end_call_for_all_title, l.end_call_for_all_text, cb => cb ? chatRoom.endCallForAll() : 0xDEAD), - disabled: !chatRoom.iAmInRoom() - }) : null; - } - return null; - } -} -const StartMeetingNotification = ({ - chatRoom, - offset, - onWaitingRoomJoin, - onStartCall -}) => { - if (chatRoom.call || !megaChat.hasSupportForCalls) { - return null; - } - return REaCt().createElement("div", { - className: "in-call-notif neutral start", - style: { - marginTop: offset - }, - onClick: () => { - eventlog(500288); - if (chatRoom.options.w && !chatRoom.iAmOperator()) { - return onWaitingRoomJoin(); - } - return onStartCall(call.ZE.AUDIO); - } - }, REaCt().createElement("button", { - className: "mega-button positive small" - }, l.schedule_start_aot)); -}; -const JoinCallNotification = ({ - chatRoom, - offset, - rhpCollapsed -}) => { - if (chatRoom.call) { - return null; - } - if (!megaChat.hasSupportForCalls) { - return REaCt().createElement(Alert, { - className: ` - ${rhpCollapsed ? 'full-span' : ''} - ${offset === ALERTS_BASE_OFFSET ? 'single-alert' : ''} - unsupported-call-alert-progress - `, - offset: offset === ALERTS_BASE_OFFSET ? 0 : offset, - type: Alert.TYPE.MEDIUM, - content: l.active_call_not_supported - }); - } - if (chatRoom.callUserLimited && !chatRoom.canJoinLimitedCall()) { - return REaCt().createElement("div", { - className: "call-user-limit-banner", - style: { - marginTop: offset - } - }, l.call_join_user_limit_banner); - } - return REaCt().createElement("div", { - className: "in-call-notif neutral join", - style: { - marginTop: offset - } - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-phone" - }), REaCt().createElement(utils.P9, { - onClick: () => { - return (0,call.dQ)(true, chatRoom).then(() => chatRoom.joinCall()).catch(ex => d && console.warn('Already in a call.', ex)); - } - }, (l[20460] || 'There is an active group call. [A]Join[/A]').replace('[A]', ''))); -}; -const allContactsInChat = participants => { - const currentContacts = M.u.keys(); - for (let i = 0; i < currentContacts.length; i++) { - const k = currentContacts[i]; - if (M.u[k].c === 1 && !participants.includes(k)) { - return false; - } - } - return true; -}; -const excludedParticipants = room => { - const excParticipants = room.type === "group" || room.type === "public" ? room.members && Object.keys(room.members).length > 0 ? Object.keys(room.members) : room.getParticipants() : room.getParticipants(); - if (excParticipants.includes(u_handle)) { - array.remove(excParticipants, u_handle, false); - } - return excParticipants; -}; -class Occurrences extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - this.loadingMore = false; - this.state = { - editDialog: false, - occurrenceId: undefined - }; - } - loadOccurrences() { - if (!this.loadingMore) { - const { - scheduledMeeting, - occurrences - } = this.props; - const occurrenceItems = Object.values(occurrences || {}); - const lastOccurrence = occurrenceItems[occurrenceItems.length - 1]; - if (lastOccurrence) { - this.loadingMore = true; - scheduledMeeting.getOccurrences({ - from: lastOccurrence.start - }).catch(dump).finally(() => { - this.loadingMore = false; - }); - } - } - } - renderCancelConfirmation(occurrence) { - const { - scheduledMeeting, - chatRoom - } = this.props; - const nextOccurrences = Object.values(scheduledMeeting.occurrences).filter(o => o.isUpcoming); - if (nextOccurrences.length > 1) { - return msgDialog(`confirmation:!^${l.cancel_meeting_occurrence_button}!${l.schedule_cancel_abort}`, 'cancel-occurrence', l.schedule_cancel_occur_dlg_title, l.schedule_cancel_occur_dlg_text, cb => cb && occurrence.cancel(), 1); - } - return chatRoom.hasMessages(true) ? msgDialog(`confirmation:!^${l.cancel_meeting_button}!${l.schedule_cancel_abort}`, 'cancel-occurrence', l.schedule_cancel_all_dialog_title, l.schedule_cancel_all_dialog_move, cb => cb && megaChat.plugins.meetingsManager.cancelMeeting(scheduledMeeting, scheduledMeeting.chatId), 1) : msgDialog(`confirmation:!^${l.cancel_meeting_button}!${l.schedule_cancel_abort}`, 'cancel-occurrence', l.schedule_cancel_all_dialog_title, l.schedule_cancel_all_dialog_archive, cb => cb && megaChat.plugins.meetingsManager.cancelMeeting(scheduledMeeting, scheduledMeeting.chatId), 1); - } - renderLoading() { - return REaCt().createElement("div", { - className: "loading-sketch" - }, Array.from({ - length: 10 - }, (el, i) => { - return REaCt().createElement("div", { - key: i, - className: "chat-occurrence" - }, REaCt().createElement("div", { - className: "chat-occurrence-date" - }), REaCt().createElement("div", { - className: "chat-occurrence-content" - }, REaCt().createElement("div", { - className: "chat-occurrence-title" - }), REaCt().createElement("div", { - className: "chat-occurrence-time" - }))); - })); - } - renderOccurrences() { - const { - chatRoom, - occurrences, - occurrencesLoading, - scheduledMeeting - } = this.props; - if (occurrencesLoading) { - return this.renderLoading(); - } - if (occurrences && occurrences.length > 0) { - const sortedOccurrences = Object.values(occurrences).sort((a, b) => a.start - b.start); - return REaCt().createElement(REaCt().Fragment, null, sortedOccurrences.map(occurrence => occurrence.isUpcoming ? REaCt().createElement("div", { - key: occurrence.uid, - className: ` - chat-occurrence - ${occurrence.uid} - ` - }, REaCt().createElement("div", { - className: "chat-occurrence-date" - }, (0,helpers.cK)(occurrence.start) && REaCt().createElement("span", null, l.today_occurrence_label, " -"), (0,helpers.ef)(occurrence.start) && REaCt().createElement("span", null, l.tomorrow_occurrence_label, " -"), REaCt().createElement("span", null, time2date(occurrence.start / 1000, 19))), REaCt().createElement("div", { - className: "chat-occurrence-content" - }, REaCt().createElement("div", { - className: "chat-occurrence-title" - }, scheduledMeeting.title), REaCt().createElement("div", { - className: "chat-occurrence-time" - }, toLocaleTime(occurrence.start), " - \xA0", toLocaleTime(occurrence.end)), chatRoom.iAmOperator() && REaCt().createElement("div", { - className: "chat-occurrence-controls" - }, REaCt().createElement("div", { - className: "chat-occurrence-control simpletip", - "data-simpletip": l[1342], - "data-simpletipposition": "top", - "data-simpletipoffset": "5" - }, REaCt().createElement(buttons.$, { - icon: "sprite-fm-mono icon-rename", - onClick: () => { - megaChat.trigger(megaChat.plugins.meetingsManager.EVENTS.EDIT, occurrence); - } - })), REaCt().createElement("div", { - className: "chat-occurrence-control simpletip", - "data-simpletip": l.msg_dlg_cancel, - "data-simpletipposition": "top", - "data-simpletipoffset": "5" - }, REaCt().createElement(buttons.$, { - icon: "sprite-fm-mono icon-bin", - onClick: () => this.renderCancelConfirmation(occurrence) - }))))) : null)); - } - return REaCt().createElement("span", null, l.no_occurrences_remain); - } - render() { - return REaCt().createElement("div", { - ref: this.domRef, - className: "chat-occurrences-list" - }, REaCt().createElement(perfectScrollbar.O, { - chatRoom: this.props.chatRoom, - ref: ref => { - this.contactsListScroll = ref; - }, - disableCheckingVisibility: true, - onUserScroll: ps => ps.isCloseToBottom(30) && this.loadOccurrences(), - isVisible: this.isCurrentlyActive, - options: { - suppressScrollX: true - } - }, REaCt().createElement("div", { - className: "chat-occurrences-list-inner" - }, this.renderOccurrences()))); - } -} -class ConversationRightArea extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - this.state = { - contactPickerDialog: false, - inviteDialog: false - }; - this.LeaveButton = (0,hostsObserver.C)(({ - chatRoom, - hasHost, - confirmLeave, - onLeave - }) => { - const isDisabled = chatRoom.call || is_chatlink || !chatRoom.iAmInRoom(); - const participants = chatRoom.getParticipantsExceptMe(); - return REaCt().createElement("div", { - className: ` - link-button - light - ${isDisabled ? 'disabled' : ''} - `, - onClick: isDisabled ? null : () => hasHost(participants) || !participants.length ? onLeave() : confirmLeave({ - title: chatRoom.isMeeting ? l.assign_host_to_leave : l.assign_host_to_leave_group, - body: chatRoom.isMeeting ? l.assign_host_to_details : l.assign_host_to_details_group, - cta: l.assign_host_button - }) - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-disabled-filled" - }), REaCt().createElement("span", null, chatRoom.isMeeting ? l.meeting_leave : l[8633])); - }); - this.OptionsButton = ({ - icon, - label, - secondLabel, - toggled, - disabled, - onClick - }) => { - const { - chatRoom - } = this.props; - const isDisabled = !chatRoom.iAmOperator() || disabled; - return REaCt().createElement(buttons.$, { - className: ` - link-button - light - room-settings-button - `, - disabled: isDisabled, - icon: ` - sprite-fm-mono - ${icon} - `, - label, - secondLabel, - secondLabelClass: "label--green", - toggle: { - enabled: toggled, - onClick: isDisabled ? null : onClick - }, - onClick: isDisabled ? null : onClick - }); - }; - this.handleCancelMeeting = () => { - const { - chatRoom - } = this.props; - const { - scheduledMeeting, - chatId - } = chatRoom || {}; - if (scheduledMeeting) { - const { - isRecurring, - title - } = scheduledMeeting; - const doConfirm = res => { - if (res) { - megaChat.plugins.meetingsManager.cancelMeeting(scheduledMeeting, chatId); - delay('chat-event-sm-cancel', () => eventlog(99925)); - } - }; - if (isRecurring) { - return chatRoom.hasMessages(true) ? msgDialog(`confirmation:!^${l.cancel_meeting_button}!${l.schedule_cancel_abort}`, null, l.schedule_cancel_dialog_title.replace('%s', megaChat.html(title)), l.schedule_cancel_dialog_move_recurring, doConfirm, 1) : msgDialog(`confirmation:!^${l.schedule_cancel_dialog_confirm}!${l.schedule_cancel_abort}`, null, l.schedule_cancel_dialog_title.replace('%s', megaChat.html(title)), l.schedule_cancel_dialog_archive_recurring, doConfirm, 1); - } - return chatRoom.hasMessages(true) ? msgDialog(`confirmation:!^${l.cancel_meeting_button}!${l.schedule_cancel_abort}`, null, l.schedule_cancel_dialog_title.replace('%s', megaChat.html(title)), l.schedule_cancel_dialog_move_single, doConfirm, 1) : msgDialog(`confirmation:!^${l.schedule_cancel_dialog_confirm}!${l.schedule_cancel_abort}`, null, l.schedule_cancel_dialog_title.replace('%s', megaChat.html(title)), l.schedule_cancel_dialog_archive_single, doConfirm, 1); - } - }; - } - customIsEventuallyVisible() { - return this.props.chatRoom.isCurrentlyActive; - } - setRetention(chatRoom, retentionTime) { - chatRoom.setRetention(retentionTime); - $(document).trigger('closeDropdowns'); - } - renderOptionsBanner() { - const { - chatRoom - } = this.props; - return !!chatRoom.options[MCO_FLAGS.WAITING_ROOM] && !!chatRoom.options[MCO_FLAGS.OPEN_INVITE] ? REaCt().createElement("div", { - className: "room-settings-banner" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-info" - }), REaCt().createElement(utils.P9, null, l.waiting_room_invite.replace('[A]', ``).replace('[/A]', ''))) : null; - } - handleAddParticipants() { - if (Object.values(M.u.toJS()).some(u => u.c === 1)) { - if (allContactsInChat(excludedParticipants(this.props.chatRoom))) { - return msgDialog(`confirmationa:!^${l[8726]}!${l.msg_dlg_cancel}`, null, `${l.all_contacts_added}`, `${l.all_contacts_added_to_chat}`, res => { - if (res) { - contactAddDialog(null, false); - } - }, 1); - } - return this.setState({ - contactPickerDialog: true - }); - } - msgDialog(`confirmationa:!^${l[8726]}!${l.msg_dlg_cancel}`, null, `${l.no_contacts}`, `${l.no_contacts_text}`, resp => { - if (resp) { - contactAddDialog(null, false); - } - }, 1); - } - renderPushSettingsButton() { - const { - pushSettingsValue, - chatRoom, - onPushSettingsToggled, - onPushSettingsClicked - } = this.props; - const icon = pushSettingsValue || pushSettingsValue === 0 ? 'icon-notification-off-filled' : 'icon-notification-filled'; - return REaCt().createElement("div", { - className: "push-settings" - }, REaCt().createElement("div", { - className: "chat-button-separator" - }), REaCt().createElement(buttons.$, { - className: ` - link-button - light - push-settings-button - ${chatRoom.isReadOnly() ? 'disabled' : ''} - `, - icon: ` - sprite-fm-mono - ${icon} - `, - label: chatRoom.isMeeting ? l.meeting_notifications : l[16709], - secondLabel: (() => { - if (pushSettingsValue !== null && pushSettingsValue !== undefined) { - return pushSettingsValue === 0 ? PushSettingsDialog.options[Infinity] : l[23539].replace('%s', toLocaleTime(pushSettingsValue)); - } - })(), - secondLabelClass: "label--green", - toggle: chatRoom.isReadOnly() ? null : { - enabled: !pushSettingsValue && pushSettingsValue !== 0, - onClick: () => !pushSettingsValue && pushSettingsValue !== 0 ? onPushSettingsClicked() : onPushSettingsToggled() - }, - onClick: () => chatRoom.isReadOnly() ? null : onPushSettingsClicked() - }), REaCt().createElement("div", { - className: "chat-button-separator" - })); - } - componentDidMount() { - super.componentDidMount(); - megaChat.rebind(`${megaChat.plugins.meetingsManager.EVENTS.OCCURRENCES_UPDATE}.${this.getUniqueId()}`, () => { - if (this.isMounted()) { - this.safeForceUpdate(); - } - }); - megaChat.rebind(`onPrepareIncomingCallDialog.${this.getUniqueId()}`, () => { - if (this.isMounted() && this.state.inviteDialog) { - this.setState({ - inviteDialog: false - }); - } - }); - } - render() { - let _room$messagesBuff, _room$messagesBuff2, _room$messagesBuff3, _room$messagesBuff4, _room$messagesBuff5; - const self = this; - const { - chatRoom: room, - onStartCall, - occurrencesLoading, - onShowScheduledDescription - } = self.props; - if (!room || !room.roomId) { - return null; - } - if (!room.isCurrentlyActive && !self._wasAppendedEvenOnce) { - return null; - } - self._wasAppendedEvenOnce = true; - const startCallDisabled = isStartCallDisabled(room) || room.iAmWaitingRoomPeer(); - let startAudioCallButton; - let startVideoCallButton; - const isInCall = !!room.call; - if (isInCall) { - startAudioCallButton = startVideoCallButton = null; - } - if (room.type === "group" || room.type === "public") { - if (room.getCallParticipants().length > 0 && !isInCall) { - startAudioCallButton = startVideoCallButton = null; - } - } - if (startAudioCallButton !== null) { - startAudioCallButton = REaCt().createElement("div", { - "data-simpletip": l.unsupported_browser_audio, - "data-simpletipposition": "top", - "data-simpletipoffset": "7", - className: ` - link-button light - ${megaChat.hasSupportForCalls ? '' : 'simpletip'} - ${startCallDisabled ? 'disabled' : ''} - `, - onClick: () => onStartCall(call.ZE.AUDIO) - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-phone" - }), REaCt().createElement("span", null, l[5896])); - } - if (startVideoCallButton !== null) { - startVideoCallButton = REaCt().createElement("div", { - "data-simpletip": l.unsupported_browser_video, - "data-simpletipposition": "top", - "data-simpletipoffset": "7", - className: ` - link-button light - ${megaChat.hasSupportForCalls ? '' : 'simpletip'} - ${startCallDisabled ? 'disabled' : ''} - `, - onClick: () => onStartCall(call.ZE.VIDEO) - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-video-call-filled" - }), REaCt().createElement("span", null, l[5897])); - } - const AVseperator = REaCt().createElement("div", { - className: "chat-button-separator" - }); - let isReadOnlyElement = null; - if (room.isReadOnly()) { - isReadOnlyElement = REaCt().createElement("center", { - className: "center", - style: { - margin: "6px" - } - }, l.read_only_chat); - } - const exParticipants = excludedParticipants(room); - let dontShowTruncateButton = false; - if (!room.iAmOperator() || room.isReadOnly() || ((_room$messagesBuff = room.messagesBuff) == null ? void 0 : _room$messagesBuff.messages.length) === 0 || ((_room$messagesBuff2 = room.messagesBuff) == null ? void 0 : _room$messagesBuff2.messages.length) === 1 && ((_room$messagesBuff3 = room.messagesBuff) == null ? void 0 : _room$messagesBuff3.messages.getItem(0).dialogType) === "truncated") { - dontShowTruncateButton = true; - } - const renameButtonClass = ` - link-button - light - ${(0,call.P)() || room.isReadOnly() || !room.iAmOperator() ? 'disabled' : ''} - `; - const getChatLinkClass = ` - link-button - light - ${(0,call.P)() || room.isReadOnly() ? 'disabled' : ''} - `; - let participantsList = null; - if (room.type === "group" || room.type === "public") { - participantsList = REaCt().createElement("div", null, isReadOnlyElement, REaCt().createElement(buttons.$, { - className: "mega-button action invite-dialog-btn", - icon: "sprite-fm-mono icon-user-plus-thin-outline", - label: l[8726], - disabled: (0,call.P)() || room.isReadOnly() || !room.iAmOperator() && !room.publicLink && !room.options[MCO_FLAGS.OPEN_INVITE], - onClick: () => { - delay('chat-event-inv-rhp', () => eventlog(99963)); - if (room.type === 'group') { - return this.handleAddParticipants(); - } - loadingDialog.show('fetchchatlink'); - room.updatePublicHandle(false, false, true).catch(dump).always(() => { - loadingDialog.hide('fetchchatlink'); - if (!this.isMounted()) { - return; - } - if (!room.iAmOperator() && room.options[MCO_FLAGS.OPEN_INVITE] && !room.publicLink) { - this.handleAddParticipants(); - } else if (room.type === 'public' && !room.topic) { - this.handleAddParticipants(); - } else { - this.setState({ - inviteDialog: true - }); - } - }); - } - }), REaCt().createElement(ParticipantsList, { - ref (r) { - self.participantsListRef = r; - }, - chatRoom: room, - members: room.members, - isCurrentlyActive: room.isCurrentlyActive - })); - } - const addParticipantBtn = room.type === 'private' && REaCt().createElement(buttons.$, { - className: "link-button light", - icon: "sprite-fm-mono icon-add-small", - label: l[8007], - disabled: (0,call.P)() || room.isReadOnly() || !(room.iAmOperator() || room.type !== 'private' && room.options[MCO_FLAGS.OPEN_INVITE]), - onClick: () => Object.values(M.u.toJS()).some(u => u.c === 1) ? !allContactsInChat(exParticipants) ? this.setState({ - contactPickerDialog: true - }) : msgDialog(`confirmationa:!^${l[8726]}!${l.msg_dlg_cancel}`, null, `${l.all_contacts_added}`, `${l.all_contacts_added_to_chat}`, res => { - if (res) { - contactAddDialog(null, false); - } - }, 1) : msgDialog(`confirmationa:!^${l[8726]}!${l.msg_dlg_cancel}`, null, `${l.no_contacts}`, `${l.no_contacts_text}`, resp => { - if (resp) { - contactAddDialog(null, false); - } - }, 1) - }); - const waitingRoomButton = { - icon: 'icon-clock-user-thin-solid', - label: l.waiting_room, - secondLabel: l.waiting_room_info, - toggled: room.options[MCO_FLAGS.WAITING_ROOM], - disabled: room.havePendingCall(), - onClick: () => { - room.toggleWaitingRoom(); - delay('chat-event-wr-create-button', () => eventlog(99937)); - } - }; - const openInviteButton = { - icon: 'icon-user-filled', - label: room.isMeeting ? l.meeting_open_invite_label : l.chat_open_invite_label, - secondLabel: l.open_invite_desc, - toggled: room.options[MCO_FLAGS.OPEN_INVITE], - onClick: () => { - room.toggleOpenInvite(); - if (room.scheduledMeeting) { - delay('chat-event-sm-allow-non-hosts', () => eventlog(99928)); - } - } - }; - const retentionTime = room.retentionTime ? secondsToDays(room.retentionTime) : 0; - const ICON_ACTIVE = REaCt().createElement("i", { - className: "sprite-fm-mono icon-check" - }); - const retentionHistoryBtn = REaCt().createElement(buttons.$, { - className: "link-button light history-retention-btn", - icon: "sprite-fm-mono icon-recents-filled", - label: l[23436], - disabled: !room.iAmOperator() || room.isReadOnly() || (0,call.P)(), - secondLabel: room.getRetentionLabel(), - secondLabelClass: "label--red", - chatRoom: room - }, room.iAmOperator() ? REaCt().createElement(dropdowns.Dropdown, { - className: "retention-history-menu light", - noArrow: "false", - vertOffset: -53, - horizOffset: -205 - }, REaCt().createElement("div", { - className: "retention-history-menu__list" - }, REaCt().createElement("div", { - className: "dropdown-item link-button retention-history-menu__list__elem", - onClick: () => this.setRetention(room, 0) - }, REaCt().createElement("span", null, l.disabled_chat_history_cleaning_status), retentionTime === 0 && ICON_ACTIVE), REaCt().createElement("div", { - className: "dropdown-item link-button retention-history-menu__list__elem", - onClick: () => this.setRetention(room, daysToSeconds(1)) - }, REaCt().createElement("span", null, l[23437]), retentionTime === 1 && ICON_ACTIVE), REaCt().createElement("div", { - className: "dropdown-item link-button retention-history-menu__list__elem", - onClick: () => this.setRetention(room, daysToSeconds(7)) - }, REaCt().createElement("span", null, l[23438]), retentionTime === 7 && ICON_ACTIVE), REaCt().createElement("div", { - className: "dropdown-item link-button retention-history-menu__list__elem", - onClick: () => this.setRetention(room, daysToSeconds(30)) - }, REaCt().createElement("span", null, l[23439]), retentionTime === 30 && ICON_ACTIVE), REaCt().createElement("div", { - className: "dropdown-item link-button retention-history-menu__list__elem", - onClick: () => { - $(document).trigger('closeDropdowns'); - self.props.onHistoryRetentionConfig(); - } - }, REaCt().createElement("span", null, l[23440]), [0, 1, 7, 30].indexOf(retentionTime) === -1 && ICON_ACTIVE))) : null); - const MEMBERS_LIMITED = Object.keys(room.members).length > MAX_USERS_CHAT_PRIVATE; - const { - scheduledMeeting, - isMeeting - } = room; - const { - isRecurring, - isUpcoming, - occurrences - } = scheduledMeeting || {}; - let archiveText = room.isMeeting ? l.archive_meeting_btn : l.archive_chat_btn; - if (room.isArchived()) { - archiveText = room.isMeeting ? l.unarchive_meeting_btn : l[19065]; - } - return REaCt().createElement("div", { - ref: this.domRef, - className: "chat-right-area" - }, REaCt().createElement(perfectScrollbar.O, { - className: "chat-right-area conversation-details-scroll", - options: { - 'suppressScrollX': true - }, - ref: ref => { - this.rightScroll = ref; - }, - triggerGlobalResize: true, - isVisible: room.isCurrentlyActive, - chatRoom: room - }, REaCt().createElement("div", { - className: "chat-right-pad" - }, REaCt().createElement(Accordion, (0,esm_extends.A)({}, this.state, { - chatRoom: room, - onToggle: SoonFc(20, () => { - if (this.rightScroll) { - this.rightScroll.reinitialise(); - } - if (this.participantsListRef) { - let _this$participantsLis, _this$participantsLis2; - (_this$participantsLis = (_this$participantsLis2 = this.participantsListRef).safeForceUpdate) == null || _this$participantsLis.call(_this$participantsLis2); - } - }), - expandedPanel: { - participants: false, - options: false, - occurrences: isMeeting && scheduledMeeting && isRecurring - } - }), participantsList ? REaCt().createElement(AccordionPanel, { - className: "small-pad", - title: room.isMeeting ? l.meeting_participants : l.chat_participants, - chatRoom: room, - key: "participants" - }, participantsList) : null, room.type === 'public' && room.observers > 0 && !room.options.w ? REaCt().createElement("div", { - className: "accordion-text observers" - }, l[20466], REaCt().createElement("span", { - className: "observers-count" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-eye-reveal" - }), room.observers)) : REaCt().createElement("div", null), isRecurring && isUpcoming && scheduledMeeting.occurrences.some(o => o.isUpcoming) && REaCt().createElement(AccordionPanel, { - key: "occurrences", - className: "chat-occurrences-panel", - accordionClass: "chatroom-occurrences-panel", - title: l.occurrences_heading, - chatRoom: room, - scheduledMeeting, - occurrences - }, REaCt().createElement(Occurrences, { - chatRoom: room, - scheduledMeeting, - occurrences, - occurrencesLoading - })), REaCt().createElement(AccordionPanel, { - key: "options", - className: "have-animation buttons", - accordionClass: "chatroom-options-panel", - title: l[7537], - chatRoom: room, - sfuClient: window.sfuClient - }, REaCt().createElement(REaCt().Fragment, null, room.isNote ? null : REaCt().createElement(REaCt().Fragment, null, addParticipantBtn, startAudioCallButton, startVideoCallButton, REaCt().createElement(EndCallButton, { - call: room.havePendingGroupCall() || room.haveActiveCall(), - chatRoom: room - }), scheduledMeeting && REaCt().createElement("div", { - className: ` - link-button light - schedule-view-desc - ${room.isReadOnly() || !scheduledMeeting.description ? 'disabled' : ''} - `, - onClick: () => { - if (!room.isReadOnly() && scheduledMeeting.description) { - onShowScheduledDescription(); - } - } - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-description" - }), REaCt().createElement("span", null, l.schedule_view_desc)), (room.type === 'group' || room.type === 'public') && !scheduledMeeting ? REaCt().createElement("div", { - className: renameButtonClass, - onClick: e => { - if ($(e.target).closest('.disabled').length > 0) { - return false; - } - if (this.props.onRenameClicked) { - this.props.onRenameClicked(); - } - } - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-rename" - }), REaCt().createElement("span", null, room.isMeeting ? l.rename_meeting : l[9080])) : null, scheduledMeeting ? REaCt().createElement("div", { - className: ` - link-button - light - ${room.iAmOperator() ? '' : 'disabled'} - `, - onClick: () => room.iAmOperator() ? megaChat.trigger(megaChat.plugins.meetingsManager.EVENTS.EDIT, room) : null - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-rename" - }), scheduledMeeting.isRecurring ? REaCt().createElement("span", null, l.edit_meeting_series_button) : REaCt().createElement("span", null, l.edit_meeting_button)) : null, room.type === 'public' && !room.isMeeting ? REaCt().createElement("div", { - className: getChatLinkClass, - onClick: e => { - if ($(e.target).closest('.disabled').length > 0) { - return false; - } - this.props.onGetManageChatLinkClicked(); - } - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-link-filled" - }), REaCt().createElement("span", null, l[20481])) : null, scheduledMeeting ? REaCt().createElement("div", { - className: ` - link-button - light - ${room.iAmOperator() && !scheduledMeeting.canceled ? '' : 'disabled'} - `, - onClick: () => { - if (room.iAmOperator() && !scheduledMeeting.canceled) { - this.handleCancelMeeting(); - } - } - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-bin-filled" - }), scheduledMeeting.isRecurring ? REaCt().createElement("span", null, l.cancel_meeting_series_button) : REaCt().createElement("span", null, l.cancel_meeting_button)) : null, !room.membersSetFromApi.members.hasOwnProperty(u_handle) && room.type === 'public' && !is_chatlink && room.publicChatHandle && room.publicChatKey ? REaCt().createElement("div", { - className: "link-button light", - onClick: e => { - if ($(e.target).closest('.disabled').length > 0) { - return false; - } - this.props.onJoinViaPublicLinkClicked(); - } - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-rename" - }), REaCt().createElement("span", null, l[20597])) : null, scheduledMeeting ? null : REaCt().createElement(REaCt().Fragment, null, AVseperator, REaCt().createElement(buttons.$, { - className: "link-button light dropdown-element", - icon: "sprite-fm-mono icon-upload-filled", - label: l[23753], - disabled: room.isReadOnly() - }, REaCt().createElement(dropdowns.Dropdown, { - className: "wide-dropdown send-files-selector light", - noArrow: "true", - vertOffset: 4, - onClick: () => false - }, REaCt().createElement("div", { - className: "dropdown info-txt" - }, l[23753] || 'Send...'), REaCt().createElement(dropdowns.DropdownItem, { - className: "link-button", - icon: "sprite-fm-mono icon-cloud-drive", - label: l[19794] || 'My Cloud Drive', - disabled: mega.paywall, - onClick: () => { - this.props.onAttachFromCloudClicked(); - } - }), REaCt().createElement(dropdowns.DropdownItem, { - className: "link-button", - icon: "sprite-fm-mono icon-session-history", - label: l[19795] || 'My computer', - disabled: mega.paywall, - onClick: () => { - this.props.onAttachFromComputerClicked(); - } - })))), this.renderPushSettingsButton()), room.type === 'private' ? null : REaCt().createElement(REaCt().Fragment, null, room.scheduledMeeting && this.OptionsButton(waitingRoomButton), this.OptionsButton(openInviteButton), this.renderOptionsBanner(), AVseperator), REaCt().createElement(buttons.$, { - className: "link-button light export-chat-button", - disabled: ((_room$messagesBuff4 = room.messagesBuff) == null ? void 0 : _room$messagesBuff4.messages.length) === 0 || room.exportIo, - onClick: () => { - room.exportToFile(); - } - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-export-chat-filled" - }), REaCt().createElement("span", null, room.isMeeting ? l.export_meeting_rhp : l.export_chat_rhp)), REaCt().createElement(buttons.$, { - className: "link-button light clear-history-button", - disabled: dontShowTruncateButton || !room.members.hasOwnProperty(u_handle), - onClick: () => { - if (this.props.onTruncateClicked) { - this.props.onTruncateClicked(); - } - } - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-remove" - }), REaCt().createElement("span", { - className: "accordion-clear-history-text" - }, room.isMeeting ? l.meeting_clear_hist : l[8871])), retentionHistoryBtn, room.iAmOperator() && room.type === 'public' && !scheduledMeeting ? REaCt().createElement("div", { - className: "chat-enable-key-rotation-paragraph" - }, AVseperator, REaCt().createElement("div", { - className: ` - link-button - light - ${MEMBERS_LIMITED ? 'disabled' : ''} - `, - onClick: e => { - if (MEMBERS_LIMITED || $(e.target).closest('.disabled').length > 0) { - return false; - } - this.props.onMakePrivateClicked(); - } - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-key" - }), REaCt().createElement("span", null, l[20623])), REaCt().createElement("p", null, REaCt().createElement("span", null, l[20454]))) : null, AVseperator, REaCt().createElement("div", { - className: ` - link-button - light - ${(room.members.hasOwnProperty(u_handle) || room.state === ChatRoom.STATE.LEFT) && !is_chatlink ? '' : 'disabled'} - `, - onClick: e => { - if ($(e.target).closest('.disabled').length > 0) { - return false; - } - if (room.isArchived()) { - if (this.props.onUnarchiveClicked) { - this.props.onUnarchiveClicked(); - } - } else if (this.props.onArchiveClicked) { - this.props.onArchiveClicked(); - } - } - }, REaCt().createElement("i", { - className: ` - sprite-fm-mono - ${room.isArchived() ? 'icon-unarchive' : 'icon-archive'} - ` - }), REaCt().createElement("span", null, archiveText)), room.type === 'private' ? null : REaCt().createElement(this.LeaveButton, { - chatRoom: room, - participants: room.getParticipantsExceptMe(), - onLeave: () => room.leave(true) - }))), REaCt().createElement(SharedFilesAccordionPanel, { - key: "sharedFiles", - title: l[19796] || 'Shared Files', - chatRoom: room, - sharedFiles: (_room$messagesBuff5 = room.messagesBuff) == null ? void 0 : _room$messagesBuff5.sharedFiles - }), room.type === 'private' && !room.isNote ? REaCt().createElement(IncSharesAccordionPanel, { - key: "incomingShares", - title: l[5542], - chatRoom: room - }) : null))), this.state.contactPickerDialog && REaCt().createElement(ui_contacts.ContactPickerDialog, { - exclude: exParticipants, - megaChat: room.megaChat, - multiple: true, - className: "popup add-participant-selector", - singleSelectedButtonLabel: room.isMeeting ? l.meeting_add_participant : l[8869], - multipleSelectedButtonLabel: room.isMeeting ? l.meeting_add_participant : l[8869], - nothingSelectedButtonLabel: l[8870], - inviteWarningLabel: room.haveActiveCall(), - chatRoom: room, - onSelectDone: selected => { - this.props.onAddParticipantSelected(selected); - this.setState({ - contactPickerDialog: false - }); - }, - onClose: () => this.setState({ - contactPickerDialog: false - }), - selectFooter: true - }), this.state.inviteDialog && REaCt().createElement(modalDialogs.A.ModalDialog, { - onClose: () => { - this.setState({ - inviteDialog: false - }); - }, - dialogName: "chat-link-dialog", - chatRoom: room - }, REaCt().createElement(inviteParticipantsPanel.Q, { - chatRoom: room, - onAddParticipants: () => { - this.setState({ - inviteDialog: false - }, () => this.handleAddParticipants()); - } - }))); - } -} -ConversationRightArea.defaultProps = { - 'requiresUpdateOnResize': true -}; -const ConversationPanel = (conversationpanel_dec = utils.Ay.SoonFcWrap(360), _dec2 = (0,mixins.N9)(0.7, 9), conversationpanel_class = class ConversationPanel extends mixins.w9 { - constructor(props) { - super(props); - this.domRef = REaCt().createRef(); - this.messagesBlockRef = REaCt().createRef(); - this.$container = undefined; - this.$messages = undefined; - this.selectedNodes = []; - this.state = { - startCallPopupIsActive: false, - localVideoIsMinimized: false, - isFullscreenModeEnabled: false, - mouseOverDuringCall: false, - attachCloudDialog: false, - sendContactDialog: false, - confirmDeleteDialog: false, - pasteImageConfirmDialog: false, - nonLoggedInJoinChatDialog: false, - pushSettingsDialog: false, - pushSettingsValue: null, - messageToBeDeleted: null, - callMinimized: false, - editing: false, - showHistoryRetentionDialog: false, - setNonLoggedInJoinChatDlgTrue: null, - hasInvalidKeys: null, - invalidKeysBanner: null, - descriptionDialog: false, - occurrencesLoading: false, - waitingRoom: false, - callUserLimit: false, - historyTimeOutBanner: DISMISS_TRANSITIONS.NOT_SHOWN, - renameDialog: false, - renameDialogValue: undefined, - typingAreaText: '' - }; - this.RenameDialog = () => { - const { - chatRoom - } = this.props; - const { - renameDialogValue - } = this.state; - const isDisabled = renameDialogValue === chatRoom.getRoomTitle() || !$.trim(renameDialogValue).length; - const onSubmit = () => chatRoom.setRoomTopic(renameDialogValue).then(() => this.setState({ - renameDialog: false, - renameDialogValue: undefined - })).catch(dump); - return REaCt().createElement(modalDialogs.A.ModalDialog, { - chatRoom, - title: chatRoom.isMeeting ? l.rename_meeting : l[9080], - name: "rename-group", - className: "chat-rename-dialog dialog-template-main", - onClose: () => this.setState({ - renameDialog: false, - renameDialogValue: undefined - }), - buttons: [{ - label: l.msg_dlg_cancel, - onClick: () => this.setState({ - renameDialog: false, - renameDialogValue: undefined - }) - }, { - label: l[61], - className: ` - positive - ${isDisabled ? 'disabled' : ''} - `, - onClick: isDisabled ? null : onSubmit - }] - }, REaCt().createElement("section", { - className: "content" - }, REaCt().createElement("div", { - className: "content-block" - }, REaCt().createElement("div", { - className: "dialog secondary-header" - }, REaCt().createElement("div", { - className: "rename-input-bl" - }, REaCt().createElement("input", { - type: "text", - name: "newTopic", - className: "chat-rename-group-dialog", - value: renameDialogValue === undefined ? chatRoom.getRoomTitle() : renameDialogValue, - maxLength: ChatRoom.TOPIC_MAX_LENGTH, - onChange: ev => this.setState({ - renameDialogValue: ev.target.value.substr(0, 30) - }), - onKeyUp: ev => isDisabled ? null : ev.which === 13 && onSubmit() - })))))); - }; - this.CloudBrowserDialog = () => { - const { - chatRoom - } = this.props; - return REaCt().createElement(cloudBrowserModalDialog.CloudBrowserDialog, { - room: chatRoom, - allowAttachFolders: true, - onSelected: nodes => { - this.selectedNodes = nodes; - }, - onAttachClicked: () => { - this.setState({ - attachCloudDialog: false - }, () => { - chatRoom.scrolledToBottom = true; - chatRoom.attachNodes(this.selectedNodes).catch(dump); - }); - }, - onClose: () => { - this.setState({ - attachCloudDialog: false - }, () => { - this.selectedNodes = []; - }); - } - }); - }; - this.SelectContactDialog = () => { - const { - chatRoom - } = this.props; - const excludedContacts = chatRoom.getParticipantsExceptMe().filter(userHandle => userHandle in M.u); - return REaCt().createElement(modalDialogs.A.SelectContactDialog, { - chatRoom, - exclude: excludedContacts, - onSelectClicked: selected => this.setState({ - sendContactDialog: false - }, () => chatRoom.attachContacts(selected)), - onClose: () => this.setState({ - sendContactDialog: false - }) - }); - }; - this.DescriptionDialog = () => { - const { - chatRoom - } = this.props; - const dialogName = 'scheduled-description-dialog'; - return REaCt().createElement(modalDialogs.A.ModalDialog, { - className: "scheduled-description-dialog", - meeting: chatRoom.scheduledMeeting, - popupDidMount: () => M.safeShowDialog(dialogName, () => $(`.${dialogName}`)), - popupWillUnmount: () => $.dialog === dialogName && closeDialog(), - onClose: () => this.setState({ - descriptionDialog: false - }) - }, REaCt().createElement("header", null, REaCt().createElement("h3", null, l.schedule_desc_dlg_title)), REaCt().createElement("section", { - className: "content" - }, REaCt().createElement(perfectScrollbar.O, { - className: "description-scroller" - }, REaCt().createElement(utils.P9, { - content: megaChat.html(chatRoom.scheduledMeeting.description).replace(/\n/g, '
') || l.schedule_no_desc - })))); - }; - this.PushSettingsDialog = () => { - const { - chatRoom - } = this.props; - const { - pushSettingsValue - } = this.state; - const state = { - pushSettingsDialog: false, - pushSettingsValue: null - }; - return REaCt().createElement(PushSettingsDialog, { - room: chatRoom, - pushSettingsValue, - onClose: () => this.setState({ - ...state, - pushSettingsValue - }, () => $.dialog === 'push-settings-dialog' && closeDialog()), - onConfirm: pushSettingsValue => this.setState({ - ...state, - pushSettingsValue - }, () => pushNotificationSettings.setDnd(chatRoom.chatId, pushSettingsValue === Infinity ? 0 : unixtime() + pushSettingsValue * 60)) - }); - }; - this.updateTypingAreaText = value => { - this.setState({ - typingAreaText: value - }); - }; - const { - chatRoom: _chatRoom - } = this.props; - const uniqueId = this.getUniqueId(); - _chatRoom.rebind(`openAttachCloudDialog.${uniqueId}`, () => this.setState({ - attachCloudDialog: true - })); - _chatRoom.rebind(`openSendContactDialog.${uniqueId}`, () => this.setState({ - sendContactDialog: true - })); - _chatRoom.rebind(`openDescriptionDialog.${uniqueId}`, () => this.setState({ - descriptionDialog: true - })); - this.handleKeyDown = SoonFc(120, ev => this._handleKeyDown(ev)); - this.state.waitingRoom = _chatRoom.options.w && (_chatRoom.isAnonymous() || megaChat.initialChatId || is_eplusplus); - } - customIsEventuallyVisible() { - return this.props.chatRoom.isCurrentlyActive; - } - onMouseMove() { - if (this.isComponentEventuallyVisible()) { - this.props.chatRoom.trigger("onChatIsFocused"); - } - } - _handleKeyDown() { - if (this.__isMounted) { - const {chatRoom} = this.props; - if (chatRoom.isActive() && !chatRoom.isReadOnly()) { - chatRoom.trigger("onChatIsFocused"); - } - } - } - handleDeleteDialog(msg) { - if (msg) { - this.setState({ - editing: false, - confirmDeleteDialog: true, - messageToBeDeleted: msg - }); - } - } - toggleExpandedFlag() { - if (this.props.onToggleExpandedFlag) { - this.props.onToggleExpandedFlag(); - } - return document.body.classList[call.Ay.isExpanded() ? 'remove' : 'add'](call.Fj); - } - startCall(type, scheduled) { - const { - chatRoom - } = this.props; - if (isStartCallDisabled(chatRoom) || chatRoom.iAmWaitingRoomPeer()) { - return false; - } - return type === call.ZE.AUDIO ? chatRoom.startAudioCall(scheduled) : chatRoom.startVideoCall(scheduled); - } - renderUpcomingInfo() { - const { - scheduledMeeting - } = this.props.chatRoom; - if (scheduledMeeting) { - const { - recurring, - nextOccurrenceStart, - nextOccurrenceEnd, - isUpcoming - } = scheduledMeeting; - const until = `${(0,helpers.ro)(nextOccurrenceStart, nextOccurrenceEnd) ? '' : time2date(nextOccurrenceEnd / 1000, 4)} ${toLocaleTime(nextOccurrenceEnd)}`; - return REaCt().createElement(REaCt().Fragment, null, isUpcoming && recurring && REaCt().createElement("span", null, l.next_meeting), REaCt().createElement("span", null, (l.schedule_formatted_date || '%1 from %2 to %3').replace('%1', time2date(nextOccurrenceStart / 1000, 4)).replace('%2', toLocaleTime(nextOccurrenceStart)).replace('%3', until))); - } - return null; - } - componentDidMount() { - super.componentDidMount(); - const { - chatRoom - } = this.props; - this.$container = $('.conversation-panel', '#fmholder'); - this.$messages = $('.messages.scroll-area > .perfectScrollbarContainer', this.$container); - window.addEventListener('keydown', this.handleKeyDown); - chatRoom.rebind('onSendMessage.scrollToBottom', () => { - chatRoom.scrolledToBottom = true; - if (this.messagesListScrollable) { - this.messagesListScrollable.scrollToBottom(); - } - }); - chatRoom.rebind('openSendFilesDialog.cpanel', () => this.setState({ - attachCloudDialog: true - })); - chatRoom.rebind('showGetChatLinkDialog.ui', () => { - createTimeoutPromise(() => chatRoom.topic && chatRoom.state === ChatRoom.STATE.READY, 350, 15000).always(() => { - return chatRoom.isCurrentlyActive ? this.setState({ - chatLinkDialog: true - }) : chatRoom.updatePublicHandle(false, true); - }); - }); - if (chatRoom.type === 'private') { - const otherContactHash = chatRoom.getParticipantsExceptMe()[0]; - if (otherContactHash in M.u) { - this._privateChangeListener = M.u[otherContactHash].addChangeListener(() => { - if (!this.isMounted()) { - return 0xDEAD; - } - this.safeForceUpdate(); - }); - } - } - if (is_chatlink && !chatRoom.isMeeting) { - this.state.setNonLoggedInJoinChatDlgTrue = setTimeout(() => { - M.safeShowDialog('chat-links-preview-desktop', () => { - if (this.isMounted()) { - this.setState({ - nonLoggedInJoinChatDialog: true - }); - } - }); - }, rand_range(5, 10) * 1000); - } - if (is_chatlink && chatRoom.isMeeting && u_type !== false && u_type < 3) { - eventlog(99747, JSON.stringify([1, u_type | 0]), true); - } - chatRoom._uiIsMounted = true; - chatRoom.$rConversationPanel = this; - onIdle(() => this.isMounted() && chatRoom.trigger('onComponentDidMount')); - ChatdIntegration._waitForProtocolHandler(chatRoom, () => { - if (this.isMounted()) { - const hasInvalidKeys = chatRoom.hasInvalidKeys(); - this.setState({ - hasInvalidKeys, - invalidKeysBanner: hasInvalidKeys - }, () => this.safeForceUpdate()); - } - }); - megaChat.rebind(`${megaChat.plugins.meetingsManager.EVENTS.OCCURRENCES_UPDATE}.${this.getUniqueId()}`, () => { - return this.isMounted() && this.setState({ - occurrencesLoading: false - }); - }); - chatRoom.rebind(`wrOnJoinNotAllowed.${this.getUniqueId()}`, () => { - return this.isMounted() && this.setState({ - waitingRoom: true - }); - }); - chatRoom.rebind(`wrOnJoinAllowed.${this.getUniqueId()}`, () => { - return this.isMounted() && this.setState({ - waitingRoom: false - }); - }); - chatRoom.rebind(`onCallUserLimitExceeded.${this.getUniqueId()}`, () => { - if (!this.isMounted()) { - return; - } - if (megaChat.initialChatId || is_eplusplus) { - this.setState({ - callUserLimit: true - }); - } - }); - chatRoom.rebind(`onHistTimeoutChange.${this.getUniqueId()}`, () => { - if (this.state.historyTimeOutBanner === DISMISS_TRANSITIONS.NOT_SHOWN && chatRoom.historyTimedOut) { - this.setState({ - historyTimeOutBanner: DISMISS_TRANSITIONS.SHOWN - }); - } else if (this.state.historyTimeOutBanner && !chatRoom.historyTimedOut) { - this.setState({ - historyTimeOutBanner: DISMISS_TRANSITIONS.NOT_SHOWN - }); - } - }); - if (chatRoom.options.w) { - chatRoom.rebind(`onMembersUpdated.${this.getUniqueId()}`, (ev, { - userId, - priv - }) => { - if (userId === u_handle && priv !== ChatRoom.MembersSet.PRIVILEGE_STATE.LEFT) { - chatRoom.unbind(`onMembersUpdated.${this.getUniqueId()}`); - if (is_chatlink) { - return megaChat.routing.reinitAndOpenExistingChat(chatRoom.chatId, chatRoom.publicChatHandle).then(chatRoom => chatRoom.havePendingCall() && priv === ChatRoom.MembersSet.PRIVILEGE_STATE.OPERATOR && chatRoom.joinCall()).catch(dump); - } - return this.state.waitingRoom && this.setState({ - waitingRoom: priv !== ChatRoom.MembersSet.PRIVILEGE_STATE.OPERATOR - }); - } - }); - } - this.pageChangeListener = mBroadcaster.addListener('beforepagechange', () => M.chat && this.state.waitingRoom && this.setState({ - waitingRoom: false - }, () => this.safeForceUpdate())); - } - componentWillUnmount() { - super.componentWillUnmount(); - const self = this; - const {chatRoom} = self.props; - chatRoom._uiIsMounted = true; - if (this._privateChangeListener) { - const otherContactHash = self.props.chatRoom.getParticipantsExceptMe()[0]; - if (otherContactHash in M.u) { - M.u[otherContactHash].removeChangeListener(this._privateChangeListener); - delete this._privateChangeListener; - } - } - mBroadcaster.removeListener(this.pageChangeListener); - this.props.chatRoom.unbind(`openAttachCloudDialog.${this.getUniqueId()}`); - this.props.chatRoom.unbind(`openSendContactDialog.${this.getUniqueId()}`); - this.props.chatRoom.unbind(`openDescriptionDialog.${this.getUniqueId()}`); - window.removeEventListener('keydown', self.handleKeyDown); - $(document).off(`fullscreenchange.megaChat_${chatRoom.roomId}`); - $(document).off(`keydown.keyboardScroll_${chatRoom.roomId}`); - this.props.chatRoom.unbind(`wrOnJoinNotAllowed.${this.getUniqueId()}`); - this.props.chatRoom.unbind(`wrOnJoinAllowed.${this.getUniqueId()}`); - megaChat.unbind(`onIncomingCall.${this.getUniqueId()}`); - this.props.chatRoom.unbind(`onHistTimeoutChange.${this.getUniqueId()}`); - } - componentDidUpdate(prevProps, prevState) { - const self = this; - const room = this.props.chatRoom; - room.megaChat.updateSectionUnreadCount(); - if (prevProps.isActive === false && self.props.isActive === true) { - const $typeArea = $('.messages-textarea:visible:first', this.$container); - if ($typeArea.length === 1) { - $typeArea.trigger("focus"); - moveCursortoToEnd($typeArea[0]); - } - } - if (!prevState.renameDialog && self.state.renameDialog === true) { - Soon(() => { - const $input = $('.chat-rename-dialog input'); - if ($input && $input[0] && !$($input[0]).is(":focus")) { - $input.trigger("focus"); - $input[0].selectionStart = 0; - $input[0].selectionEnd = $input.val().length; - } - }); - } - if (self.$messages && self.isComponentEventuallyVisible()) { - $(window).rebind('pastedimage.chatRoom', (e, blob, fileName) => { - if (self.$messages && self.isComponentEventuallyVisible()) { - self.setState({ - 'pasteImageConfirmDialog': [blob, fileName, URL.createObjectURL(blob)] - }); - e.preventDefault(); - } - }); - self.props.chatRoom.trigger("onComponentDidUpdate"); - } - } - isActive() { - return document.hasFocus() && this.$messages && this.$messages.is(":visible"); - } - render() { - const self = this; - const room = this.props.chatRoom; - if (!room || !room.roomId) { - return null; - } - const contacts = room.getParticipantsExceptMe(); - let contactHandle; - let contact; - let nonLoggedInJoinChatDialog = null; - if (self.state.nonLoggedInJoinChatDialog === true) { - const usersCount = Object.keys(room.members).length; - const closeJoinDialog = () => { - onIdle(() => { - if ($.dialog === 'chat-links-preview-desktop') { - closeDialog(); - } - }); - self.setState({ - 'nonLoggedInJoinChatDialog': false - }); - }; - nonLoggedInJoinChatDialog = REaCt().createElement(modalDialogs.A.ModalDialog, { - title: l[20596], - className: "mega-dialog chat-links-preview-desktop dialog-template-graphic", - chatRoom: room, - onClose: closeJoinDialog - }, REaCt().createElement("section", { - className: "content" - }, REaCt().createElement("div", { - className: "chatlink-contents" - }, REaCt().createElement("div", { - className: "huge-icon group-chat" - }), REaCt().createElement("h3", null, REaCt().createElement(utils.zT, null, room.getRoomTitle())), REaCt().createElement("h5", null, usersCount ? mega.icu.format(l[20233], usersCount) : ''), REaCt().createElement("p", null, l[20595]))), REaCt().createElement("footer", null, REaCt().createElement("div", { - className: "bottom-buttons" - }, REaCt().createElement("button", { - className: "mega-button positive", - onClick: () => { - closeJoinDialog(); - megaChat.loginOrRegisterBeforeJoining(room.publicChatHandle, false, false, false, () => { - megaChat.routing.reinitAndJoinPublicChat(room.chatId, room.publicChatHandle, room.publicChatKey).then(() => { - delete megaChat.initialPubChatHandle; - }, ex => { - console.error("Failed to join room:", ex); - }); - }); - } - }, l[20597]), REaCt().createElement("button", { - className: "mega-button", - onClick: closeJoinDialog - }, l[18682])))); - } - let privateChatDialog; - if (self.state.privateChatDialog === true) { - const onClose = () => this.setState({ - privateChatDialog: false - }); - privateChatDialog = REaCt().createElement(modalDialogs.A.ModalDialog, { - title: l[20594], - className: "mega-dialog create-private-chat", - chatRoom: room, - onClose, - dialogType: "action", - dialogName: "create-private-chat-dialog" - }, REaCt().createElement("section", { - className: "content" - }, REaCt().createElement("div", { - className: "content-block" - }, REaCt().createElement("i", { - className: "huge-icon lock" - }), REaCt().createElement("div", { - className: "dialog-body-text" - }, REaCt().createElement("strong", null, l[20590]), REaCt().createElement("br", null), REaCt().createElement("span", null, l[20591])))), REaCt().createElement("footer", null, REaCt().createElement("div", { - className: "footer-container" - }, REaCt().createElement("button", { - className: "mega-button positive large", - onClick: () => { - this.props.chatRoom.switchOffPublicMode(); - onClose(); - } - }, REaCt().createElement("span", null, l[20593]))))); - } - let confirmDeleteDialog = null; - if (self.state.confirmDeleteDialog === true) { - confirmDeleteDialog = REaCt().createElement(modalDialogs.A.ConfirmDialog, { - chatRoom: room, - dialogType: "main", - title: l[8004], - subtitle: l[8879], - name: "delete-message", - pref: "1", - onClose: () => { - self.setState({ - 'confirmDeleteDialog': false - }); - }, - onConfirmClicked: () => { - const msg = self.state.messageToBeDeleted; - if (!msg) { - return; - } - const chatdint = room.megaChat.plugins.chatdIntegration; - if (msg.getState() === Message.STATE.SENT || msg.getState() === Message.STATE.DELIVERED || msg.getState() === Message.STATE.NOT_SENT) { - const textContents = msg.textContents || ''; - if (textContents[1] === Message.MANAGEMENT_MESSAGE_TYPES.VOICE_CLIP) { - const attachmentMetadata = msg.getAttachmentMeta() || []; - Promise.all(attachmentMetadata.map(v => M.moveToRubbish(v.h))).catch(dump); - } - chatdint.deleteMessage(room, msg.internalId ? msg.internalId : msg.orderValue); - msg.deleted = true; - msg.textContents = ""; - } else if (msg.getState() === Message.STATE.NOT_SENT_EXPIRED) { - chatdint.discardMessage(room, msg.internalId ? msg.internalId : msg.orderValue); - } - self.setState({ - 'confirmDeleteDialog': false, - 'messageToBeDeleted': false - }); - if (msg.getState && msg.getState() === Message.STATE.NOT_SENT && !msg.requiresManualRetry) { - msg.message = ""; - msg.textContents = ""; - msg.messageHtml = ""; - msg.deleted = true; - msg.trigger('onChange', [msg, "deleted", false, true]); - } - } - }, REaCt().createElement("section", { - className: "content" - }, REaCt().createElement("div", { - className: "content-block" - }, REaCt().createElement(generic.A, { - className: " dialog-wrapper", - message: self.state.messageToBeDeleted, - hideActionButtons: true, - initTextScrolling: true, - dialog: true, - chatRoom: self.props.chatRoom - })))); - } - if (self.state.pasteImageConfirmDialog) { - confirmDeleteDialog = REaCt().createElement(modalDialogs.A.ConfirmDialog, { - chatRoom: room, - title: l[20905], - subtitle: l[20906], - icon: "sprite-fm-uni icon-question", - name: "paste-image-chat", - pref: "2", - onClose: () => { - self.setState({ - 'pasteImageConfirmDialog': false - }); - }, - onConfirmClicked: () => { - const meta = self.state.pasteImageConfirmDialog; - if (!meta) { - return; - } - try { - Object.defineProperty(meta[0], 'name', { - configurable: true, - writeable: true, - value: `${Date.now() }.${ M.getSafeName(meta[1] || meta[0].name)}` - }); - } catch (e) {} - self.props.chatRoom.scrolledToBottom = true; - M.addUpload([meta[0]]); - self.setState({ - 'pasteImageConfirmDialog': false - }); - URL.revokeObjectURL(meta[2]); - } - }, REaCt().createElement("img", { - src: self.state.pasteImageConfirmDialog[2], - style: { - maxWidth: "90%", - height: "auto", - maxHeight: $(document).outerHeight() * 0.3, - margin: '10px auto', - display: 'block', - border: '1px solid #ccc', - borderRadius: '4px' - }, - onLoad (e) { - $(e.target).parents('.paste-image-chat').position({ - of: $(document.body) - }); - } - })); - } - if (self.state.truncateDialog === true) { - confirmDeleteDialog = REaCt().createElement(modalDialogs.A.ConfirmDialog, { - chatRoom: room, - title: room.isMeeting ? l.meeting_clear_hist : l[8871], - subtitle: room.isMeeting ? l.meeting_trunc_txt : l[8881], - icon: "sprite-fm-uni icon-question", - name: "truncate-conversation", - pref: "3", - dontShowAgainCheckbox: false, - onClose: () => { - self.setState({ - 'truncateDialog': false - }); - }, - onConfirmClicked: () => { - self.props.chatRoom.scrolledToBottom = true; - room.truncate(); - self.setState({ - 'truncateDialog': false - }); - } - }); - } - if (self.state.archiveDialog === true) { - confirmDeleteDialog = REaCt().createElement(modalDialogs.A.ConfirmDialog, { - chatRoom: room, - title: room.isMeeting ? l.meeting_archive_dlg : l[19068], - subtitle: room.isMeeting ? l.meeting_archive_dlg_text : l[19069], - icon: "sprite-fm-uni icon-question", - name: "archive-conversation-dialog", - pref: "4", - onClose: () => { - self.setState({ - 'archiveDialog': false - }); - }, - onConfirmClicked: () => { - self.props.chatRoom.scrolledToBottom = true; - room.archive(); - self.setState({ - 'archiveDialog': false - }); - } - }); - } - if (self.state.unarchiveDialog === true) { - confirmDeleteDialog = REaCt().createElement(modalDialogs.A.ConfirmDialog, { - chatRoom: room, - title: room.isMeeting ? l.meeting_unarchive_dlg : l[19063], - subtitle: room.isMeeting ? l.meeting_unarchive_dlg_text : l[19064], - icon: "sprite-fm-uni icon-question", - name: "unarchive-conversation-dialog", - pref: "5", - onClose: () => { - self.setState({ - 'unarchiveDialog': false - }); - }, - onConfirmClicked: () => { - self.props.chatRoom.scrolledToBottom = true; - room.unarchive(); - self.setState({ - 'unarchiveDialog': false - }); - } - }); - } - let topicInfo = null; - const isUpcoming = room.scheduledMeeting && room.scheduledMeeting.isUpcoming; - const isRecurring = room.scheduledMeeting && room.scheduledMeeting.isRecurring; - if (room.type === 'group' || room.type === 'public') { - topicInfo = REaCt().createElement("div", { - className: "chat-topic-info" - }, REaCt().createElement("div", { - className: ` - chat-topic-icon - ${room.isMeeting ? 'meeting-icon' : ''} - ` - }, REaCt().createElement("i", { - className: room.isMeeting ? 'sprite-fm-mono icon-video-call-filled' : 'sprite-fm-uni icon-chat-group' - })), REaCt().createElement("div", { - className: "chat-topic-text" - }, REaCt().createElement("span", { - className: "txt" - }, REaCt().createElement(utils.zT, null, room.getRoomTitle()), isUpcoming && isRecurring && REaCt().createElement("i", { - className: "sprite-fm-mono recurring-meeting icon-repeat-thin-solid" - })), REaCt().createElement("span", { - className: "txt small" - }, is_chatlink && isUpcoming && !isRecurring ? this.renderUpcomingInfo() : REaCt().createElement(ui_contacts.MembersAmount, { - chatRoom: room - })))); - } else { - contactHandle = contacts[0]; - contact = M.u[contactHandle || u_handle]; - topicInfo = megaChat.WITH_SELF_NOTE && room.isNote ? REaCt().createElement("div", { - className: "note-chat-topic" - }, REaCt().createElement("div", { - className: "note-chat-signifier" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-file-text-thin-outline note-chat-icon" - })), REaCt().createElement("span", { - className: "note-chat-label" - }, l.note_label)) : REaCt().createElement(ui_contacts.ContactCard, { - key: contact.u, - className: "short", - chatRoom: room, - contact, - noContextButton: true, - showLastGreen: true - }); - } - let historyRetentionDialog = null; - if (self.state.showHistoryRetentionDialog === true) { - historyRetentionDialog = REaCt().createElement(HistoryRetentionDialog, { - chatRoom: room, - title: '', - name: "rename-group", - className: "", - onClose: () => { - self.setState({ - showHistoryRetentionDialog: false - }); - } - }); - } - if (this.state.waitingRoom) { - return REaCt().createElement(WaitingRoom, { - chatRoom: room, - havePendingCall: room.havePendingCall(), - onWaitingRoomLeave: () => { - let _room$call; - (_room$call = room.call) == null || _room$call.destroy(); - if (is_eplusplus) { - room.leave(true); - return onIdle(M.logout); - } - return this.setState({ - waitingRoom: false - }, () => { - onIdle(() => { - if (megaChat.initialChatId) { - megaChat.initialChatId = undefined; - loadSubPage(getLandingPage()); - } - }); - }); - } - }); - } - if (this.state.callUserLimit) { - return REaCt().createElement(ChatOverlay, { - overlayType: ChatOverlays.PARTICIPANT_LIMIT, - onClose: () => { - if (is_eplusplus) { - location.replace('https://mega.io'); - } else { - this.setState({ - callUserLimit: false - }); - } - } - }); - } - const startCallDisabled = isStartCallDisabled(room) || room.iAmWaitingRoomPeer(); - return REaCt().createElement("div", { - ref: this.domRef, - className: ` - conversation-panel - ${room.type === 'public' ? 'group-chat ' : ''} - ${room.type}-chat - ${!room.isCurrentlyActive || megaChat._joinDialogIsShown ? 'hidden' : ''} - `, - onMouseMove: () => self.onMouseMove(), - "data-room-id": self.props.chatRoom.chatId - }, room.meetingsLoading && REaCt().createElement(Loading, { - chatRoom: room, - title: room.meetingsLoading.title - }), room.call && REaCt().createElement(call.Ay, { - chatRoom: room, - peers: room.call.peers, - call: room.call, - minimized: this.state.callMinimized, - typingAreaText: this.state.typingAreaText, - onCallMinimize: () => { - return this.state.callMinimized ? null : this.setState({ - callMinimized: true - }, () => { - this.toggleExpandedFlag(); - this.safeForceUpdate(); - }); - }, - onCallExpand: () => { - return this.state.callMinimized && this.setState({ - callMinimized: false - }, () => { - $.hideTopMenu(); - if ($.dialog) { - closeDialog(); - } - loadSubPage('fm/chat'); - room.show(); - this.toggleExpandedFlag(); - }); - }, - didMount: () => { - this.toggleExpandedFlag(); - if (room.isMeeting) { - room.updatePublicHandle().catch(dump); - } - }, - willUnmount: minimised => this.setState({ - callMinimized: false - }, () => minimised ? null : this.toggleExpandedFlag()), - onCallEnd: () => this.safeForceUpdate(), - onDeleteMessage: msg => this.handleDeleteDialog(msg), - onTypingAreaChanged: this.updateTypingAreaText, - parent: this - }), megaChat.initialPubChatHandle && room.publicChatHandle === megaChat.initialPubChatHandle && !room.call && room.isMeeting && !room.call && room.activeCallIds.length > 0 && REaCt().createElement(Join, { - initialView: u_type || is_eplusplus ? Join.VIEW.ACCOUNT : Join.VIEW.INITIAL, - chatRoom: room, - onJoinGuestClick: (firstName, lastName, audioFlag, videoFlag) => { - room.meetingsLoading = l.joining; - u_eplusplus(firstName, lastName).then(() => { - return megaChat.routing.reinitAndJoinPublicChat(room.chatId, room.publicChatHandle, room.publicChatKey); - }).then(() => { - delete megaChat.initialPubChatHandle; - return megaChat.getChatById(room.chatId).joinCall(audioFlag, videoFlag); - }).catch(ex => { - if (d) { - console.error('E++ account failure!', ex); - } - setTimeout(() => { - msgDialog('warninga', l[135], l.eplusplus_create_failed, escapeHTML(api_strerror(ex) || ex)); - }, 1234); - eventlog(99745, JSON.stringify([1, String(ex).split('\n')[0]])); - }); - }, - onJoinClick: (audioFlag, videoFlag) => { - const {chatId} = room; - if (room.members[u_handle]) { - delete megaChat.initialPubChatHandle; - megaChat.routing.reinitAndOpenExistingChat(chatId, room.publicChatHandle).then(() => { - return megaChat.getChatById(chatId).joinCall(audioFlag, videoFlag); - }).catch(ex => { - console.error("Failed to open existing room and join call:", ex); - }); - } else { - megaChat.routing.reinitAndJoinPublicChat(chatId, room.publicChatHandle, room.publicChatKey).then(() => { - delete megaChat.initialPubChatHandle; - return megaChat.getChatById(chatId).joinCall(audioFlag, videoFlag); - }).catch(ex => { - console.error("Failed to join room:", ex); - }); - } - } - }), REaCt().createElement("div", { - className: ` - chat-content-block - ${room.megaChat.chatUIFlags.convPanelCollapse ? 'no-pane' : 'with-pane'} - ` - }, room.megaChat.chatUIFlags.convPanelCollapse ? null : REaCt().createElement(ConversationRightArea, { - isVisible: this.props.chatRoom.isCurrentlyActive, - chatRoom: this.props.chatRoom, - roomFlags: this.props.chatRoom.flags, - members: this.props.chatRoom.membersSetFromApi, - messagesBuff: room.messagesBuff, - pushSettingsValue: pushNotificationSettings.getDnd(this.props.chatRoom.chatId), - occurrencesLoading: this.state.occurrencesLoading, - onStartCall: mode => (0,call.dQ)(room.haveActiveCall(), room).then(() => this.startCall(mode)).catch(() => d && console.warn('Already in a call.')), - onAttachFromComputerClicked: () => this.props.chatRoom.uploadFromComputer(), - onTruncateClicked: () => this.setState({ - truncateDialog: true - }), - onArchiveClicked: () => this.setState({ - archiveDialog: true - }), - onUnarchiveClicked: () => this.setState({ - unarchiveDialog: true - }), - onRenameClicked: () => { - this.setState({ - renameDialog: true, - renameDialogValue: this.props.chatRoom.getRoomTitle() - }); - }, - onGetManageChatLinkClicked: () => this.setState({ - chatLinkDialog: true - }), - onMakePrivateClicked: () => this.setState({ - privateChatDialog: true - }), - onCloseClicked: () => room.destroy(), - onJoinViaPublicLinkClicked: () => room.joinViaPublicHandle(), - onSwitchOffPublicMode: topic => room.switchOffPublicMode(topic), - onAttachFromCloudClicked: () => this.setState({ - attachCloudDialog: true - }), - onPushSettingsClicked: () => M.safeShowDialog('push-settings-dialog', () => this.setState({ - pushSettingsDialog: true - })), - onPushSettingsToggled: () => { - return room.dnd || room.dnd === 0 ? this.setState({ - pushSettingsValue: null - }, () => pushNotificationSettings.disableDnd(room.chatId)) : pushNotificationSettings.setDnd(room.chatId, 0); - }, - onHistoryRetentionConfig: () => this.setState({ - showHistoryRetentionDialog: true - }), - onAddParticipantSelected: contactHashes => { - room.scrolledToBottom = true; - if (room.type === 'group' || room.type === 'public') { - if (room.options.w && room.call) { - let _room$call$sfuClient; - (_room$call$sfuClient = room.call.sfuClient) == null || _room$call$sfuClient.wrAllowJoin(contactHashes); - } - return room.trigger('onAddUserRequest', [contactHashes]); - } - loadingDialog.show(); - megaChat.trigger('onNewGroupChatRequest', [[...room.getParticipantsExceptMe(), ...contactHashes], { - keyRotation: false, - topic: '' - }]); - }, - onShowScheduledDescription: room.scheduledMeeting ? () => this.setState({ - descriptionDialog: true - }) : null - }), this.state.attachCloudDialog && REaCt().createElement(this.CloudBrowserDialog, null), this.state.sendContactDialog && REaCt().createElement(this.SelectContactDialog, null), this.state.descriptionDialog && REaCt().createElement(this.DescriptionDialog, null), this.state.pushSettingsDialog && REaCt().createElement(this.PushSettingsDialog, null), privateChatDialog, nonLoggedInJoinChatDialog, confirmDeleteDialog, historyRetentionDialog, null, this.state.renameDialog && REaCt().createElement(this.RenameDialog, null), this.state.chatLinkDialog && REaCt().createElement(ChatlinkDialog, { - chatRoom: this.props.chatRoom, - onClose: () => this.setState({ - chatLinkDialog: false - }) - }), REaCt().createElement("div", { - className: ` - chat-topic-block - ${room.isNote ? 'is-note' : ''} - ` - }, REaCt().createElement("div", { - className: "chat-topic-buttons" - }, room.type === 'public' && room.isMeeting && REaCt().createElement(buttons.$, { - className: "mega-button small share-meeting-button", - label: l.share_meeting_button, - onClick: () => this.setState({ - chatLinkDialog: true - }, () => eventlog(500230)) - }), REaCt().createElement(buttons.$, { - className: "right", - disableCheckingVisibility: true, - icon: "sprite-fm-mono icon-info-filled", - onClick: () => room.megaChat.toggleUIFlag('convPanelCollapse') - }), room.isNote ? null : REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("div", { - "data-simpletip": l.unsupported_browser_video, - "data-simpletipposition": "top", - "data-simpletipoffset": "5", - className: ` - ${!megaChat.hasSupportForCalls ? 'simpletip' : ''} - right - ${startCallDisabled ? 'disabled' : ''} - ` - }, REaCt().createElement(buttons.$, { - icon: "sprite-fm-mono icon-video-call-filled", - onClick: () => startCallDisabled ? false : (0,call.dQ)(room.haveActiveCall(), room).then(() => this.startCall(call.ZE.VIDEO)).catch(() => d && console.warn('Already in a call.')).then(() => room.isMeeting ? eventlog(500289) : eventlog(500290)) - })), REaCt().createElement("div", { - "data-simpletip": l.unsupported_browser_audio, - "data-simpletipposition": "top", - "data-simpletipoffset": "5", - className: ` - ${!megaChat.hasSupportForCalls ? 'simpletip' : ''} - right - ${startCallDisabled ? 'disabled' : ''} - ` - }, REaCt().createElement(buttons.$, { - icon: "sprite-fm-mono icon-phone", - onClick: () => startCallDisabled ? false : (0,call.dQ)(room.haveActiveCall(), room).then(() => this.startCall(call.ZE.AUDIO)).catch(() => d && console.warn('Already in a call.')).then(() => room.isMeeting ? eventlog(500291) : eventlog(500292)) - })))), topicInfo), REaCt().createElement("div", { - ref: this.messagesBlockRef, - className: ` - messages-block - ${""} - ` - }, this.state.hasInvalidKeys && this.state.invalidKeysBanner && REaCt().createElement(Alert, { - type: Alert.TYPE.HIGH, - className: ` - ${megaChat.chatUIFlags.convPanelCollapse ? 'full-span' : ''} - ${this.props.offset === ALERTS_BASE_OFFSET ? 'single-alert' : ''} - `, - offset: this.props.offset === ALERTS_BASE_OFFSET ? 0 : this.props.offset, - content: REaCt().createElement(REaCt().Fragment, null, l.chat_key_failed_banner.split('[A]')[0], REaCt().createElement("a", { - onClick: () => M.reload() - }, l.chat_key_failed_banner.substring(l.chat_key_failed_banner.indexOf('[A]') + 3, l.chat_key_failed_banner.indexOf('[/A]'))), l.chat_key_failed_banner.split('[/A]')[1]), - onClose: () => this.setState({ - invalidKeysBanner: false - }) - }), this.state.historyTimeOutBanner === DISMISS_TRANSITIONS.SHOWN && REaCt().createElement(Alert, { - type: Alert.TYPE.ERROR, - className: ` - ${megaChat.chatUIFlags.convPanelCollapse ? 'full-span' : ''} - ${this.props.offset === ALERTS_BASE_OFFSET ? 'single_alert' : ''} - history-timeout-banner - `, - offset: this.props.offset === ALERTS_BASE_OFFSET ? 0 : this.props.offset, - content: REaCt().createElement(REaCt().Fragment, null, l.chat_timeout_banner, REaCt().createElement("a", { - onClick: () => location.reload() - }, l[85])), - onClose: () => this.setState({ - historyTimeOutBanner: DISMISS_TRANSITIONS.DISMISSED - }) - }), REaCt().createElement(historyPanel.A, (0,esm_extends.A)({}, this.props, { - onMessagesListScrollableMount: mls => { - this.messagesListScrollable = mls; - }, - ref: historyPanel => { - this.historyPanel = historyPanel; - }, - onDeleteClicked: msg => this.handleDeleteDialog(msg) - })), !is_chatlink && room.state !== ChatRoom.STATE.LEFT && navigator.onLine && room.scheduledMeeting && !room.isArchived() && !this.state.hasInvalidKeys && !isStartCallDisabled(room) ? REaCt().createElement(StartMeetingNotification, { - chatRoom: room, - offset: this.props.offset, - onWaitingRoomJoin: () => this.setState({ - waitingRoom: true - }), - onStartCall: mode => { - return isStartCallDisabled(room) ? null : (0,call.dQ)(true, room).then(() => this.startCall(mode, true)).catch(ex => d && console.warn(`Already in a call. ${ex}`)); - } - }) : null, !is_chatlink && room.state !== ChatRoom.STATE.LEFT && (room.havePendingGroupCall() || room.havePendingCall()) && !this.state.hasInvalidKeys && navigator.onLine ? REaCt().createElement(JoinCallNotification, { - rhpCollapsed: megaChat.chatUIFlags.convPanelCollapse, - chatRoom: room, - offset: this.props.offset - }) : null, room.isAnonymous() ? REaCt().createElement("div", { - className: "join-chat-block" - }, REaCt().createElement("div", { - className: "mega-button large positive", - onClick: () => { - const join = () => { - megaChat.routing.reinitAndJoinPublicChat(room.chatId, room.publicChatHandle, room.publicChatKey).then(() => delete megaChat.initialPubChatHandle, ex => console.error("Failed to join room:", ex)); - }; - if (u_type === 0) { - return loadSubPage('register'); - } - if (u_type === false) { - clearTimeout(self.state.setNonLoggedInJoinChatDlgTrue); - megaChat.loginOrRegisterBeforeJoining(room.publicChatHandle, false, false, false, join); - return; - } - clearTimeout(self.state.setNonLoggedInJoinChatDlgTrue); - join(); - } - }, l[20597])) : REaCt().createElement(composedTextArea.A, { - chatRoom: room, - parent: this, - containerRef: this.messagesBlockRef, - typingAreaText: this.state.typingAreaText, - onTypingAreaChanged: this.updateTypingAreaText - })))); - } -}, (0,applyDecoratedDescriptor.A)(conversationpanel_class.prototype, "onMouseMove", [conversationpanel_dec], Object.getOwnPropertyDescriptor(conversationpanel_class.prototype, "onMouseMove"), conversationpanel_class.prototype), (0,applyDecoratedDescriptor.A)(conversationpanel_class.prototype, "render", [_dec2], Object.getOwnPropertyDescriptor(conversationpanel_class.prototype, "render"), conversationpanel_class.prototype), conversationpanel_class); -class ConversationPanels extends mixins.w9 { - constructor(props) { - super(props); - this.domRef = REaCt().createRef(); - this.notificationListener = 'meetings:notificationPermissions'; - this.notificationGranted = undefined; - this.notificationHelpURL = `${l.mega_help_host}/chats-meetings/meetings/enable-notification-browser-system-permission`; - this.state = { - supportAlert: undefined, - notificationsPermissions: undefined, - alertsOffset: ALERTS_BASE_OFFSET - }; - this.closeSupportAlert = () => this.setState({ - supportAlert: false - }, () => mega.config.set('nocallsup', 1)); - this.onNotificationsGranted = () => { - msgDialog('info', '', l.notifications_permissions_granted_title, l.notifications_permissions_granted_info.replace('[A]', ``).replace('[/A]', '')); - this.notificationGranted = new Notification(l.notification_granted_title, { - body: l.notification_granted_body - }); - }; - this.state.supportAlert = !megaChat.hasSupportForCalls; - this.state.notificationsPermissions = window.Notification ? Notification.permission : 'granted'; - } - renderNotificationsPending() { - return REaCt().createElement(Alert, { - type: Alert.TYPE.LIGHT, - className: ` - ${megaChat.chatUIFlags.convPanelCollapse ? 'full-span' : ''} - ${this.props.isEmpty ? 'empty-state' : ''} - `, - ref: ref => { - this.notifPendingRef = ref; - }, - onTransition: ref => this.setState({ - alertsOffset: ref ? ref.current.offsetHeight : ALERTS_BASE_OFFSET - }), - onClose: () => { - this.setState({ - notificationsPermissions: undefined - }, () => { - showToast('success', l.notifications_permissions_toast_title, l.notifications_permissions_toast_control, '', () => loadSubPage('fm/account/notifications')); - }); - } - }, l.notifications_permissions_pending, REaCt().createElement("div", { - className: "meetings-alert-control" - }, REaCt().createElement("a", { - href: "#", - onClick: ev => { - ev.preventDefault(); - Notification.requestPermission().then(status => { - this.setState({ - notificationsPermissions: status - }, () => onIdle(() => this.state.notificationsPermissions === 'granted' && this.onNotificationsGranted())); - }).catch(ex => d && console.warn(`Failed to retrieve permissions: ${ex}`)); - } - }, l.notifications_permissions_enable))); - } - renderNotificationsBlocked() { - return REaCt().createElement(Alert, { - type: Alert.TYPE.MEDIUM, - className: ` - ${megaChat.chatUIFlags.convPanelCollapse ? 'full-span' : ''} - ${this.props.isEmpty ? 'empty-state' : ''} - `, - ref: ref => { - this.notifBlockedRef = ref; - }, - onTransition: ref => this.setState({ - alertsOffset: ref ? ref.current.offsetHeight : ALERTS_BASE_OFFSET - }), - onClose: () => this.setState({ - notificationsPermissions: undefined - }) - }, REaCt().createElement(utils.P9, { - content: l.notifications_permissions_denied_info.replace('[A]', ``).replace('[/A]', '') - })); - } - componentWillUnmount() { - super.componentWillUnmount(); - mBroadcaster.removeListener(this.notificationListener); - } - componentDidMount() { - let _this$props$onMount, _this$props; - super.componentDidMount(); - (_this$props$onMount = (_this$props = this.props).onMount) == null || _this$props$onMount.call(_this$props); - megaChat.chats.forEach(chatRoom => { - const { - scheduledMeeting - } = chatRoom; - if (scheduledMeeting && !scheduledMeeting.isPast && scheduledMeeting.isRecurring) { - scheduledMeeting.getOccurrences().catch(nop); - } - }); - mBroadcaster.addListener(this.notificationListener, notificationsPermissions => this.isMounted() && this.setState({ - notificationsPermissions - })); - window.addEventListener('resize', () => { - delay('conv-panels-resize', () => { - if (!M.chat || !this.isMounted()) { - return; - } - const { - alertsOffset - } = this.state; - if (alertsOffset !== ALERTS_BASE_OFFSET) { - let _this$notifBlockedRef, _this$notifPendingRef, _this$noSupportRef; - const state = {}; - if ((_this$notifBlockedRef = this.notifBlockedRef) != null && _this$notifBlockedRef.current) { - state.alertsOffset = this.notifBlockedRef.current.offsetHeight; - } else if ((_this$notifPendingRef = this.notifPendingRef) != null && _this$notifPendingRef.current) { - state.alertsOffset = this.notifPendingRef.current.offsetHeight; - } else if ((_this$noSupportRef = this.noSupportRef) != null && _this$noSupportRef.current) { - state.alertsOffset = this.noSupportRef.current.offsetHeight; - } - if (state.alertsOffset !== alertsOffset) { - this.setState(state); - } - } - }); - }); - } - render() { - const { - routingSection, - chatUIFlags, - isEmpty, - onToggleExpandedFlag - } = this.props; - const { - notificationsPermissions, - supportAlert, - alertsOffset - } = this.state; - const now = Date.now(); - return REaCt().createElement("div", { - ref: this.domRef, - className: "conversation-panels" - }, routingSection === 'contacts' || is_chatlink ? null : window.Notification && notificationsPermissions !== 'granted' && REaCt().createElement(REaCt().Fragment, null, notificationsPermissions === 'default' && this.renderNotificationsPending(), notificationsPermissions === 'denied' && this.renderNotificationsBlocked()), routingSection === 'contacts' ? null : supportAlert && !mega.config.get('nocallsup') && REaCt().createElement(Alert, { - type: Alert.TYPE.MEDIUM, - className: ` - ${megaChat.chatUIFlags.convPanelCollapse ? 'full-span' : ''} - ${isEmpty ? 'empty-state' : ''} - unsupported-call-alert - `, - content: call.Ay.getUnsupportedBrowserMessage(), - ref: ref => { - this.noSupportRef = ref; - }, - onTransition: ref => this.setState({ - alertsOffset: ref ? ref.current.offsetHeight : ALERTS_BASE_OFFSET - }), - onClose: this.closeSupportAlert - }), megaChat.chats.map(chatRoom => { - if (chatRoom.isCurrentlyActive || now - chatRoom.lastShownInUI < 900000) { - return REaCt().createElement(ConversationPanel, { - key: `${chatRoom.roomId}_${chatRoom.instanceIndex}`, - chatRoom, - roomType: chatRoom.type, - isExpanded: chatRoom.megaChat.chatUIFlags.convPanelCollapse, - isActive: chatRoom.isCurrentlyActive, - messagesBuff: chatRoom.messagesBuff, - chatUIFlags, - offset: alertsOffset, - onToggleExpandedFlag - }); - } - return null; - })); - } -} -class EmptyConvPanel extends REaCt().Component { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - this.state = { - linkData: '' - }; - this.Tile = ({ - title, - desc, - imgClass, - buttonPrimary, - buttonSecondary, - onClickPrimary, - onClickSecondary - }) => REaCt().createElement("div", { - className: "conversations-empty-tile" - }, REaCt().createElement("span", { - className: `chat-tile-img ${imgClass}` - }), REaCt().createElement("div", { - className: "tile-content" - }, REaCt().createElement("h2", null, title), REaCt().createElement("div", null, desc), REaCt().createElement(buttons.$, { - className: "mega-button positive", - label: buttonPrimary, - onClick: onClickPrimary - }), buttonSecondary && REaCt().createElement(buttons.$, { - className: "mega-button action positive", - icon: "sprite-fm-mono icon-link", - label: buttonSecondary, - onClick: onClickSecondary - }))); - } - componentDidMount() { - (M.account && M.account.contactLink ? Promise.resolve(M.account.contactLink) : api.send('clc')).then(res => { - let _this$domRef; - if ((_this$domRef = this.domRef) != null && _this$domRef.current && typeof res === 'string') { - const prefix = res.startsWith('C!') ? '' : 'C!'; - this.setState({ - linkData: `${getBaseUrl()}/${prefix}${res}` - }); - } - }).catch(dump); - } - render() { - const { - isMeeting, - onNewChat, - onStartMeeting, - onScheduleMeeting - } = this.props; - const { - linkData - } = this.state; - return REaCt().createElement("div", { - ref: this.domRef, - className: "conversations-empty" - }, REaCt().createElement("div", { - className: "conversations-empty-header" - }, REaCt().createElement("h1", null, isMeeting ? l.meetings_empty_header : l.chat_empty_header), REaCt().createElement("h3", null, (0,utils.lI)(isMeeting ? l.meetings_empty_subheader : l.chat_empty_subheader, '[A]', ui_link.A, { - onClick: () => { - window.open('https://mega.io/chatandmeetings', '_blank', 'noopener,noreferrer'); - eventlog(this.props.isMeeting ? 500281 : 500280); - } - }))), REaCt().createElement("div", { - className: "conversations-empty-content" - }, REaCt().createElement(this.Tile, { - title: isMeeting ? l.meetings_empty_calls_head : l.invite_friend_btn, - desc: isMeeting ? l.meetings_empty_calls_desc : l.chat_empty_contact_desc, - imgClass: isMeeting ? 'empty-meetings-call' : 'empty-chat-contacts', - buttonPrimary: isMeeting ? l.new_meeting_start : l[71], - buttonSecondary: !isMeeting && linkData && l.copy_contact_link_btn, - onClickPrimary: () => { - if (isMeeting) { - onStartMeeting(); - eventlog(500275); - } else { - contactAddDialog(); - eventlog(500276); - } - }, - onClickSecondary: () => { - copyToClipboard(linkData, `${l[371]}${linkData}`); - delay('chat-event-copy-contact-link', () => eventlog(500277)); - } - }), REaCt().createElement(this.Tile, { - title: isMeeting ? l.meetings_empty_schedule_head : l.chat_empty_add_chat_header, - desc: isMeeting ? l.meetings_empty_schedule_desc : l.chat_empty_add_chat_desc, - imgClass: isMeeting ? 'empty-meetings-schedule' : 'empty-chat-new', - buttonPrimary: isMeeting ? l.schedule_meeting_start : l.add_chat, - onClickPrimary: () => { - if (isMeeting) { - onScheduleMeeting(); - eventlog(500278); - } else { - onNewChat(); - eventlog(500279); - } - } - }))); - } -} -function isStartCallDisabled(room) { - if ((0,call.P)()) { - return true; - } - if (!megaChat.hasSupportForCalls) { - return true; - } - return !room.isOnlineForCalls() || room.isReadOnly() || !room.chatId || room.call || (room.type === "group" || room.type === "public") && false || room.getCallParticipants().length > 0; -} - -}, - -732 -(_, EXP_, REQ_) { - -"use strict"; - -// EXPORTS -REQ_.d(EXP_, { - qY: () => conversations_EVENTS, - Vw: () => VIEWS, - Ay: () => conversations -}); - -// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/extends.js -const esm_extends = REQ_(168); -// EXTERNAL MODULE: external "React" -const React_ = REQ_(594); -const REaCt = REQ_.n(React_); -// EXTERNAL MODULE: ./js/chat/mixins.js -const mixins = REQ_(137); -// EXTERNAL MODULE: ./js/chat/ui/conversationpanel.jsx + 15 modules -const conversationpanel = REQ_(438); -// EXTERNAL MODULE: ./js/chat/ui/contactsPanel/contactsPanel.jsx + 20 modules -const contactsPanel = REQ_(173); -// EXTERNAL MODULE: ./js/ui/modalDialogs.jsx + 1 modules -const modalDialogs = REQ_(318); -// EXTERNAL MODULE: ./js/chat/ui/meetings/button.jsx -const meetings_button = REQ_(959); -// EXTERNAL MODULE: ./js/chat/ui/meetings/workflow/preview.jsx -const preview = REQ_(485); -// EXTERNAL MODULE: ./js/chat/ui/link.jsx -const ui_link = REQ_(280); -// EXTERNAL MODULE: ./js/ui/utils.jsx -const utils = REQ_(314); -;// ./js/chat/ui/meetings/workflow/start.jsx - -let _Start; - - - - - - -class Start extends REaCt().Component { - constructor(props) { - super(props); - this.inputRef = REaCt().createRef(); - this.defaultTopic = l.default_meeting_topic.replace('%NAME', M.getNameByHandle(u_handle)); - this.state = { - audio: false, - video: false, - editing: false, - previousTopic: undefined, - topic: undefined - }; - this.handleChange = ev => this.setState({ - topic: ev.target.value - }); - this.toggleEdit = () => { - this.setState(state => { - const topic = state.topic.trim() || this.defaultTopic; - return { - editing: !state.editing, - topic, - previousTopic: topic - }; - }, () => onIdle(this.doFocus)); - }; - this.doFocus = () => { - if (this.state.editing) { - const input = this.inputRef.current; - input.focus(); - input.setSelectionRange(0, input.value.length); - } - }; - this.doReset = () => this.setState(state => ({ - editing: false, - topic: state.previousTopic, - previousTopic: undefined - })); - this.bindEvents = () => $(document).rebind(`mousedown.${Start.NAMESPACE}`, ev => { - if (this.state.editing && !ev.target.classList.contains(Start.CLASS_NAMES.EDIT) && !ev.target.classList.contains(Start.CLASS_NAMES.INPUT)) { - this.toggleEdit(); - } - }).rebind(`keyup.${Start.NAMESPACE}`, ({ - keyCode - }) => { - if (this.state.editing) { - const [ENTER, ESCAPE] = [13, 27]; - return keyCode === ENTER ? this.toggleEdit() : keyCode === ESCAPE ? this.doReset() : null; - } - }); - this.Input = () => REaCt().createElement("input", { - type: "text", - ref: this.inputRef, - className: Start.CLASS_NAMES.INPUT, - value: this.state.topic, - maxLength: ChatRoom.TOPIC_MAX_LENGTH, - onChange: this.handleChange - }); - this.onStreamToggle = (audio, video) => this.setState({ - audio, - video - }); - this.startMeeting = () => { - const { - onStart - } = this.props; - const { - topic, - audio, - video - } = this.state; - if (onStart) { - onStart(topic.trim() || this.defaultTopic, audio, video); - } - }; - this.state.topic = this.defaultTopic; - } - componentDidMount() { - this.bindEvents(); - if ($.dialog === 'onboardingDialog') { - closeDialog(); - } - M.safeShowDialog(Start.dialogName, () => $(`#${Start.NAMESPACE}`)); - } - componentWillUnmount() { - $(document).unbind(`.${Start.NAMESPACE}`); - if ($.dialog === Start.dialogName) { - closeDialog(); - } - } - render() { - const { - NAMESPACE, - CLASS_NAMES - } = Start; - const { - editing, - topic - } = this.state; - return REaCt().createElement(modalDialogs.A.ModalDialog, (0,esm_extends.A)({}, this.state, { - id: NAMESPACE, - dialogName: NAMESPACE, - className: NAMESPACE, - stopKeyPropagation: editing, - onClose: () => this.props.onClose() - }), REaCt().createElement("div", { - className: `${NAMESPACE}-preview` - }, REaCt().createElement(preview.A, { - context: NAMESPACE, - onToggle: this.onStreamToggle - })), REaCt().createElement("div", { - className: "fm-dialog-body" - }, REaCt().createElement("div", { - className: `${NAMESPACE}-title` - }, editing ? REaCt().createElement(this.Input, null) : REaCt().createElement("h2", { - onClick: this.toggleEdit - }, REaCt().createElement(utils.zT, null, topic)), REaCt().createElement(meetings_button.A, { - className: ` - mega-button - action - small - ${CLASS_NAMES.EDIT} - ${editing ? 'editing' : ''} - `, - icon: "icon-rename", - simpletip: { - label: l[1342], - position: 'top' - }, - onClick: this.toggleEdit - }, REaCt().createElement("span", null, l[1342]))), REaCt().createElement(meetings_button.A, { - className: "mega-button positive large start-meeting-button", - onClick: () => { - this.startMeeting(); - eventlog(500235); - } - }, REaCt().createElement("span", null, l[7315])), REaCt().createElement(ui_link.A, { - to: "https://mega.io/chatandmeetings", - target: "_blank" - }, l.how_meetings_work))); - } -} -_Start = Start; -Start.NAMESPACE = 'start-meeting'; -Start.dialogName = `${_Start.NAMESPACE}-dialog`; -Start.CLASS_NAMES = { - EDIT: 'call-title-edit', - INPUT: 'call-title-input' -}; -Start.STREAMS = { - AUDIO: 1, - VIDEO: 2 -}; -window.StartMeetingDialogUI = { - Start -}; -// EXTERNAL MODULE: ./js/ui/perfectScrollbar.jsx -const perfectScrollbar = REQ_(486); -// EXTERNAL MODULE: ./js/chat/ui/contacts.jsx -const ui_contacts = REQ_(251); -;// ./js/chat/ui/meetings/schedule/invite.jsx - - - - -class Invite extends mixins.w9 { - constructor(props) { - super(props); - this.domRef = REaCt().createRef(); - this.wrapperRef = REaCt().createRef(); - this.inputRef = REaCt().createRef(); - this.state = { - value: '', - expanded: false, - loading: true, - frequents: [], - frequentsInitial: [], - contacts: [], - contactsInitial: [], - selected: [] - }; - this.handleMousedown = ({ - target - }) => this.domRef && this.domRef.current && this.domRef.current.contains(target) ? null : this.setState({ - expanded: false - }); - this.getSortedContactsList = frequents => { - const filteredContacts = []; - M.u.forEach(contact => { - if (contact.c === 1 && !frequents.includes(contact.u) && !this.state.selected.includes(contact.u)) { - filteredContacts.push(contact); - } - }); - const sortFn = M.getSortByNameFn2(1); - filteredContacts.sort((a, b) => sortFn(a, b)); - return filteredContacts; - }; - this.doMatch = (value, collection) => { - value = value.toLowerCase(); - return collection.filter(contact => { - contact = typeof contact === 'string' ? M.getUserByHandle(contact) : contact; - const name = M.getNameByHandle(contact.u).toLowerCase(); - const email = contact.m && contact.m.toLowerCase(); - return name.includes(value) || email.includes(value); - }); - }; - this.handleSearch = this.handleSearch.bind(this); - this.state.selected = this.props.participants || []; - } - reinitializeWrapper() { - const wrapperRef = this.wrapperRef && this.wrapperRef.current; - if (wrapperRef) { - wrapperRef.reinitialise(); - wrapperRef.scrollToY(0); - } - } - buildContactsList() { - megaChat.getFrequentContacts().then(frequentContacts => { - if (this.isMounted()) { - const frequents = frequentContacts.slice(-ui_contacts.MAX_FREQUENTS).map(c => c.userId); - const contacts = this.getSortedContactsList(frequents); - this.setState({ - frequents, - frequentsInitial: frequents, - contacts, - contactsInitial: contacts, - loading: false - }); - } - }); - } - handleSearch(ev) { - const { - value - } = ev.target; - const searching = value.length >= 2; - const frequents = searching ? this.doMatch(value, this.state.frequentsInitial) : this.state.frequentsInitial; - const contacts = searching ? this.doMatch(value, this.state.contactsInitial) : this.state.contactsInitial; - this.setState({ - value, - contacts, - frequents - }, () => this.reinitializeWrapper()); - } - handleSelect({ - userHandle, - expanded = false - }) { - this.setState(state => ({ - value: '', - expanded, - selected: state.selected.includes(userHandle) ? state.selected.filter(c => c !== userHandle) : [...state.selected, userHandle] - }), () => { - let _this$inputRef$curren; - this.props.onSelect(this.state.selected); - this.buildContactsList(); - this.reinitializeWrapper(); - (_this$inputRef$curren = this.inputRef.current) == null || _this$inputRef$curren.focus(); - }); - } - getFilteredContacts(contacts) { - if (contacts && contacts.length) { - return contacts.map(contact => { - contact = contact instanceof MegaDataMap ? contact : M.u[contact]; - return this.state.selected.includes(contact.u) ? null : REaCt().createElement("div", { - key: contact.u, - className: "invite-section-item", - onClick: () => { - this.handleSelect({ - userHandle: contact.u, - expanded: true - }); - } - }, REaCt().createElement(ui_contacts.Avatar, { - contact - }), REaCt().createElement("div", { - className: "invite-item-data" - }, REaCt().createElement("div", { - className: "invite-item-name" - }, REaCt().createElement(ui_contacts.ContactAwareName, { - overflow: true, - simpletip: { - offset: 10 - }, - contact - })), REaCt().createElement("div", { - className: "invite-item-mail" - }, contact.m))); - }); - } - return null; - } - renderContent() { - const { - frequents, - contacts, - selected - } = this.state; - const hasMoreFrequents = frequents.length && frequents.some(h => !selected.includes(h)); - const $$SECTION = (title, children) => REaCt().createElement("div", { - className: "invite-section" - }, REaCt().createElement("div", { - className: "invite-section-title" - }, title), children && REaCt().createElement("div", { - className: "invite-section-list" - }, children)); - if (hasMoreFrequents || contacts.length) { - return REaCt().createElement(perfectScrollbar.O, { - ref: this.wrapperRef, - className: "invite-scroll-wrapper", - options: { - 'suppressScrollX': true - } - }, hasMoreFrequents ? $$SECTION(l.recent_contact_label, this.getFilteredContacts(frequents)) : '', contacts.length ? $$SECTION(l.all_contact_label, this.getFilteredContacts(contacts)) : '', frequents.length === 0 && contacts.length === 0 && $$SECTION(l.invite_no_results_found, null)); - } - return $$SECTION(l.invite_no_contacts_to_add, null); - } - componentWillUnmount() { - super.componentWillUnmount(); - document.removeEventListener('mousedown', this.handleMousedown); - } - componentDidMount() { - super.componentDidMount(); - document.addEventListener('mousedown', this.handleMousedown); - this.buildContactsList(); - } - render() { - const { - className, - isLoading - } = this.props; - const { - value, - expanded, - loading, - selected - } = this.state; - return REaCt().createElement("div", { - ref: this.domRef, - className: ` - ${Invite.NAMESPACE} - ${className || ''} - ` - }, REaCt().createElement("div", { - className: "multiple-input" - }, REaCt().createElement("ul", { - className: "token-input-list-mega", - onClick: ({ - target - }) => isLoading ? null : target.classList.contains('token-input-list-mega') && this.setState({ - expanded: true - }) - }, selected.map(handle => { - return REaCt().createElement("li", { - key: handle, - className: "token-input-token-mega" - }, REaCt().createElement("div", { - className: "contact-tag-item" - }, REaCt().createElement(ui_contacts.Avatar, { - contact: M.u[handle], - className: "avatar-wrapper box-avatar" - }), REaCt().createElement(ui_contacts.ContactAwareName, { - contact: M.u[handle], - overflow: true - }), REaCt().createElement("i", { - className: "sprite-fm-mono icon-close-component", - onClick: () => isLoading ? null : this.handleSelect({ - userHandle: handle - }) - }))); - }), REaCt().createElement("li", { - className: "token-input-input-token-mega" - }, REaCt().createElement("input", { - ref: this.inputRef, - type: "text", - name: "participants", - className: `${Invite.NAMESPACE}-input`, - disabled: isLoading, - autoComplete: "off", - placeholder: selected.length ? '' : l.schedule_participant_input, - value, - onClick: () => this.setState({ - expanded: true - }), - onChange: this.handleSearch, - onKeyDown: ({ - target, - keyCode - }) => { - const { - selected - } = this.state; - return keyCode === 8 && target.value === '' && selected.length && this.handleSelect({ - userHandle: selected[selected.length - 1] - }); - } - })))), loading ? null : REaCt().createElement("div", { - className: `mega-input-dropdown ${expanded ? '' : 'hidden'}` - }, this.renderContent())); - } -} -Invite.NAMESPACE = 'meetings-invite'; -// EXTERNAL MODULE: ./js/chat/ui/meetings/schedule/helpers.jsx -const helpers = REQ_(110); -;// ./js/chat/ui/meetings/schedule/dateObserver.jsx - - -const withDateObserver = Component => class extends REaCt().Component { - constructor(...args) { - super(...args); - this.listener = undefined; - this.state = { - timestamp: undefined - }; - } - componentWillUnmount() { - mBroadcaster.removeListener(this.listener); - } - componentDidMount() { - this.listener = mBroadcaster.addListener(withDateObserver.NAMESPACE, timestamp => this.setState({ - timestamp - })); - } - render() { - return REaCt().createElement(Component, (0,esm_extends.A)({}, this.props, { - timestamp: this.state.timestamp - })); - } -}; -withDateObserver.NAMESPACE = 'meetings:onSelectDate'; -;// ./js/chat/ui/meetings/schedule/datepicker.jsx - - - -class Datepicker extends REaCt().Component { - constructor(props) { - super(props); - this.OPTIONS = { - classes: 'meetings-datepicker-calendar', - dateFormat: '@', - minDate: null, - startDate: null, - selectedDates: [], - prevHtml: '', - nextHtml: '', - altField: null, - firstDay: 0, - autoClose: true, - toggleSelected: false, - position: 'bottom left', - language: { - daysMin: [l[8763], l[8764], l[8765], l[8766], l[8767], l[8768], l[8769]], - months: [l[408], l[409], l[410], l[411], l[412], l[413], l[414], l[415], l[416], l[417], l[418], l[419]], - monthsShort: [l[24035], l[24037], l[24036], l[24038], l[24047], l[24039], l[24040], l[24041], l[24042], l[24043], l[24044], l[24045]] - }, - onSelect: dateText => { - const prevDate = new Date(+this.props.value); - const nextDate = new Date(+dateText); - nextDate.setHours(prevDate.getHours(), prevDate.getMinutes()); - this.props.onSelect(nextDate.getTime()); - mBroadcaster.sendMessage(withDateObserver.NAMESPACE, nextDate.getTime()); - } - }; - this.domRef = REaCt().createRef(); - this.inputRef = REaCt().createRef(); - this.datepicker = null; - this.formatValue = value => { - if (typeof value === 'number') { - return time2date(value / 1000, 18); - } - return value; - }; - this.OPTIONS.startDate = new Date(this.props.startDate); - this.OPTIONS.selectedDates = this.props.selectedDates || [this.OPTIONS.startDate]; - this.OPTIONS.minDate = this.props.minDate ? new Date(this.props.minDate) : new Date(); - this.OPTIONS.position = this.props.position || this.OPTIONS.position; - this.OPTIONS.altField = `input.${this.props.altField}`; - } - initialize() { - const inputRef = this.inputRef && this.inputRef.current; - if (inputRef) { - let _this$props$onMount, _this$props; - $(inputRef).datepicker(this.OPTIONS); - this.datepicker = $(inputRef).data('datepicker'); - (_this$props$onMount = (_this$props = this.props).onMount) == null || _this$props$onMount.call(_this$props, this.datepicker); - } - } - componentWillUnmount() { - if (this.domRef && this.domRef.current) { - $(this.domRef.current).unbind(`keyup.${Datepicker.NAMESPACE}`); - } - } - componentDidMount() { - M.require('datepicker_js').done(() => this.initialize()); - if (this.domRef && this.domRef.current) { - $(this.domRef.current).rebind(`keyup.${Datepicker.NAMESPACE}`, ({ - keyCode - }) => { - if (keyCode === 13) { - this.datepicker.hide(); - return false; - } - }); - } - } - render() { - const { - NAMESPACE - } = Datepicker; - const { - value, - name, - className, - placeholder, - isLoading, - onFocus, - onChange, - onBlur - } = this.props; - const formattedValue = this.formatValue(value); - return REaCt().createElement("div", { - ref: this.domRef, - className: NAMESPACE - }, REaCt().createElement("div", { - className: "mega-input datepicker-input" - }, REaCt().createElement("input", { - ref: this.inputRef, - type: "text", - name, - className: ` - dialog-input - ${className || ''} - `, - autoComplete: "off", - disabled: isLoading, - placeholder: placeholder || '', - value: formattedValue, - onFocus: ev => onFocus == null ? void 0 : onFocus(ev), - onChange: ev => onChange == null ? void 0 : onChange(ev), - onBlur: ev => onBlur == null ? void 0 : onBlur(ev) - }), REaCt().createElement("i", { - className: "sprite-fm-mono icon-calendar1", - onClick: isLoading ? null : () => { - if (this.datepicker) { - let _this$inputRef$curren; - this.datepicker.show(); - (_this$inputRef$curren = this.inputRef.current) == null || _this$inputRef$curren.focus(); - } - } - }))); - } -} -Datepicker.NAMESPACE = 'meetings-datepicker'; -const datepicker = (0,mixins.Zz)(withDateObserver)(Datepicker); -;// ./js/chat/ui/meetings/schedule/select.jsx - - - - - -class Select extends REaCt().Component { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - this.inputRef = REaCt().createRef(); - this.menuRef = REaCt().createRef(); - this.optionRefs = {}; - this.state = { - expanded: false, - manualTimeInput: '', - timestamp: '' - }; - this.handleMousedown = ({ - target - }) => { - let _this$domRef; - return (_this$domRef = this.domRef) != null && _this$domRef.current.contains(target) ? null : this.setState({ - expanded: false - }); - }; - this.handleToggle = ({ - target - } = {}) => { - let _this$menuRef, _menuRef$domRef; - const menuRef = (_this$menuRef = this.menuRef) == null ? void 0 : _this$menuRef.current; - const menuElement = (_menuRef$domRef = menuRef.domRef) == null ? void 0 : _menuRef$domRef.current; - if (target !== menuElement) { - const { - value - } = this.props; - this.setState(state => ({ - expanded: !state.expanded - }), () => { - if (value && this.optionRefs[value]) { - menuRef.scrollToElement(this.optionRefs[value]); - } - }); - } - }; - } - getFormattedDuration(duration) { - duration = moment.duration(duration); - const days = duration.get('days'); - const hours = duration.get('hours'); - const minutes = duration.get('minutes'); - if (!hours && !minutes && !days) { - return ''; - } - const totalHours = days ? ~~duration.asHours() : hours; - if (!hours && minutes) { - return days ? `(${totalHours}\u00a0h ${minutes}\u00a0m)` : `(${minutes}\u00a0m)`; - } - return minutes ? `(${totalHours}\u00a0h ${minutes}\u00a0m)` : `(${totalHours}\u00a0h)`; - } - componentWillUnmount() { - document.removeEventListener('mousedown', this.handleMousedown); - if (this.inputRef && this.inputRef.current) { - $(this.inputRef.current).unbind(`keyup.${Select.NAMESPACE}`); - } - } - componentDidMount() { - let _this$inputRef; - document.addEventListener('mousedown', this.handleMousedown); - const inputRef = (_this$inputRef = this.inputRef) == null ? void 0 : _this$inputRef.current; - if (inputRef) { - $(inputRef).rebind(`keyup.${Select.NAMESPACE}`, ({ - keyCode - }) => { - if (keyCode === 13) { - this.handleToggle(); - inputRef.blur(); - return false; - } - }); - } - } - render() { - const { - NAMESPACE - } = Select; - const { - name, - className, - icon, - typeable, - options, - value, - format, - isLoading, - onChange, - onBlur, - onSelect - } = this.props; - return REaCt().createElement("div", { - ref: this.domRef, - className: ` - ${NAMESPACE} - ${className || ''} - ` - }, REaCt().createElement("div", { - className: ` - mega-input - dropdown-input - ${typeable ? 'typeable' : ''} - `, - onClick: isLoading ? null : this.handleToggle - }, typeable ? null : value && REaCt().createElement("span", null, format ? format(value) : value), REaCt().createElement("input", { - ref: this.inputRef, - type: "text", - className: ` - ${NAMESPACE}-input - ${name} - `, - value: (() => { - if (this.state.manualTimeInput) { - return this.state.manualTimeInput; - } - return format ? format(value) : value; - })(), - onFocus: ({ - target - }) => { - this.setState({ - manualTimeInput: '', - timestamp: '' - }, () => target.select()); - }, - onChange: ({ - target - }) => { - const { - value: manualTimeInput - } = target; - const { - value - } = this.props; - const prevDate = moment(value); - const inputTime = (0,helpers.We)(manualTimeInput); - prevDate.set({ - hours: inputTime.get('hours'), - minutes: inputTime.get('minutes') - }); - const timestamp = prevDate.valueOf(); - onChange == null || onChange(timestamp); - if (this.optionRefs[value]) { - this.menuRef.current.scrollToElement(this.optionRefs[value]); - } - this.setState({ - manualTimeInput, - timestamp - }); - }, - onBlur: () => { - onBlur(this.state.timestamp); - this.setState({ - manualTimeInput: '', - timestamp: '' - }); - } - }), icon && REaCt().createElement("i", { - className: "sprite-fm-mono icon-dropdown" - }), options && REaCt().createElement("div", { - className: ` - mega-input-dropdown - ${this.state.expanded ? '' : 'hidden'} - ` - }, REaCt().createElement(perfectScrollbar.O, { - ref: this.menuRef, - options: { - suppressScrollX: true - } - }, options.map(option => { - return REaCt().createElement("div", { - ref: ref => { - this.optionRefs[option.value] = ref; - }, - key: option.value, - className: ` - option - ${option.value === value || option.label === value ? 'active' : ''} - `, - onClick: () => onSelect(option) - }, option.label, "\xA0", option.duration && this.getFormattedDuration(option.duration)); - }))))); - } -} -Select.NAMESPACE = 'meetings-select'; -const schedule_select = (0,mixins.Zz)(withDateObserver)(Select); -;// ./js/chat/ui/meetings/schedule/datetime.jsx - - - - -class DateTime extends REaCt().Component { - constructor(...args) { - super(...args); - this.state = { - datepickerRef: undefined, - manualDateInput: '', - manualTimeInput: '', - initialDate: '' - }; - this.handleChange = ev => { - const { - onChange - } = this.props; - const { - datepickerRef, - initialDate - } = this.state; - if (!datepickerRef) { - return; - } - const { - value - } = ev.target; - const date = (0,helpers.XH)(value); - const timestamp = date.valueOf(); - const dateObj = new Date(timestamp); - dateObj.setHours(initialDate.getHours(), initialDate.getMinutes()); - datepickerRef.selectedDates = [dateObj]; - datepickerRef.currentDate = dateObj; - datepickerRef.nav._render(); - datepickerRef.views.days._render(); - onChange == null || onChange(value); - this.setState({ - manualDateInput: dateObj.getTime() - }); - }; - } - render() { - const { - name, - startDate, - altField, - value, - minDate, - filteredTimeIntervals, - label, - isLoading, - onMount, - onSelectDate, - onSelectTime, - onBlur - } = this.props; - return REaCt().createElement(REaCt().Fragment, null, label && REaCt().createElement("span", null, label), REaCt().createElement(datepicker, { - name: `${datepicker.NAMESPACE}-${name}`, - className: isLoading ? 'disabled' : '', - isLoading, - startDate, - altField: `${schedule_select.NAMESPACE}-${altField}`, - value, - minDate, - onMount: datepickerRef => this.setState({ - datepickerRef - }, () => onMount(datepickerRef)), - onSelect: onSelectDate, - onFocus: ({ - target - }) => { - this.setState({ - manualDateInput: undefined, - manualTimeInput: undefined, - initialDate: new Date(value) - }, () => target.select()); - }, - onChange: this.handleChange, - onBlur: () => onBlur(this.state.manualDateInput) - }), REaCt().createElement(schedule_select, { - name: `${schedule_select.NAMESPACE}-${altField}`, - className: isLoading ? 'disabled' : '', - isLoading, - typeable: true, - options: filteredTimeIntervals, - value: (() => typeof value === 'number' ? value : this.state.datepickerRef.currentDate.getTime())(), - format: toLocaleTime, - onSelect: onSelectTime, - onChange: () => false, - onBlur: timestamp => { - if (timestamp) { - onSelectTime({ - value: timestamp - }); - } - } - })); - } -} -;// ./js/chat/ui/meetings/schedule/recurring.jsx - - - - - - - - - - - - -class Recurring extends mixins.w9 { - constructor(props) { - let _Object$values$find; - super(props); - this.domRef = REaCt().createRef(); - this.VIEWS = { - DAILY: 0x00, - WEEKLY: 0x01, - MONTHLY: 0x02 - }; - this.FREQUENCIES = { - DAILY: 'd', - WEEKLY: 'w', - MONTHLY: 'm' - }; - this.WEEK_DAYS = { - MONDAY: { - value: 1, - label: l.schedule_day_control_mon - }, - TUESDAY: { - value: 2, - label: l.schedule_day_control_tue - }, - WEDNESDAY: { - value: 3, - label: l.schedule_day_control_wed - }, - THURSDAY: { - value: 4, - label: l.schedule_day_control_thu - }, - FRIDAY: { - value: 5, - label: l.schedule_day_control_fri - }, - SATURDAY: { - value: 6, - label: l.schedule_day_control_sat - }, - SUNDAY: { - value: 7, - label: l.schedule_day_control_sun - } - }; - this.OFFSETS = [[l.recur_freq_offset_first_mon || '[A]first[/A][B]Monday[/B]', l.recur_freq_offset_first_tue || '[A]first[/A][B]Tuesday[/B]', l.recur_freq_offset_first_wed || '[A]first[/A][B]Wednesday[/B]', l.recur_freq_offset_first_thu || '[A]first[/A][B]Thursday[/B]', l.recur_freq_offset_first_fri || '[A]first[/A][B]Friday[/B]', l.recur_freq_offset_first_sat || '[A]first[/A][B]Saturday[/B]', l.recur_freq_offset_first_sun || '[A]first[/A][B]Sunday[/B]'], [l.recur_freq_offset_second_mon || '[A]second[/A][B]Monday[/B]', l.recur_freq_offset_second_tue || '[A]second[/A][B]Tuesday[/B]', l.recur_freq_offset_second_wed || '[A]second[/A][B]Wednesday[/B]', l.recur_freq_offset_second_thu || '[A]second[/A][B]Thursday[/B]', l.recur_freq_offset_second_fri || '[A]second[/A][B]Friday[/B]', l.recur_freq_offset_second_sat || '[A]second[/A][B]Saturday[/B]', l.recur_freq_offset_second_sun || '[A]second[/A][B]Sunday[/B]'], [l.recur_freq_offset_third_mon || '[A]third[/A][B]Monday[/B]', l.recur_freq_offset_third_tue || '[A]third[/A][B]Tuesday[/B]', l.recur_freq_offset_third_wed || '[A]third[/A][B]Wednesday[/B]', l.recur_freq_offset_third_thu || '[A]third[/A][B]Thursday[/B]', l.recur_freq_offset_third_fri || '[A]third[/A][B]Friday[/B]', l.recur_freq_offset_third_sat || '[A]third[/A][B]Saturday[/B]', l.recur_freq_offset_third_sun || '[A]third[/A][B]Sunday[/B]'], [l.recur_freq_offset_fourth_mon || '[A]fourth[/A][B]Monday[/B]', l.recur_freq_offset_fourth_tue || '[A]fourth[/A][B]Tuesday[/B]', l.recur_freq_offset_fourth_wed || '[A]fourth[/A][B]Wednesday[/B]', l.recur_freq_offset_fourth_thu || '[A]fourth[/A][B]Thursday[/B]', l.recur_freq_offset_fourth_fri || '[A]fourth[/A][B]Friday[/B]', l.recur_freq_offset_fourth_sat || '[A]fourth[/A][B]Saturday[/B]', l.recur_freq_offset_fourth_sun || '[A]fourth[/A][B]Sunday[/B]'], [l.recur_freq_offset_fifth_mon || '[A]fifth[/A][B]Monday[/B]', l.recur_freq_offset_fifth_tue || '[A]fifth[/A][B]Tuesday[/B]', l.recur_freq_offset_fifth_wed || '[A]fifth[/A][B]Wednesday[/B]', l.recur_freq_offset_fifth_thu || '[A]fifth[/A][B]Thursday[/B]', l.recur_freq_offset_fifth_fri || '[A]fifth[/A][B]Friday[/B]', l.recur_freq_offset_fifth_sat || '[A]fifth[/A][B]Saturday[/B]', l.recur_freq_offset_fifth_sun || '[A]fifth[/A][B]Sunday[/B]']]; - this.OFFSET_POS_REGEX = /\[A]([^[]+)\[\/A]/; - this.OFFSET_DAY_REGEX = /\[B]([^[]+)\[\/B]/; - this.MONTH_RULES = { - DAY: 'day', - OFFSET: 'offset' - }; - this.initialEnd = (0,helpers.PS)(this.props.startDateTime, 6); - this.initialWeekDays = Object.values(this.WEEK_DAYS).map(d => d.value); - this.initialMonthDay = this.props.startDateTime ? new Date(this.props.startDateTime).getDate() : undefined; - this.state = { - view: this.VIEWS.DAILY, - frequency: this.FREQUENCIES.DAILY, - end: this.initialEnd, - prevEnd: undefined, - interval: 0, - weekDays: this.initialWeekDays, - monthRule: this.MONTH_RULES.DAY, - monthDays: [this.initialMonthDay], - offset: { - value: 1, - weekDay: 1 - }, - monthDaysWarning: this.initialMonthDay > 28 - }; - this.toggleView = (view, frequency, state) => this.props.isLoading ? null : this.setState({ - view, - frequency, - ...state - }); - this.MonthDaySelect = ({ - offset - }) => { - const dayIdx = (offset && offset.weekDay || 1) - 1; - const posIdx = (offset && offset.value || 1) - 1; - const dayValues = this.OFFSETS[posIdx].map((part, idx) => ({ - value: idx + 1, - label: this.OFFSET_DAY_REGEX.exec(part)[1] - })); - const posValues = []; - for (let i = 0; i < this.OFFSETS.length; i++) { - posValues.push({ - value: i + 1, - label: this.OFFSET_POS_REGEX.exec(this.OFFSETS[i][dayIdx])[1] - }); - } - const posFirst = this.OFFSETS[posIdx][dayIdx].indexOf('[A]') < this.OFFSETS[posIdx][dayIdx].indexOf('[B]'); - const pos = REaCt().createElement(schedule_select, { - name: "recurring-offset-value", - className: "inline", - icon: true, - value: posValues[posIdx].label, - isLoading: this.props.isLoading, - options: posValues, - onSelect: option => { - this.setState(state => ({ - monthRule: this.MONTH_RULES.OFFSET, - offset: { - value: option.value, - weekDay: state.offset.weekDay || this.WEEK_DAYS.MONDAY.value - } - })); - } - }); - return REaCt().createElement(REaCt().Fragment, null, posFirst && pos, REaCt().createElement(schedule_select, { - name: "recurring-offset-day", - className: "inline", - icon: true, - value: dayValues[dayIdx].label, - isLoading: this.props.isLoading, - options: dayValues, - onSelect: option => { - this.setState(state => ({ - monthRule: this.MONTH_RULES.OFFSET, - offset: { - value: state.offset.value || 1, - weekDay: option.value - } - })); - } - }), !posFirst && pos); - }; - this.IntervalSelect = () => { - const { - interval, - view - } = this.state; - return REaCt().createElement("div", { - className: "mega-input inline recurring-interval" - }, REaCt().createElement(schedule_select, { - name: `${Recurring.NAMESPACE}-interval`, - value: interval > 0 ? interval : 1, - icon: true, - isLoading: this.props.isLoading, - options: [...Array(view === this.VIEWS.WEEKLY ? 52 : 12).keys()].map(value => { - value += 1; - return { - value, - label: value - }; - }), - onSelect: ({ - value - }) => { - this.setState({ - interval: value === 1 ? 0 : value - }); - } - })); - }; - const { - chatRoom, - startDateTime - } = this.props; - const weekDay = new Date(startDateTime).getDay(); - this.state.offset.weekDay = ((_Object$values$find = Object.values(this.WEEK_DAYS).find(d => d.value === weekDay)) == null ? void 0 : _Object$values$find.value) || this.WEEK_DAYS.SUNDAY.value; - if (chatRoom && chatRoom.scheduledMeeting && chatRoom.scheduledMeeting.isRecurring) { - const { - frequency, - interval, - end, - weekDays, - monthDays, - offset - } = chatRoom.scheduledMeeting.recurring; - this.state.view = frequency === 'd' ? this.VIEWS.DAILY : frequency === 'w' ? this.VIEWS.WEEKLY : this.VIEWS.MONTHLY; - this.state.frequency = frequency; - this.state.end = end; - this.state.interval = interval; - this.state.weekDays = weekDays && weekDays.length ? weekDays : this.initialWeekDays; - this.state.monthRule = monthDays && monthDays.length ? this.MONTH_RULES.DAY : this.MONTH_RULES.OFFSET; - this.state.monthDays = monthDays && monthDays.length ? [monthDays[0]] : [this.initialMonthDay]; - this.state.offset = offset && Object.keys(offset).length ? offset : this.state.offset; - } - } - getFormattedState(state) { - const { - frequency, - end, - interval, - weekDays, - monthRule, - monthDays, - offset - } = state; - switch (true) { - case frequency === this.FREQUENCIES.DAILY: - return { - frequency, - end, - weekDays - }; - case frequency === this.FREQUENCIES.WEEKLY: - return { - frequency, - end, - ...interval && { - interval - }, - weekDays - }; - case frequency === this.FREQUENCIES.MONTHLY: - return { - frequency, - end, - ...interval && { - interval - }, - ...monthRule === this.MONTH_RULES.DAY ? { - monthDays - } : { - offset: [[offset.value, offset.weekDay]] - } - }; - } - } - renderDayControls() { - const { - weekDays, - view - } = this.state; - const handleWeeklySelection = (weekDay, remove) => { - this.setState(state => { - if (remove) { - return { - weekDays: state.weekDays.length === 1 ? state.weekDays : state.weekDays.filter(d => d !== weekDay) - }; - } - return { - weekDays: [...state.weekDays, weekDay] - }; - }, () => { - const { - weekDays - } = this.state; - if (weekDays.length === Object.keys(this.WEEK_DAYS).length) { - this.toggleView(this.VIEWS.DAILY, this.FREQUENCIES.DAILY); - } - }); - }; - const handleDailySelection = weekDay => { - this.toggleView(this.VIEWS.WEEKLY, this.FREQUENCIES.WEEKLY, { - weekDays: weekDays.filter(d => d !== weekDay) - }); - }; - return REaCt().createElement("div", { - className: "recurring-field-row" - }, Object.values(this.WEEK_DAYS).map(({ - value, - label - }) => { - const isCurrentlySelected = weekDays.includes(value); - return REaCt().createElement(meetings_button.A, { - key: value, - className: ` - mega-button - action - recurring-toggle-button - ${isCurrentlySelected ? 'active' : ''} - ${weekDays.length === 1 && isCurrentlySelected ? 'disabled' : ''} - `, - onClick: this.props.isLoading ? null : () => { - if (view === this.VIEWS.WEEKLY) { - return handleWeeklySelection(value, isCurrentlySelected); - } - return handleDailySelection(value); - } - }, label); - })); - } - renderIntervalControls() { - const { - view, - interval - } = this.state; - return REaCt().createElement("div", { - className: "recurring-field-row" - }, (0,utils.lI)(mega.icu.format(view === this.VIEWS.MONTHLY ? l.recur_rate_monthly : l.recur_rate_weekly, interval > 0 ? interval : 1), "[S]", this.IntervalSelect)); - } - renderEndControls() { - const { - isLoading, - onMount - } = this.props; - const { - end, - prevEnd - } = this.state; - return REaCt().createElement("div", { - className: "recurring-field-row" - }, REaCt().createElement("div", { - className: "recurring-title-heading" - }, l.recurring_ends), REaCt().createElement("div", { - className: "recurring-radio-buttons" - }, REaCt().createElement("div", { - className: "recurring-label-wrap" - }, REaCt().createElement("div", { - className: ` - uiTheme - ${end ? 'radioOff' : 'radioOn'} - ` - }, REaCt().createElement("input", { - type: "radio", - name: `${Recurring.NAMESPACE}-radio-end`, - disabled: isLoading, - className: ` - uiTheme - ${end ? 'radioOff' : 'radioOn'} - `, - onChange: () => { - this.setState(state => ({ - end: undefined, - prevEnd: state.end || state.prevEnd - })); - } - })), REaCt().createElement("div", { - className: "radio-txt" - }, REaCt().createElement("span", { - className: "recurring-radio-label", - onClick: () => isLoading ? null : this.setState(state => ({ - end: undefined, - prevEnd: state.end || state.prevEnd - })) - }, l.recurring_never))), REaCt().createElement("div", { - className: "recurring-label-wrap" - }, REaCt().createElement("div", { - className: ` - uiTheme - ${end ? 'radioOn' : 'radioOff'} - ` - }, REaCt().createElement("input", { - type: "radio", - name: `${Recurring.NAMESPACE}-radio-end`, - disabled: isLoading, - className: ` - uiTheme - ${end ? 'radioOn' : 'radioOff'} - `, - onChange: () => isLoading ? null : this.setState({ - end: prevEnd || this.initialEnd - }) - })), REaCt().createElement("div", { - className: "radio-txt" - }, REaCt().createElement("span", { - className: "recurring-radio-label", - onClick: () => isLoading || end ? null : this.setState({ - end: prevEnd || this.initialEnd - }) - }, l.recurring_on), REaCt().createElement(datepicker, { - name: `${Recurring.NAMESPACE}-endDateTime`, - position: "top left", - startDate: end || this.initialEnd, - selectedDates: [new Date(end)], - isLoading, - value: end || prevEnd || '', - placeholder: time2date(end || prevEnd || this.initialEnd / 1000, 18), - onMount, - onSelect: timestamp => this.setState({ - end: timestamp - }, () => this.safeForceUpdate()) - }))))); - } - renderDaily() { - return REaCt().createElement("div", { - className: `${Recurring.NAMESPACE}-daily` - }, this.renderDayControls(), this.renderEndControls()); - } - renderWeekly() { - return REaCt().createElement("div", { - className: `${Recurring.NAMESPACE}-weekly` - }, this.renderIntervalControls(), this.renderDayControls(), this.renderEndControls()); - } - renderMonthly() { - const { - isLoading - } = this.props; - const { - monthRule, - monthDays, - monthDaysWarning, - offset - } = this.state; - return REaCt().createElement("div", { - className: `${Recurring.NAMESPACE}-monthly` - }, this.renderIntervalControls(), REaCt().createElement("div", { - className: "recurring-field-row" - }, REaCt().createElement("div", { - className: "recurring-radio-buttons", - onClick: isLoading ? null : ev => { - const { - name, - value - } = ev.target; - if (name === `${Recurring.NAMESPACE}-radio-monthRule`) { - this.setState({ - monthRule: value - }); - } - } - }, REaCt().createElement("div", { - className: "recurring-label-wrap" - }, REaCt().createElement("div", { - className: ` - uiTheme - ${monthRule === 'day' ? 'radioOn' : 'radioOff'} - ` - }, REaCt().createElement("input", { - type: "radio", - name: `${Recurring.NAMESPACE}-radio-monthRule`, - value: "day", - disabled: isLoading, - className: ` - uiTheme - ${monthRule === 'day' ? 'radioOn' : 'radioOff'} - ` - })), REaCt().createElement("div", { - className: "radio-txt" - }, REaCt().createElement("span", { - className: "recurring-radio-label", - onClick: () => isLoading ? null : this.setState({ - monthRule: this.MONTH_RULES.DAY - }) - }, l.recurring_frequency_day), REaCt().createElement("div", { - className: "mega-input inline recurring-day" - }, REaCt().createElement(schedule_select, { - name: `${Recurring.NAMESPACE}-monthDay`, - icon: true, - value: monthDays[0], - isLoading, - options: [...Array(31).keys()].map(value => { - value += 1; - return { - value, - label: value - }; - }), - onSelect: ({ - value - }) => { - this.setState({ - monthRule: this.MONTH_RULES.DAY, - monthDays: [value], - monthDaysWarning: value > 28 - }); - } - })))), monthDaysWarning && REaCt().createElement("div", { - className: "recurring-label-wrap" - }, REaCt().createElement("div", { - className: "mega-banner body with-btn" - }, REaCt().createElement("div", { - className: "green-notification cell text-cell" - }, REaCt().createElement("div", { - className: "versioning-body-text" - }, mega.icu.format(l.recurring_monthdays_warning, monthDays[0]))))), REaCt().createElement("div", { - className: "recurring-label-wrap" - }, REaCt().createElement("div", { - className: ` - uiTheme - ${monthRule === this.MONTH_RULES.OFFSET ? 'radioOn' : 'radioOff'} - ` - }, REaCt().createElement("input", { - type: "radio", - name: `${Recurring.NAMESPACE}-radio-monthRule`, - value: "offset", - disabled: isLoading, - className: ` - uiTheme - ${monthRule === this.MONTH_RULES.OFFSET ? 'radioOn' : 'radioOff'} - ` - })), REaCt().createElement("div", { - className: "radio-txt" - }, REaCt().createElement(this.MonthDaySelect, { - offset - }))))), this.renderEndControls()); - } - renderNavigation(view) { - return REaCt().createElement(REaCt().Fragment, null, REaCt().createElement(meetings_button.A, { - className: ` - mega-button - action - recurring-nav-button - ${view === this.VIEWS.DAILY ? 'active' : ''} - `, - onClick: () => this.toggleView(this.VIEWS.DAILY, this.FREQUENCIES.DAILY) - }, l.recurring_daily), REaCt().createElement(meetings_button.A, { - className: ` - mega-button - action - recurring-nav-button - ${view === this.VIEWS.WEEKLY ? 'active' : ''} - `, - onClick: () => this.toggleView(this.VIEWS.WEEKLY, this.FREQUENCIES.WEEKLY) - }, l.recurring_weekly), REaCt().createElement(meetings_button.A, { - className: ` - mega-button - action - recurring-nav-button - ${view === this.VIEWS.MONTHLY ? 'active' : ''} - `, - onClick: () => this.toggleView(this.VIEWS.MONTHLY, this.FREQUENCIES.MONTHLY) - }, l.recurring_monthly)); - } - renderContent(view) { - switch (view) { - case this.VIEWS.DAILY: - return this.renderDaily(); - case this.VIEWS.WEEKLY: - return this.renderWeekly(); - case this.VIEWS.MONTHLY: - return this.renderMonthly(); - } - } - UNSAFE_componentWillUpdate(nextProps, nextState) { - if (this.state.view !== this.VIEWS.DAILY && nextState.view === this.VIEWS.DAILY) { - nextState.weekDays = this.initialWeekDays; - } - if (nextState.weekDays.length === Object.keys(this.WEEK_DAYS).length && this.state.view !== this.VIEWS.WEEKLY && nextState.view === this.VIEWS.WEEKLY || !(0,helpers.ro)(nextProps.startDateTime, this.props.startDateTime) && this.state.view === this.VIEWS.WEEKLY) { - const weekday = new Date(nextProps.startDateTime).getDay(); - nextState.weekDays = [weekday === 0 ? 7 : weekday]; - } - if (!(0,helpers.ro)(nextProps.startDateTime, this.props.startDateTime) && this.state.view === this.VIEWS.MONTHLY) { - let _Object$values$find2; - const nextDate = new Date(nextProps.startDateTime); - nextState.monthDays = [nextDate.getDate()]; - nextState.offset.weekDay = ((_Object$values$find2 = Object.values(this.WEEK_DAYS).find(d => d.value === nextDate.getDay())) == null ? void 0 : _Object$values$find2.value) || this.WEEK_DAYS.SUNDAY.value; - nextState.monthDaysWarning = nextState.monthDays > 28; - } - if (nextState.view === this.VIEWS.MONTHLY && this.state.interval > 12) { - nextState.interval = 12; - } - this.props.onUpdate(this.getFormattedState(nextState)); - } - componentDidMount() { - super.componentDidMount(); - this.props.onUpdate(this.getFormattedState(this.state)); - } - render() { - const { - NAMESPACE - } = Recurring; - const { - view - } = this.state; - return REaCt().createElement(Row, null, REaCt().createElement(Column, null), REaCt().createElement(Column, null, REaCt().createElement("div", { - ref: this.domRef, - className: ` - ${NAMESPACE} - ${this.props.isLoading ? 'disabled' : ''} - ` - }, REaCt().createElement("div", { - className: `${NAMESPACE}-container` - }, REaCt().createElement("div", { - className: `${NAMESPACE}-navigation` - }, this.renderNavigation(view)), REaCt().createElement("div", { - className: `${NAMESPACE}-content` - }, this.renderContent(view)))))); - } -} -Recurring.NAMESPACE = 'meetings-recurring'; -class Edit extends mixins.w9 { - constructor(props) { - super(props); - this.occurrenceRef = null; - this.datepickerRefs = []; - this.interval = ChatRoom.SCHEDULED_MEETINGS_INTERVAL; - this.incomingCallListener = 'onPrepareIncomingCallDialog.recurringEdit'; - this.state = { - startDateTime: undefined, - endDateTime: undefined, - isDirty: false, - closeDialog: false, - overlayed: false - }; - this.onStartDateSelect = startDateTime => { - this.setState({ - startDateTime, - isDirty: true - }, () => { - this.datepickerRefs.endDateTime.selectDate(new Date(startDateTime + this.interval)); - }); - }; - this.onEndDateSelect = endDateTime => { - this.setState({ - endDateTime, - isDirty: true - }, () => { - const { - startDateTime, - endDateTime - } = this.state; - if (endDateTime < startDateTime) { - if (endDateTime < Date.now()) { - return this.setState({ - endDateTime: startDateTime + this.interval - }); - } - this.handleTimeSelect({ - startDateTime: endDateTime - this.interval - }); - } - }); - }; - this.handleTimeSelect = ({ - startDateTime, - endDateTime - }) => { - startDateTime = startDateTime || this.state.startDateTime; - endDateTime = endDateTime || this.state.endDateTime; - this.setState(state => { - return { - startDateTime: endDateTime <= state.startDateTime ? endDateTime - this.interval : startDateTime, - endDateTime: startDateTime >= state.endDateTime ? startDateTime + this.interval : endDateTime, - isDirty: true - }; - }); - }; - const { - scheduledMeeting, - occurrenceId - } = this.props; - this.occurrenceRef = scheduledMeeting.occurrences[occurrenceId]; - if (this.occurrenceRef) { - this.state.startDateTime = this.occurrenceRef.start; - this.state.endDateTime = this.occurrenceRef.end; - } - } - componentWillUnmount() { - super.componentWillUnmount(); - if (this.incomingCallListener) { - megaChat.off(this.incomingCallListener); - } - if ($.dialog === Schedule.dialogName) { - closeDialog(); - } - } - componentDidMount() { - super.componentDidMount(); - M.safeShowDialog(Schedule.dialogName, () => { - if (!this.isMounted()) { - throw Error(`Edit dialog: component not mounted.`); - } - megaChat.rebind(this.incomingCallListener, () => { - if (this.isMounted()) { - this.setState({ - overlayed: true, - closeDialog: false - }); - megaChat.plugins.callManager2.rebind('onRingingStopped.recurringEdit', () => { - megaChat.plugins.callManager2.off('onRingingStopped.recurringEdit'); - this.setState({ - overlayed: false - }); - fm_showoverlay(); - }); - } - }); - return $(`#${Schedule.NAMESPACE}`); - }); - } - componentDidUpdate(prevProps) { - if (prevProps.callExpanded && !this.props.callExpanded) { - if (!$.dialog) { - M.safeShowDialog(Schedule.dialogName, `#${Schedule.NAMESPACE}`); - } - fm_showoverlay(); - this.setState({ - closeDialog: false - }); - } - if (!prevProps.callExpanded && this.props.callExpanded) { - this.setState({ - closeDialog: false - }); - } - } - render() { - const { - chatRoom, - callExpanded, - onClose - } = this.props; - const { - startDateTime, - endDateTime, - isDirty, - closeDialog, - overlayed - } = this.state; - const dialogClasses = ['fluid']; - if (closeDialog) { - dialogClasses.push('with-confirmation-dialog'); - } - if (callExpanded || overlayed) { - dialogClasses.push('hidden'); - } - const withUpgrade = !u_attr.p && endDateTime - startDateTime > 36e5; - if (withUpgrade) { - dialogClasses.push('upgrade'); - } - return REaCt().createElement(modalDialogs.A.ModalDialog, (0,esm_extends.A)({}, this.state, { - id: Schedule.NAMESPACE, - className: dialogClasses.join(' '), - dialogName: Schedule.dialogName, - dialogType: "main", - onClose: () => { - return isDirty ? this.setState({ - closeDialog: true - }) : onClose(); - } - }), REaCt().createElement("header", null, REaCt().createElement("h2", null, l.edit_meeting_title)), REaCt().createElement("div", { - className: "fm-dialog-body" - }, REaCt().createElement(Row, null, REaCt().createElement("div", { - className: "mega-banner body recurring-edit-banner" - }, REaCt().createElement("div", { - className: "cell" - }, (0,utils.lI)(l.scheduled_edit_occurrence_note, '[A]', ui_link.A, { - onClick: () => { - onClose(); - megaChat.trigger(megaChat.plugins.meetingsManager.EVENTS.EDIT, chatRoom); - } - })))), REaCt().createElement(Row, { - className: "start-aligned" - }, REaCt().createElement(Column, null, REaCt().createElement("i", { - className: "sprite-fm-mono icon-recents-filled" - })), REaCt().createElement("div", { - className: "schedule-date-container" - }, REaCt().createElement(DateTime, { - name: "startDateTime", - altField: "startTime", - datepickerRef: this.datepickerRefs.startDateTime, - startDate: startDateTime, - value: startDateTime, - filteredTimeIntervals: (0,helpers.a4)(startDateTime), - label: l.schedule_start_date, - onMount: datepicker => { - this.datepickerRefs.startDateTime = datepicker; - }, - onSelectDate: startDateTime => this.onStartDateSelect(startDateTime), - onSelectTime: ({ - value: startDateTime - }) => this.handleTimeSelect({ - startDateTime - }), - onChange: value => this.setState({ - startDateTime: value - }), - onBlur: timestamp => { - if (timestamp) { - timestamp = timestamp < Date.now() ? this.occurrenceRef.start : timestamp; - this.onStartDateSelect(timestamp); - } - } - }), REaCt().createElement(DateTime, { - name: "endDateTime", - altField: "endTime", - datepickerRef: this.datepickerRefs.endDateTime, - startDate: endDateTime, - value: endDateTime, - filteredTimeIntervals: (0,helpers.a4)(endDateTime, startDateTime), - label: l.schedule_end_date, - onMount: datepicker => { - this.datepickerRefs.endDateTime = datepicker; - }, - onSelectDate: endDateTime => this.onEndDateSelect(endDateTime), - onSelectTime: ({ - value: endDateTime - }) => this.handleTimeSelect({ - endDateTime - }), - onChange: timestamp => this.setState({ - endDateTime: timestamp - }), - onBlur: timestamp => timestamp && this.onEndDateSelect(timestamp) - }))), withUpgrade && REaCt().createElement(UpgradeNotice, { - onUpgradeClicked: () => { - onClose(); - loadSubPage('pro'); - eventlog(500257); - } - })), REaCt().createElement("footer", null, REaCt().createElement("div", { - className: "footer-container" - }, REaCt().createElement(meetings_button.A, { - className: "mega-button positive", - onClick: () => { - const { - startDateTime, - endDateTime - } = this.state; - if (startDateTime !== this.occurrenceRef.start || endDateTime !== this.occurrenceRef.end) { - delay('chat-event-sm-edit-meeting', () => eventlog(99923)); - this.occurrenceRef.update(startDateTime, endDateTime); - } - onClose(); - } - }, REaCt().createElement("span", null, l.update_meeting_button)))), !(overlayed || callExpanded) && closeDialog && REaCt().createElement(CloseDialog, { - onToggle: () => this.setState({ - closeDialog: false - }), - onClose - })); - } -} -// EXTERNAL MODULE: ./js/chat/chatRoom.jsx + 1 modules -const chat_chatRoom = REQ_(553); -;// ./js/chat/ui/meetings/schedule/schedule.jsx - -let _Schedule; - - - - - - - - - - - - -class Schedule extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - this.scheduledMeetingRef = null; - this.localStreamRef = '.float-video'; - this.datepickerRefs = []; - this.incomingCallListener = 'onPrepareIncomingCallDialog.scheduleDialog'; - this.ringingStoppedListener = 'onRingingStopped.scheduleDialog'; - this.interval = ChatRoom.SCHEDULED_MEETINGS_INTERVAL; - this.nearestHalfHour = (0,helpers.i_)(); - this.state = { - topic: '', - startDateTime: this.nearestHalfHour, - endDateTime: this.nearestHalfHour + this.interval, - timezone: (0,helpers.dB)(), - recurring: false, - participants: [], - link: false, - sendInvite: false, - waitingRoom: false, - openInvite: false, - description: '', - closeDialog: false, - isEdit: false, - isDirty: false, - isLoading: false, - topicInvalid: false, - invalidTopicMsg: '', - descriptionInvalid: false, - overlayed: false - }; - this.onTopicChange = value => { - if (value.length > ChatRoom.TOPIC_MAX_LENGTH) { - this.setState({ - invalidTopicMsg: l.err_schedule_title_long, - topicInvalid: true - }); - value = value.substring(0, ChatRoom.TOPIC_MAX_LENGTH); - } else if (value.length === 0) { - this.setState({ - invalidTopicMsg: l.schedule_title_missing, - topicInvalid: true - }); - } else if (this.state.invalidTopicMsg) { - this.setState({ - invalidTopicMsg: '', - topicInvalid: false - }); - } - this.handleChange('topic', value); - }; - this.onTextareaChange = value => { - if (value.length > 3000) { - this.setState({ - descriptionInvalid: true - }); - value = value.substring(0, 3000); - } else if (this.state.descriptionInvalid) { - this.setState({ - descriptionInvalid: false - }); - } - this.handleChange('description', value); - }; - this.onStartDateSelect = () => { - this.datepickerRefs.endDateTime.selectDate(new Date(this.state.startDateTime + this.interval)); - }; - this.onEndDateSelect = () => { - const { - startDateTime, - endDateTime - } = this.state; - if (endDateTime < startDateTime) { - if (endDateTime < Date.now()) { - return this.setState({ - endDateTime: startDateTime + this.interval - }); - } - this.handleDateSelect({ - startDateTime: endDateTime - this.interval - }); - } - }; - this.handleToggle = prop => { - return Object.keys(this.state).includes(prop) && this.setState(state => ({ - [prop]: !state[prop], - isDirty: true - })); - }; - this.handleChange = (prop, value) => { - return Object.keys(this.state).includes(prop) && this.setState({ - [prop]: value, - isDirty: true - }); - }; - this.handleDateSelect = ({ - startDateTime, - endDateTime - }, callback) => { - this.setState(state => ({ - startDateTime: startDateTime || state.startDateTime, - endDateTime: endDateTime || state.endDateTime, - isDirty: true - }), () => { - const { - recurring - } = this.state; - if (recurring && recurring.end) { - const recurringEnd = (0,helpers.PS)(this.state.startDateTime, 6); - this.datepickerRefs.recurringEnd.selectDate(new Date(recurringEnd)); - } - if (callback) { - callback(); - } - }); - }; - this.handleTimeSelect = ({ - startDateTime, - endDateTime - }) => { - startDateTime = startDateTime || this.state.startDateTime; - endDateTime = endDateTime || this.state.endDateTime; - this.setState(state => { - return { - startDateTime: endDateTime <= state.startDateTime ? endDateTime - this.interval : startDateTime, - endDateTime: startDateTime >= state.endDateTime ? startDateTime + this.interval : endDateTime, - isDirty: true - }; - }); - }; - this.handleParticipantSelect = participants => { - return participants && Array.isArray(participants) && this.setState({ - participants, - isDirty: true - }, () => { - const domRef = this.domRef && this.domRef.current; - if (domRef) { - domRef.reinitialise(); - } - }); - }; - this.handleSubmit = () => { - if (this.state.topic) { - return this.setState({ - isLoading: true - }, async () => { - const { - chatRoom, - onClose - } = this.props; - const params = [this.state, chatRoom]; - if (chatRoom) { - delay('chat-event-sm-edit-meeting', () => eventlog(99923)); - } else { - delay('chat-event-sm-button-create', () => eventlog(99922)); - } - delay('chat-events-sm-settings', () => this.submitStateEvents({ - ...this.state - })); - await megaChat.plugins.meetingsManager[chatRoom ? 'updateMeeting' : 'createMeeting'](...params); - this.setState({ - isLoading: false - }, () => { - onClose(); - megaChat.trigger(conversations_EVENTS.NAV_RENDER_VIEW, VIEWS.MEETINGS); - }); - }); - } - return this.setState({ - topicInvalid: true, - invalidTopicMsg: l.schedule_title_missing - }); - }; - } - syncPublicLink() { - if (this.state.isEdit) { - const { - chatRoom - } = this.props; - chatRoom.updatePublicHandle().then(() => this.isMounted() && this.setState({ - link: !!chatRoom.publicLink - })).catch(dump); - } - } - getFilteredTimeIntervals(timestamp, offsetFrom) { - const timeIntervals = (0,helpers.a4)(timestamp, offsetFrom); - const { - end - } = this.scheduledMeetingRef || {}; - if (this.state.isEdit && end < Date.now()) { - return timeIntervals; - } - return timeIntervals.filter(o => { - return offsetFrom ? o.value > this.nearestHalfHour : o.value > Date.now(); - }); - } - submitStateEvents(state) { - if (state.link) { - eventlog(500162); - } - if (state.sendInvite) { - eventlog(500163); - } - if (state.waitingRoom) { - eventlog(500164); - } - if (state.openInvite) { - eventlog(500165); - } - if (state.description) { - eventlog(500166); - } - if (state.recurring) { - eventlog(500167); - } else { - eventlog(500168); - } - eventlog(500169, state.topic.length); - } - componentWillUnmount() { - super.componentWillUnmount(); - if ($.dialog === Schedule.dialogName) { - closeDialog(); - } - [document, this.localStreamRef].map(el => $(el).unbind(`.${Schedule.NAMESPACE}`)); - megaChat.off(this.incomingCallListener); - } - UNSAFE_componentWillMount() { - const { - chatRoom - } = this.props; - if (chatRoom) { - const { - scheduledMeeting, - publicLink, - options - } = chatRoom; - this.state.topic = scheduledMeeting.title; - this.state.startDateTime = scheduledMeeting.start; - this.state.endDateTime = scheduledMeeting.end; - this.state.timezone = scheduledMeeting.timezone || (0,helpers.dB)(); - this.state.recurring = scheduledMeeting.recurring; - this.state.participants = chatRoom.getParticipantsExceptMe(); - this.state.link = !!publicLink; - this.state.description = scheduledMeeting.description || ''; - this.state.sendInvite = scheduledMeeting.flags; - this.state.waitingRoom = options[chat_chatRoom.MCO_FLAGS.WAITING_ROOM]; - this.state.openInvite = options[chat_chatRoom.MCO_FLAGS.OPEN_INVITE]; - this.state.isEdit = true; - this.scheduledMeetingRef = scheduledMeeting; - } - } - componentDidMount() { - super.componentDidMount(); - this.syncPublicLink(); - if ($.dialog === 'onboardingDialog') { - closeDialog(); - } - M.safeShowDialog(Schedule.dialogName, () => { - if (!this.isMounted()) { - throw new Error(`${Schedule.dialogName} dialog: component ${Schedule.NAMESPACE} not mounted.`); - } - $(document).rebind(`keyup.${Schedule.NAMESPACE}`, ({ - keyCode, - target - }) => { - return this.state.closeDialog || target instanceof HTMLTextAreaElement ? null : keyCode === 13 && this.handleSubmit(); - }); - $(this.localStreamRef).rebind(`click.${Schedule.NAMESPACE}`, () => { - if (this.state.isDirty) { - this.handleToggle('closeDialog'); - return false; - } - }); - megaChat.rebind(this.incomingCallListener, () => { - if (this.isMounted()) { - this.setState({ - overlayed: true, - closeDialog: false - }); - megaChat.plugins.callManager2.rebind(this.ringingStoppedListener, () => { - megaChat.plugins.callManager2.off(this.ringingStoppedListener); - this.setState({ - overlayed: false - }); - fm_showoverlay(); - }); - } - }); - return $(`#${Schedule.NAMESPACE}`); - }); - } - componentDidUpdate(prevProps) { - if (prevProps.callExpanded && !this.props.callExpanded) { - if (!$.dialog) { - M.safeShowDialog(Schedule.dialogName, `#${Schedule.NAMESPACE}`); - } - fm_showoverlay(); - this.setState({ - closeDialog: false - }); - } - if (!prevProps.callExpanded && this.props.callExpanded) { - this.setState({ - closeDialog: false - }); - } - } - render() { - let _this$props$chatRoom; - const { - topic, - startDateTime, - endDateTime, - recurring, - participants, - link, - sendInvite, - waitingRoom, - openInvite, - description, - closeDialog, - isEdit, - isDirty, - isLoading, - topicInvalid, - invalidTopicMsg, - descriptionInvalid, - overlayed - } = this.state; - return REaCt().createElement(modalDialogs.A.ModalDialog, (0,esm_extends.A)({}, this.state, { - id: Schedule.NAMESPACE, - className: ` - ${closeDialog ? 'with-confirmation-dialog' : ''} - ${this.props.callExpanded || overlayed ? 'hidden' : ''} - `, - dialogName: Schedule.dialogName, - dialogType: "main", - onClose: () => isDirty ? this.handleToggle('closeDialog') : this.props.onClose() - }), REaCt().createElement(Header, { - chatRoom: isEdit && this.props.chatRoom - }), REaCt().createElement(perfectScrollbar.O, { - ref: this.domRef, - className: "fm-dialog-body", - options: { - suppressScrollX: true - } - }, REaCt().createElement(Input, { - name: "topic", - placeholder: l.schedule_title_input, - value: topic, - invalid: topicInvalid, - invalidMessage: invalidTopicMsg, - autoFocus: true, - isLoading, - onFocus: () => topicInvalid && this.setState({ - topicInvalid: false - }), - onChange: this.onTopicChange - }), REaCt().createElement(Row, { - className: `unencrypted-warning-row ${topicInvalid ? 'with-topic-err' : ''}` - }, REaCt().createElement(Column, null), REaCt().createElement(Column, null, REaCt().createElement("div", { - className: "unencrypted-warning" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-info" - }), REaCt().createElement("span", null, l.schedule_encryption_note)))), REaCt().createElement(Row, { - className: "start-aligned" - }, REaCt().createElement(Column, null, REaCt().createElement("i", { - className: "sprite-fm-mono icon-recents-filled" - })), REaCt().createElement("div", { - className: "schedule-date-container" - }, REaCt().createElement(DateTime, { - name: "startDateTime", - altField: "startTime", - datepickerRef: this.datepickerRefs.startDateTime, - startDate: startDateTime, - value: startDateTime, - filteredTimeIntervals: this.getFilteredTimeIntervals(startDateTime), - label: l.schedule_start_date, - isLoading, - onMount: datepicker => { - this.datepickerRefs.startDateTime = datepicker; - }, - onSelectDate: startDateTime => { - this.handleDateSelect({ - startDateTime - }, this.onStartDateSelect); - }, - onSelectTime: ({ - value: startDateTime - }) => this.handleTimeSelect({ - startDateTime - }), - onChange: value => this.handleChange('startDateTime', value), - onBlur: timestamp => { - if (timestamp) { - const startDateTime = timestamp < Date.now() ? this.nearestHalfHour : timestamp; - this.handleDateSelect({ - startDateTime - }, this.onStartDateSelect); - } - } - }), REaCt().createElement(DateTime, { - name: "endDateTime", - altField: "endTime", - datepickerRef: this.datepickerRefs.endDateTime, - isLoading, - startDate: endDateTime, - value: endDateTime, - filteredTimeIntervals: this.getFilteredTimeIntervals(endDateTime, startDateTime), - label: l.schedule_end_date, - onMount: datepicker => { - this.datepickerRefs.endDateTime = datepicker; - }, - onSelectDate: endDateTime => { - this.handleDateSelect({ - endDateTime - }, this.onEndDateSelect); - }, - onSelectTime: ({ - value: endDateTime - }) => this.handleTimeSelect({ - endDateTime - }), - onChange: value => this.handleChange('endDateTime', value), - onBlur: timestamp => { - this.handleDateSelect({ - endDateTime: timestamp - }, this.onEndDateSelect); - } - }))), !u_attr.p && endDateTime - startDateTime > 36e5 && REaCt().createElement(UpgradeNotice, { - onUpgradeClicked: () => { - this.props.onClose(); - loadSubPage('pro'); - eventlog(500258); - } - }), REaCt().createElement(Checkbox, { - name: "recurring", - checked: recurring, - label: l.schedule_recurring_label, - isLoading, - onToggle: prop => { - this.handleToggle(prop); - delay('chat-event-sm-recurring', () => eventlog(99919)); - } - }), recurring && REaCt().createElement(Recurring, { - chatRoom: this.props.chatRoom, - startDateTime, - endDateTime, - isLoading, - onMount: datepicker => { - this.datepickerRefs.recurringEnd = datepicker; - }, - onUpdate: state => { - this.setState({ - recurring: state - }); - } - }), REaCt().createElement(Row, null, REaCt().createElement(Column, null, REaCt().createElement("i", { - className: "sprite-fm-mono icon-contacts" - })), REaCt().createElement(Column, null, REaCt().createElement(Invite, { - className: isLoading ? 'disabled' : '', - isLoading, - participants, - onSelect: this.handleParticipantSelect - }))), REaCt().createElement(Switch, { - name: "link", - toggled: link, - label: l.schedule_link_label, - isLoading, - subLabel: l.schedule_link_info, - onToggle: prop => { - this.handleToggle(prop); - delay('chat-event-sm-meeting-link', () => eventlog(99920)); - } - }), REaCt().createElement(Checkbox, { - name: "sendInvite", - checked: sendInvite, - label: l.schedule_invite_label, - isLoading, - onToggle: prop => { - this.handleToggle(prop); - delay('chat-event-sm-calendar-invite', () => eventlog(99921)); - } - }), REaCt().createElement(Checkbox, { - name: "waitingRoom", - className: (_this$props$chatRoom = this.props.chatRoom) != null && _this$props$chatRoom.havePendingCall() ? 'disabled' : '', - checked: waitingRoom, - label: l.waiting_room, - subLabel: l.waiting_room_info, - isLoading, - onToggle: waitingRoom => { - let _this$props$chatRoom2; - if ((_this$props$chatRoom2 = this.props.chatRoom) != null && _this$props$chatRoom2.havePendingCall()) { - return; - } - this.handleToggle(waitingRoom); - delay('chat-event-sm-waiting-room', () => eventlog(500297)); - } - }), REaCt().createElement(Checkbox, { - name: "openInvite", - checked: openInvite, - label: l.open_invite_desc, - isLoading, - onToggle: ev => { - this.handleToggle(ev); - delay('chat-event-sm-open-invite', () => eventlog(500298)); - } - }), waitingRoom && openInvite ? REaCt().createElement(Row, null, REaCt().createElement("div", { - className: "schedule-dialog-banner warn" - }, REaCt().createElement(utils.P9, null, l.waiting_room_invite.replace('[A]', ` - `).replace('[/A]', '')))) : null, REaCt().createElement(Textarea, { - name: "description", - isLoading, - invalid: descriptionInvalid, - placeholder: l.schedule_description_input, - value: description, - onFocus: () => descriptionInvalid && this.setState({ - descriptionInvalid: false - }), - onChange: this.onTextareaChange - })), REaCt().createElement(Footer, { - isLoading, - isEdit, - topic, - onSubmit: this.handleSubmit - }), !(overlayed || this.props.callExpanded) && closeDialog && REaCt().createElement(CloseDialog, { - onToggle: this.handleToggle, - onClose: this.props.onClose - })); - } -} -_Schedule = Schedule; -Schedule.NAMESPACE = 'schedule-dialog'; -Schedule.dialogName = `meetings-${_Schedule.NAMESPACE}`; -window.ScheduleMeetingDialogUI = { - Schedule -}; -const CloseDialog = ({ - onToggle, - onClose -}) => { - return REaCt().createElement(REaCt().Fragment, null, REaCt().createElement(modalDialogs.A.ModalDialog, { - name: `${Schedule.NAMESPACE}-confirmation`, - dialogType: "message", - className: ` - with-close-btn - ${Schedule.NAMESPACE}-confirmation - `, - title: l.schedule_discard_dlg_title, - icon: "sprite-fm-uni icon-question", - buttons: [{ - key: 'n', - label: l.schedule_discard_cancel, - onClick: () => onToggle('closeDialog') - }, { - key: 'y', - label: l.schedule_discard_confirm, - className: 'positive', - onClick: onClose - }], - noCloseOnClickOutside: true, - stopKeyPropagation: true, - hideOverlay: true, - onClose: () => onToggle('closeDialog') - }), REaCt().createElement("div", { - className: `${Schedule.NAMESPACE}-confirmation-overlay`, - onClick: () => onToggle('closeDialog') - })); -}; -const Row = ({ - children, - className -}) => REaCt().createElement("div", { - className: ` - ${Schedule.NAMESPACE}-row - ${className || ''} - ` -}, children); -const Column = ({ - children, - className -}) => REaCt().createElement("div", { - className: ` - ${Schedule.NAMESPACE}-column - ${className || ''} - ` -}, children); -const Header = ({ - chatRoom -}) => { - const $$container = title => REaCt().createElement("header", null, REaCt().createElement("h2", null, title)); - if (chatRoom) { - const { - scheduledMeeting - } = chatRoom; - return $$container(scheduledMeeting.isRecurring ? l.edit_meeting_series_title : l.edit_meeting_title); - } - return $$container(l.schedule_meeting_title); -}; -const Input = ({ - name, - placeholder, - value, - invalid, - invalidMessage, - autoFocus, - isLoading, - onFocus, - onChange -}) => { - return REaCt().createElement(Row, { - className: invalid ? 'invalid-aligned' : '' - }, REaCt().createElement(Column, null, REaCt().createElement("i", { - className: "sprite-fm-mono icon-rename" - })), REaCt().createElement(Column, null, REaCt().createElement("div", { - className: ` - mega-input - ${invalid ? 'error msg' : ''} - ` - }, REaCt().createElement("input", { - type: "text", - name: `${Schedule.NAMESPACE}-${name}`, - className: isLoading ? 'disabled' : '', - disabled: isLoading, - autoFocus, - autoComplete: "off", - placeholder, - value, - onFocus, - onChange: ({ - target - }) => onChange(target.value) - }), invalid && REaCt().createElement("div", { - className: "message-container mega-banner" - }, invalidMessage)))); -}; -const Checkbox = ({ - name, - className, - checked, - label, - subLabel, - isLoading, - onToggle -}) => { - return REaCt().createElement(Row, { - className: ` - ${subLabel ? 'start-aligned' : ''} - ${className || ''} - ` - }, REaCt().createElement(Column, null, REaCt().createElement("div", { - className: ` - checkdiv - ${checked ? 'checkboxOn' : 'checkboxOff'} - ${isLoading ? 'disabled' : ''} - ` - }, REaCt().createElement("input", { - name: `${Schedule.NAMESPACE}-${name}`, - disabled: isLoading, - type: "checkbox", - onChange: () => onToggle(name) - }))), REaCt().createElement(Column, { - className: subLabel ? 'with-sub-label' : '' - }, REaCt().createElement("label", { - htmlFor: `${Schedule.NAMESPACE}-${name}`, - className: isLoading ? 'disabled' : '', - onClick: () => isLoading ? null : onToggle(name) - }, label), subLabel && REaCt().createElement("div", { - className: "sub-label" - }, subLabel))); -}; -const Switch = ({ - name, - toggled, - label, - isLoading, - subLabel, - onToggle -}) => { - const className = `${Schedule.NAMESPACE}-switch`; - return REaCt().createElement(Row, null, REaCt().createElement(Column, null, REaCt().createElement("i", { - className: "sprite-fm-uni icon-mega-logo" - })), REaCt().createElement(Column, { - className: subLabel ? `with-sub-label ${className}` : className - }, REaCt().createElement("span", { - className: ` - schedule-label - ${isLoading ? 'disabled' : ''} - `, - onClick: () => isLoading ? null : onToggle(name) - }, label), REaCt().createElement("div", { - className: ` - mega-switch - ${toggled ? 'toggle-on' : ''} - ${isLoading ? 'disabled' : ''} - `, - onClick: () => isLoading ? null : onToggle(name) - }, REaCt().createElement("div", { - className: ` - mega-feature-switch - sprite-fm-mono-after - ${toggled ? 'icon-check-after' : 'icon-minimise-after'} - ` - })), subLabel && REaCt().createElement("div", { - className: "sub-label" - }, subLabel))); -}; -const Textarea = ({ - name, - placeholder, - isLoading, - value, - invalid, - onChange, - onFocus -}) => { - return REaCt().createElement(Row, { - className: "start-aligned" - }, REaCt().createElement(Column, null, REaCt().createElement("i", { - className: "sprite-fm-mono icon-description" - })), REaCt().createElement(Column, null, REaCt().createElement("div", { - className: `mega-input box-style textarea ${invalid ? 'error' : ''}` - }, REaCt().createElement("textarea", { - name: `${Schedule.NAMESPACE}-${name}`, - className: isLoading ? 'disabled' : '', - placeholder, - value, - readOnly: isLoading, - onChange: ({ - target - }) => onChange(target.value), - onFocus - })), invalid && REaCt().createElement("div", { - className: "mega-input error msg textarea-error" - }, REaCt().createElement("div", { - className: "message-container mega-banner" - }, l.err_schedule_desc_long)))); -}; -const Footer = ({ - isLoading, - isEdit, - topic, - onSubmit -}) => { - return REaCt().createElement("footer", null, REaCt().createElement("div", { - className: "footer-container" - }, REaCt().createElement(meetings_button.A, { - className: ` - mega-button - positive - ${isLoading ? 'disabled' : ''} - `, - onClick: () => isLoading ? null : onSubmit(), - topic - }, REaCt().createElement("span", null, isEdit ? l.update_meeting_button : l.schedule_meeting_button)))); -}; -const UpgradeNotice = ({ - onUpgradeClicked -}) => { - return !!mega.flags.ff_chmon && REaCt().createElement(Row, { - className: "schedule-upgrade-notice" - }, REaCt().createElement("h3", null, l.schedule_limit_title), REaCt().createElement("div", null, l.schedule_limit_upgrade_features), REaCt().createElement(meetings_button.A, { - className: "mega-button positive", - onClick: onUpgradeClicked - }, REaCt().createElement("span", null, l.upgrade_now))); -}; -// EXTERNAL MODULE: ./js/ui/miniui.jsx -const miniui = REQ_(818); -;// ./js/chat/ui/startGroupChatWizard.jsx -const React = REQ_(594); - - - - -class StartGroupChatWizard extends mixins.w9 { - constructor(props) { - super(props); - this.dialogName = 'start-group-chat'; - this.domRef = React.createRef(); - this.inputContainerRef = React.createRef(); - this.inputRef = React.createRef(); - let haveContacts = false; - const keys = M.u.keys(); - for (let i = 0; i < keys.length; i++) { - if (M.u[keys[i]].c === 1) { - haveContacts = true; - break; - } - } - this.state = { - 'selected': this.props.selected ? this.props.selected : [], - haveContacts, - 'step': this.props.flowType === 2 || !haveContacts ? 1 : 0, - 'keyRotation': false, - 'createChatLink': this.props.flowType === 2, - 'groupName': '', - openInvite: 1 - }; - this.onFinalizeClick = this.onFinalizeClick.bind(this); - this.onSelectClicked = this.onSelectClicked.bind(this); - this.onSelected = this.onSelected.bind(this); - } - onSelected(nodes) { - this.setState({ - 'selected': nodes - }); - if (this.props.onSelected) { - this.props.onSelected(nodes); - } - } - onSelectClicked() { - if (this.props.onSelectClicked) { - this.props.onSelectClicked(); - } - } - onFinalizeClick(e) { - if (e) { - e.preventDefault(); - e.stopPropagation(); - } - const { - groupName, - selected, - keyRotation, - createChatLink, - openInvite - } = this.state; - megaChat.createAndShowGroupRoomFor(selected, groupName.trim(), { - keyRotation, - createChatLink: keyRotation ? false : createChatLink, - openInvite - }); - this.props.onClose(this); - eventlog(500236); - } - componentDidMount() { - super.componentDidMount(); - if (!this.props.subDialog) { - M.safeShowDialog(this.dialogName, nop); - } - } - componentWillUnmount() { - super.componentWillUnmount(); - if ($.dialog === this.dialogName) { - closeDialog(); - } - } - render() { - const self = this; - const classes = `new-group-chat contrast small-footer contact-picker-widget ${ self.props.className}`; - let contacts = M.u; - const {haveContacts} = self.state; - const buttons = []; - let allowNext = false; - let failedToEnableChatlink = self.state.failedToEnableChatlink && self.state.createChatLink === true && !self.state.groupName; - if (self.state.keyRotation) { - failedToEnableChatlink = false; - } - let extraContent; - if (this.props.extraContent) { - self.state.step = 0; - extraContent = React.createElement("div", { - className: "content-block imported" - }); - } else if (self.state.step === 0 && haveContacts) { - allowNext = true; - buttons.push({ - "label": self.props.cancelLabel, - "key": "cancel", - "onClick" (e) { - self.props.onClose(self); - e.preventDefault(); - e.stopPropagation(); - } - }); - buttons.push({ - "label": l[556], - "key": "next", - "className": !allowNext ? "disabled positive" : "positive", - "onClick" (e) { - e.preventDefault(); - e.stopPropagation(); - self.setState({ - 'step': 1 - }); - } - }); - } else if (self.state.step === 1) { - allowNext = self.state.createChatLink ? !failedToEnableChatlink : true; - contacts = []; - self.state.selected.forEach((h) => { - if (h in M.u) { - contacts.push(M.u[h]); - } - }); - if (!haveContacts || this.props.flowType === 2) { - buttons.push({ - "label": self.props.cancelLabel, - "key": "cancel", - "onClick" (e) { - self.props.onClose(self); - e.preventDefault(); - e.stopPropagation(); - } - }); - } else { - buttons.push({ - "label": l[822], - "key": "back", - "onClick" (e) { - e.preventDefault(); - e.stopPropagation(); - self.setState({ - 'step': 0 - }); - } - }); - } - buttons.push({ - "label": l[726], - "key": "done", - "className": !allowNext ? "positive disabled" : "positive", - "onClick" (e) { - if (self.state.createChatLink === true && !self.state.groupName) { - self.setState({ - 'failedToEnableChatlink': true - }); - } else { - self.onFinalizeClick(e); - } - } - }); - } - let chatInfoElements; - if (self.state.step === 1) { - let _this$state$groupName; - let checkboxClassName = self.state.createChatLink ? "checkboxOn" : "checkboxOff"; - if (failedToEnableChatlink && self.state.createChatLink) { - checkboxClassName += " intermediate-state"; - } - if (self.state.keyRotation) { - checkboxClassName = "checkboxOff"; - } - chatInfoElements = React.createElement(React.Fragment, null, React.createElement("div", { - className: ` - contacts-search-header left-aligned top-pad - ${failedToEnableChatlink ? 'failed' : ''} - ` - }, React.createElement("div", { - className: ` - mega-input - with-icon - box-style - ${((_this$state$groupName = this.state.groupName) == null ? void 0 : _this$state$groupName.length) > 0 ? 'valued' : ''} - ${failedToEnableChatlink ? 'error msg' : ''} - `, - ref: this.inputContainerRef - }, React.createElement("i", { - className: "sprite-fm-mono icon-channel-new" - }), React.createElement("input", { - autoFocus: true, - className: "megaInputs", - type: "text", - ref: this.inputRef, - placeholder: l[18509], - value: this.state.groupName, - maxLength: ChatRoom.TOPIC_MAX_LENGTH, - onKeyDown: e => { - const code = e.which || e.keyCode; - if (allowNext && code === 13 && self.state.step === 1) { - this.onFinalizeClick(); - } - }, - onChange: e => { - const containerRef = this.inputContainerRef.current; - const { - value - } = e.target; - containerRef.classList[value.length > 0 ? 'add' : 'remove']('valued'); - this.setState({ - groupName: value, - failedToEnableChatlink: false - }); - } - }))), this.props.flowType === 2 ? null : React.createElement("div", { - className: "group-chat-dialog content" - }, React.createElement(miniui.A.ToggleCheckbox, { - className: "rotation-toggle", - checked: this.state.keyRotation, - onToggle: keyRotation => this.setState({ - keyRotation - }, () => this.inputRef.current.focus()) - }), React.createElement("div", { - className: "group-chat-dialog header" - }, l[20576]), React.createElement("div", { - className: "group-chat-dialog description" - }, l[20484]), React.createElement(miniui.A.ToggleCheckbox, { - className: "open-invite-toggle", - checked: this.state.openInvite, - value: this.state.openInvite, - onToggle: openInvite => this.setState({ - openInvite - }, () => this.inputRef.current.focus()) - }), React.createElement("div", { - className: "group-chat-dialog header" - }, l.open_invite_label), React.createElement("div", { - className: "group-chat-dialog description" - }, l.open_invite_desc), React.createElement("div", { - className: ` - group-chat-dialog checkbox - ${this.state.keyRotation ? 'disabled' : ''} - ${failedToEnableChatlink ? 'failed' : ''} - `, - onClick: () => { - delay('chatWizard-createChatLink', () => { - this.setState(state => ({ - createChatLink: !state.createChatLink - })); - this.inputRef.current.focus(); - }, 100); - } - }, React.createElement("div", { - className: `checkdiv ${checkboxClassName}` - }, React.createElement("input", { - type: "checkbox", - name: "group-encryption", - id: "group-encryption", - className: "checkboxOn hidden" - })), React.createElement("label", { - htmlFor: "group-encryption", - className: "radio-txt lato mid" - }, l[20575]), React.createElement("div", { - className: "clear" - }))), failedToEnableChatlink ? React.createElement("div", { - className: "group-chat-dialog description chatlinks-intermediate-msg" - }, l[20573]) : null); - } - return React.createElement(modalDialogs.A.ModalDialog, { - step: self.state.step, - title: this.props.flowType === 2 && self.state.createChatLink ? l[20638] : this.props.customDialogTitle || l[19483], - className: classes, - dialogType: "tool", - dialogName: "group-chat-dialog", - showSelectedNum: self.props.showSelectedNum, - selectedNum: self.state.selected.length, - closeDlgOnClickOverlay: self.props.closeDlgOnClickOverlay, - onClose: () => { - self.props.onClose(self); - }, - popupDidMount: elem => { - if (this.props.extraContent) { - let _elem$querySelector; - (_elem$querySelector = elem.querySelector('.content-block.imported')) == null || _elem$querySelector.appendChild(this.props.extraContent); - } - if (this.props.onExtraContentDidMount) { - this.props.onExtraContentDidMount(elem); - } - }, - triggerResizeOnUpdate: true, - buttons - }, React.createElement("div", { - ref: this.domRef, - className: "content-block" - }, chatInfoElements, React.createElement(ui_contacts.ContactPickerWidget, { - step: self.state.step, - exclude: self.props.exclude, - contacts, - selectableContacts: "true", - onSelectDone: self.onSelectClicked, - onSelected: self.onSelected, - selected: self.state.selected, - headerClasses: "left-aligned", - multiple: true, - readOnly: self.state.step !== 0, - allowEmpty: true, - showMeAsSelected: self.state.step === 1, - className: self.props.pickerClassName, - disableFrequents: self.props.disableFrequents, - skipMailSearch: self.props.skipMailSearch, - autoFocusSearchField: self.props.autoFocusSearchField, - selectCleanSearchRes: self.props.selectCleanSearchRes, - disableDoubleClick: self.props.disableDoubleClick, - selectedWidthSize: self.props.selectedWidthSize, - emptySelectionMsg: self.props.emptySelectionMsg, - newEmptySearchResult: self.props.newEmptySearchResult, - newNoContact: self.props.newNoContact, - highlightSearchValue: self.props.highlightSearchValue, - emailTooltips: self.props.emailTooltips - })), extraContent); - } -} -StartGroupChatWizard.clickTime = 0; -StartGroupChatWizard.defaultProps = { - 'selectLabel': l[1940], - 'cancelLabel': l.msg_dlg_cancel, - 'hideable': true, - 'flowType': 1, - 'pickerClassName': '', - 'showSelectedNum': false, - 'disableFrequents': false, - 'skipMailSearch': false, - 'autoFocusSearchField': true, - 'selectCleanSearchRes': true, - 'disableDoubleClick': false, - 'newEmptySearchResult': false, - 'newNoContact': false, - 'closeDlgOnClickOverlay': true, - 'emailTooltips': false -}; -window.StartGroupChatDialogUI = { - StartGroupChatWizard -}; -const startGroupChatWizard = { - StartGroupChatWizard -}; -// EXTERNAL MODULE: ./js/chat/ui/meetings/call.jsx + 11 modules -const call = REQ_(3); -// EXTERNAL MODULE: ./js/chat/ui/chatToaster.jsx -const chatToaster = REQ_(424); -;// ./js/chat/ui/searchPanel/resultTable.jsx - -const ResultTable = ({ - heading, - children -}) => { - return REaCt().createElement("div", { - className: `result-table ${heading ? '' : 'nil'}` - }, heading ? REaCt().createElement("div", { - className: "result-table-heading" - }, heading) : null, children); -}; -const resultTable = ResultTable; -;// ./js/chat/ui/searchPanel/resultRow.jsx - - - - - - - - -const RESULT_ROW_CLASS = 'result-table-row'; -const USER_CARD_CLASS = 'user-card'; -const roomIsGroup = room => room && room.type === 'group' || room.type === 'public'; -const openResult = ({ - room, - messageId, - index -}, callback) => { - document.dispatchEvent(new Event(EVENTS.RESULT_OPEN)); - if (isString(room)) { - loadSubPage(`fm/chat/p/${room}`); - } else if (room && room.chatId && !messageId) { - const chatRoom = megaChat.getChatById(room.chatId); - if (chatRoom) { - loadSubPage(chatRoom.getRoomUrl()); - } else { - loadSubPage(`/fm/chat/contacts/${room.chatId}`); - } - } else { - loadSubPage(room.getRoomUrl()); - if (messageId) { - room.scrollToMessageId(messageId, index); - } - } - return callback && typeof callback === 'function' && callback(); -}; -const lastActivity = room => { - if (!room.lastActivity || !room.ctime) { - room = megaChat.getChatById(room.chatId); - } - if (room && room.lastActivity || room.ctime) { - return room.lastActivity ? todayOrYesterday(room.lastActivity * 1000) ? getTimeMarker(room.lastActivity) : time2date(room.lastActivity, 17) : todayOrYesterday(room.ctime * 1000) ? getTimeMarker(room.ctime) : time2date(room.ctime, 17); - } - return l[8000]; -}; -class MessageRow extends mixins.w9 { - render() { - const { - data, - matches, - room, - index, - onResultOpen - } = this.props; - const isGroup = roomIsGroup(room); - const contact = room.getParticipantsExceptMe(); - const summary = room.messagesBuff.getRenderableSummary(data); - const date = todayOrYesterday(data.delay * 1000) ? getTimeMarker(data.delay) : time2date(data.delay, 17); - return REaCt().createElement("div", { - ref: node => { - this.domRef = node; - }, - className: ` - ${RESULT_ROW_CLASS} - message - `, - onClick: () => openResult({ - room, - messageId: data.messageId, - index - }, () => onResultOpen(this.domRef)) - }, REaCt().createElement("div", { - className: "message-result-avatar" - }, isGroup && REaCt().createElement("div", { - className: "chat-topic-icon" - }, REaCt().createElement("i", { - className: "sprite-fm-uni icon-chat-group" - })), room.isNote && REaCt().createElement("div", { - className: "note-chat-signifier" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-file-text-thin-outline note-chat-icon" - })), room.type === 'private' && REaCt().createElement(ui_contacts.Avatar, { - contact: M.u[contact] - })), REaCt().createElement("div", { - className: "user-card" - }, REaCt().createElement("span", { - className: "title" - }, isGroup && REaCt().createElement(utils.sp, null, room.getRoomTitle()), room.isNote && REaCt().createElement("span", null, l.note_label), room.type === 'private' && REaCt().createElement(ui_contacts.ContactAwareName, { - contact: M.u[contact], - overflow: true - })), isGroup ? null : REaCt().createElement(ui_contacts.ContactPresence, { - contact: M.u[contact] - }), REaCt().createElement("div", { - className: "clear" - }), REaCt().createElement("div", { - className: "message-result-info" - }, REaCt().createElement("div", { - className: "summary" - }, REaCt().createElement(utils.oM, { - content: megaChat.highlight(summary, matches, true) - })), REaCt().createElement("div", { - className: "result-separator" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-dot" - })), REaCt().createElement("span", { - className: "date" - }, date)))); - } -} -class ChatRow extends mixins.w9 { - render() { - const { - room, - matches, - onResultOpen - } = this.props; - const result = megaChat.highlight(megaChat.html(room.getRoomTitle()), matches, true); - return REaCt().createElement("div", { - ref: node => { - this.domRef = node; - }, - className: RESULT_ROW_CLASS, - onClick: () => openResult({ - room - }, () => onResultOpen(this.domRef)) - }, REaCt().createElement("div", { - className: "chat-topic-icon" - }, REaCt().createElement("i", { - className: "sprite-fm-uni icon-chat-group" - })), REaCt().createElement("div", { - className: USER_CARD_CLASS - }, REaCt().createElement("div", { - className: "graphic" - }, REaCt().createElement(utils.oM, null, result)), REaCt().createElement("div", { - className: "result-last-activity" - }, lastActivity(room))), REaCt().createElement("div", { - className: "clear" - })); - } -} -class MemberRow extends mixins.w9 { - render() { - const { - data, - matches, - room, - contact, - onResultOpen - } = this.props; - const isGroup = room && roomIsGroup(room); - return REaCt().createElement("div", { - ref: node => { - this.domRef = node; - }, - className: RESULT_ROW_CLASS, - onClick: () => openResult({ - room: room || contact.h - }, () => onResultOpen(this.domRef)) - }, isGroup ? REaCt().createElement("div", { - className: "chat-topic-icon" - }, REaCt().createElement("i", { - className: "sprite-fm-uni icon-chat-group" - })) : REaCt().createElement(ui_contacts.Avatar, { - contact - }), REaCt().createElement("div", { - className: USER_CARD_CLASS - }, REaCt().createElement("div", { - className: "graphic" - }, isGroup ? REaCt().createElement(utils.oM, null, megaChat.highlight(megaChat.html(room.getRoomTitle()), matches, true)) : REaCt().createElement(REaCt().Fragment, null, REaCt().createElement(utils.oM, null, megaChat.highlight(megaChat.html(nicknames.getNickname(data)), matches, true)), REaCt().createElement(ui_contacts.ContactPresence, { - contact - }))), lastActivity(room)), REaCt().createElement("div", { - className: "clear" - })); - } -} -const NilRow = ({ - onSearchMessages, - isFirstQuery -}) => { - const label = LABEL.SEARCH_MESSAGES_INLINE.replace('[A]', '').replace('[/A]', ''); - return REaCt().createElement("div", { - className: ` - ${RESULT_ROW_CLASS} - nil - ` - }, REaCt().createElement("div", { - className: "nil-container" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-preview-reveal" - }), REaCt().createElement("span", null, LABEL.NO_RESULTS), isFirstQuery && REaCt().createElement("div", { - className: "search-messages", - onClick: onSearchMessages - }, REaCt().createElement(utils.oM, { - tag: "div", - content: label - })))); -}; -class ResultRow extends mixins.w9 { - constructor(...args) { - super(...args); - this.setActive = nodeRef => { - if (nodeRef) { - const elements = document.querySelectorAll(`.${RESULT_ROW_CLASS}.${"active"}`); - for (let i = elements.length; i--;) { - elements[i].classList.remove('active'); - } - nodeRef.classList.add("active"); - } - }; - } - render() { - const { - type, - result, - children, - onSearchMessages, - isFirstQuery - } = this.props; - if (result) { - const { - data, - index, - matches, - room - } = result; - const PROPS = { - data, - index, - matches, - room, - onResultOpen: this.setActive - }; - switch (type) { - case TYPE.MESSAGE: - return REaCt().createElement(MessageRow, PROPS); - case TYPE.CHAT: - return REaCt().createElement(ChatRow, PROPS); - case TYPE.MEMBER: - return REaCt().createElement(MemberRow, (0,esm_extends.A)({}, PROPS, { - contact: M.u[data] - })); - default: - return REaCt().createElement("div", { - className: RESULT_ROW_CLASS - }, children); - } - } - return REaCt().createElement(NilRow, { - onSearchMessages, - isFirstQuery - }); - } -} -;// ./js/chat/ui/searchPanel/resultContainer.jsx - - - - -const TYPE = { - MESSAGE: 1, - CHAT: 2, - MEMBER: 3, - NIL: 4 -}; -const LABEL = { - MESSAGES: l[6868], - CONTACTS_AND_CHATS: l[20174], - NO_RESULTS: l[8674], - SEARCH_MESSAGES_CTA: l[23547], - SEARCH_MESSAGES_INLINE: l[23548], - DECRYPTING_RESULTS: l[23543], - PAUSE_SEARCH: l[23544], - SEARCH_PAUSED: l[23549], - SEARCH_COMPLETE: l[23546] -}; -class ResultContainer extends REaCt().Component { - constructor(...args) { - super(...args); - this.renderResults = (results, status, isFirstQuery, onSearchMessages) => { - if (status === STATUS.COMPLETED && results.length < 1) { - return REaCt().createElement(resultTable, null, REaCt().createElement(ResultRow, { - type: TYPE.NIL, - isFirstQuery, - onSearchMessages - })); - } - const RESULT_TABLE = { - CONTACTS_AND_CHATS: [], - MESSAGES: [] - }; - for (const resultTypeGroup in results) { - if (results.hasOwnProperty(resultTypeGroup)) { - const len = results[resultTypeGroup].length; - for (let i = 0; i < len; i++) { - const result = results[resultTypeGroup].getItem(i); - const { - MESSAGE, - MEMBER, - CHAT - } = TYPE; - const { - resultId, - type - } = result; - const table = type === MESSAGE ? 'MESSAGES' : 'CONTACTS_AND_CHATS'; - RESULT_TABLE[table] = [...RESULT_TABLE[table], REaCt().createElement(ResultRow, { - key: resultId, - type: type === MESSAGE ? MESSAGE : type === MEMBER ? MEMBER : CHAT, - result - })]; - } - } - } - return Object.keys(RESULT_TABLE).map((key, index) => { - const table = { - ref: RESULT_TABLE[key], - hasRows: RESULT_TABLE[key] && RESULT_TABLE[key].length, - isEmpty: RESULT_TABLE[key] && RESULT_TABLE[key].length < 1, - props: { - key: index, - heading: key === 'MESSAGES' ? LABEL.MESSAGES : LABEL.CONTACTS_AND_CHATS - } - }; - if (table.hasRows) { - return REaCt().createElement(resultTable, table.props, table.ref.map(row => row)); - } - if (status === STATUS.COMPLETED && key === 'MESSAGES') { - const SEARCH_MESSAGES = REaCt().createElement("button", { - className: "search-messages mega-button", - onClick: onSearchMessages - }, REaCt().createElement("span", null, LABEL.SEARCH_MESSAGES_CTA)); - const NO_RESULTS = REaCt().createElement(ResultRow, { - type: TYPE.NIL, - isFirstQuery, - onSearchMessages - }); - return REaCt().createElement(resultTable, table.props, isFirstQuery ? SEARCH_MESSAGES : NO_RESULTS); - } - return null; - }); - }; - } - render() { - const { - results, - status, - isFirstQuery, - onSearchMessages - } = this.props; - return this.renderResults(results, status, isFirstQuery, onSearchMessages); - } -} -;// ./js/chat/ui/searchPanel/searchField.jsx -let _SearchField; - - - - -const SEARCH_STATUS_CLASS = 'search-field-status'; -const BASE_ICON_CLASS = 'sprite-fm-mono'; -class SearchField extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - this.state = { - hovered: false - }; - this.renderStatusBanner = () => { - switch (this.props.status) { - case STATUS.IN_PROGRESS: - return REaCt().createElement("div", { - className: `${SEARCH_STATUS_CLASS} searching info` - }, LABEL.DECRYPTING_RESULTS); - case STATUS.PAUSED: - return REaCt().createElement("div", { - className: `${SEARCH_STATUS_CLASS} paused info` - }, LABEL.SEARCH_PAUSED); - case STATUS.COMPLETED: - return REaCt().createElement("div", { - className: `${SEARCH_STATUS_CLASS} complete success` - }, LABEL.SEARCH_COMPLETE); - default: - return null; - } - }; - this.renderStatusControls = () => { - const { - status, - onToggle - } = this.props; - const handleHover = () => this.setState(state => ({ - hovered: !state.hovered - })); - switch (status) { - case STATUS.IN_PROGRESS: - return REaCt().createElement("div", { - className: "progress-controls", - onClick: onToggle - }, REaCt().createElement("i", { - className: `${BASE_ICON_CLASS} icon-pause` - })); - case STATUS.PAUSED: - return REaCt().createElement("i", { - className: `${BASE_ICON_CLASS} icon-resume`, - onClick: onToggle, - onMouseOver: handleHover, - onMouseOut: handleHover - }); - case STATUS.COMPLETED: - return null; - default: - return null; - } - }; - } - componentDidMount() { - super.componentDidMount(); - SearchField.focus(); - } - render() { - const { - value, - searching, - status, - onChange, - onReset - } = this.props; - return REaCt().createElement("div", { - ref: this.domRef, - className: "search-field" - }, REaCt().createElement("i", { - className: `${BASE_ICON_CLASS} icon-preview-reveal search-icon-find` - }), REaCt().createElement("input", { - type: "search", - autoComplete: "off", - placeholder: l[102], - ref: SearchField.inputRef, - value, - onChange: ev => { - if (this.state.hovered) { - this.setState({ - hovered: false - }); - } - onChange(ev); - } - }), searching && REaCt().createElement("i", { - className: ` - ${BASE_ICON_CLASS} - icon-close-component - search-icon-reset - `, - onClick: onReset - }), searching && status && REaCt().createElement(REaCt().Fragment, null, this.renderStatusControls(), this.renderStatusBanner())); - } -} -_SearchField = SearchField; -SearchField.inputRef = REaCt().createRef(); -SearchField.select = () => { - const inputElement = _SearchField.inputRef && _SearchField.inputRef.current; - const value = inputElement && inputElement.value; - if (inputElement && value) { - inputElement.selectionStart = 0; - inputElement.selectionEnd = value.length; - } -}; -SearchField.focus = () => _SearchField.inputRef && _SearchField.inputRef.current && _SearchField.inputRef.current.focus(); -SearchField.hasValue = () => _SearchField.inputRef && _SearchField.inputRef.current && !!_SearchField.inputRef.current.value.length; -SearchField.isVisible = () => _SearchField.inputRef && _SearchField.inputRef.current && elementIsVisible(_SearchField.inputRef.current); -;// ./js/chat/ui/searchPanel/searchPanel.jsx - - - - - -const STATUS = { - IN_PROGRESS: 1, - PAUSED: 2, - COMPLETED: 3 -}; -const EVENTS = { - RESULT_OPEN: 'chatSearchResultOpen', - KEYDOWN: 'keydown' -}; -const ACTIONS = { - PAUSE: 'pause', - RESUME: 'resume' -}; -const SEARCH_PANEL_CLASS = `search-panel`; -class SearchPanel extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - this.wrapperRef = null; - this.state = { - value: '', - searching: false, - status: undefined, - isFirstQuery: true, - results: [] - }; - this.unbindEvents = () => { - if (this.pageChangeListener) { - mBroadcaster.removeListener(this.pageChangeListener); - } - document.removeEventListener(EVENTS.RESULT_OPEN, this.doPause); - document.removeEventListener(EVENTS.KEYDOWN, this.handleKeyDown); - megaChat.plugins.chatdIntegration.chatd.off('onClose.search'); - megaChat.plugins.chatdIntegration.chatd.off('onOpen.search'); - }; - this.bindEvents = () => { - this.pageChangeListener = mBroadcaster.addListener('pagechange', this.doPause); - document.addEventListener(EVENTS.RESULT_OPEN, this.doPause); - document.addEventListener(EVENTS.KEYDOWN, this.handleKeyDown); - megaChat.plugins.chatdIntegration.chatd.rebind('onClose.search', () => this.state.searching && this.doToggle(ACTIONS.PAUSE)); - megaChat.plugins.chatdIntegration.chatd.rebind('onOpen.search', () => this.state.searching && this.doToggle(ACTIONS.RESUME)); - }; - this.doPause = () => { - if (this.state.status === STATUS.IN_PROGRESS) { - this.doToggle(ACTIONS.PAUSE); - } - }; - this.doSearch = (s, searchMessages) => { - return ChatSearch.doSearch(s, (room, result, results) => this.setState({ - results - }), searchMessages).catch(ex => d && console.error('Search failed (or was reset)', ex)).always(() => this.setState({ - status: STATUS.COMPLETED - })); - }; - this.doToggle = (action) => { - const { - IN_PROGRESS, - PAUSED, - COMPLETED - } = STATUS; - const searching = this.state.status === IN_PROGRESS || this.state.status === PAUSED; - if (action && searching) { - const chatSearch = ChatSearch.doSearch.cs; - if (!chatSearch) { - return delay('chat-toggle', () => this.doToggle(action), 600); - } - this.setState({ - status: action === ACTIONS.PAUSE ? PAUSED : action === ACTIONS.RESUME ? IN_PROGRESS : COMPLETED - }, () => chatSearch[action]()); - } - }; - this.doDestroy = () => ChatSearch && ChatSearch.doSearch && ChatSearch.doSearch.cs && ChatSearch.doSearch.cs.destroy(); - this.handleKeyDown = ev => { - const { - keyCode - } = ev; - if (keyCode && keyCode === 27) { - return SearchField.hasValue() ? this.handleReset() : this.doPause(); - } - }; - this.handleChange = ev => { - if (SearchField.isVisible()) { - const { - value - } = ev.target; - const searching = value.length > 0; - this.doDestroy(); - this.setState({ - value, - searching, - status: undefined, - isFirstQuery: true, - results: [] - }, () => { - if (searching) { - delay('chat-search', () => this.doSearch(value, false), 1600); - if ($.dialog === 'onboardingDialog') { - closeDialog(); - } - } else { - megaChat.plugins.chatOnboarding.checkAndShowStep(); - } - }); - this.wrapperRef.scrollToY(0); - } - }; - this.handleToggle = () => { - const inProgress = this.state.status === STATUS.IN_PROGRESS; - this.setState({ - status: inProgress ? STATUS.PAUSED : STATUS.IN_PROGRESS - }, () => { - delay('chat-toggled', () => SearchField.focus()); - return this.doToggle(inProgress ? ACTIONS.PAUSE : ACTIONS.RESUME); - }); - }; - this.handleReset = () => this.setState({ - value: '', - searching: false, - status: undefined, - results: [] - }, () => { - this.wrapperRef.scrollToY(0); - onIdle(() => SearchField.focus()); - this.doDestroy(); - }); - this.handleSearchMessages = () => SearchField.hasValue() && this.setState({ - status: STATUS.IN_PROGRESS, - isFirstQuery: false - }, () => { - this.doSearch(this.state.value, true); - SearchField.focus(); - SearchField.select(); - }); - } - componentDidMount() { - super.componentDidMount(); - this.bindEvents(); - } - componentWillUnmount() { - super.componentWillUnmount(); - this.unbindEvents(); - } - render() { - const { - value, - searching, - status, - isFirstQuery, - results - } = this.state; - return REaCt().createElement("div", { - ref: this.domRef, - className: ` - ${SEARCH_PANEL_CLASS} - ${searching ? 'expanded' : ''} - ` - }, REaCt().createElement(SearchField, { - value, - searching, - status, - onChange: this.handleChange, - onToggle: this.handleToggle, - onReset: this.handleReset - }), REaCt().createElement(perfectScrollbar.O, { - className: "search-results-wrapper", - ref: wrapper => { - this.wrapperRef = wrapper; - }, - options: { - 'suppressScrollX': true - } - }, searching && REaCt().createElement(ResultContainer, { - status, - results, - isFirstQuery, - onSearchMessages: this.handleSearchMessages - }))); - } -} -;// ./js/chat/ui/leftPanel/navigation.jsx - - - -const Navigation = ({ - view, - views: { - CHATS, - MEETINGS - }, - routingSection, - unreadChats, - unreadMeetings, - contactRequests, - renderView -}) => REaCt().createElement("div", { - className: `${NAMESPACE}-nav` -}, REaCt().createElement("div", { - className: ` - ${NAMESPACE}-nav-container - ${NAMESPACE}-chats-tab - ${view === CHATS && routingSection === 'chat' ? 'active' : ''} - `, - onClick: () => { - renderView(CHATS); - eventlog(500233); - } -}, REaCt().createElement(meetings_button.A, { - unreadChats, - className: `${NAMESPACE}-nav-button`, - icon: "icon-chat-filled" -}, !!unreadChats && REaCt().createElement("div", { - className: "notifications-count" -})), REaCt().createElement("span", null, l.chats)), REaCt().createElement("div", { - className: ` - ${NAMESPACE}-nav-container - ${NAMESPACE}-meetings-tab - ${view === MEETINGS && routingSection === 'chat' ? 'active' : ''} - `, - onClick: () => { - renderView(MEETINGS); - eventlog(500234); - } -}, REaCt().createElement(meetings_button.A, { - unreadMeetings, - className: `${NAMESPACE}-nav-button`, - icon: "icon-video-call-filled" -}, !!unreadMeetings && REaCt().createElement("div", { - className: "notifications-count" -})), REaCt().createElement("span", null, l.meetings)), is_eplusplus || is_chatlink ? null : REaCt().createElement("div", { - className: ` - ${NAMESPACE}-nav-container - ${NAMESPACE}-contacts-tab - ${routingSection === 'contacts' ? 'active' : ''} - `, - onClick: () => { - loadSubPage('fm/chat/contacts'); - eventlog(500296); - } -}, REaCt().createElement(meetings_button.A, { - className: `${NAMESPACE}-nav-button`, - contactRequests, - icon: "icon-contacts" -}, !!contactRequests && REaCt().createElement("div", { - className: "notifications-count" -})), REaCt().createElement("span", null, l[165]))); -// EXTERNAL MODULE: ./js/ui/buttons.jsx -const buttons = REQ_(994); -// EXTERNAL MODULE: ./js/ui/dropdowns.jsx -const dropdowns = REQ_(911); -;// ./js/chat/ui/leftPanel/actions.jsx - - - - -const Actions = ({ - view, - views, - filter, - routingSection, - startMeeting, - scheduleMeeting, - createNewChat, - onFilter -}) => { - const { - CHATS, - MEETINGS, - LOADING - } = views; - if (is_eplusplus || is_chatlink) { - return null; - } - return REaCt().createElement("div", { - className: `${NAMESPACE}-action-buttons` - }, view === LOADING && REaCt().createElement(buttons.$, { - className: "mega-button action loading-sketch" - }, REaCt().createElement("i", null), REaCt().createElement("span", null)), view === CHATS && routingSection !== 'contacts' && REaCt().createElement(REaCt().Fragment, null, REaCt().createElement(buttons.$, { - className: "mega-button small positive new-chat-action", - label: l.add_chat, - onClick: () => { - createNewChat(); - eventlog(500284); - } - }), REaCt().createElement("div", { - className: "lhp-filter" - }, REaCt().createElement("div", { - className: "lhp-filter-control" - }, REaCt().createElement(buttons.$, { - icon: "sprite-fm-mono icon-sort-thin-solid" - }, REaCt().createElement(dropdowns.Dropdown, { - className: "light", - noArrow: "true" - }, REaCt().createElement(dropdowns.DropdownItem, { - className: "link-button", - icon: "sprite-fm-mono icon-eye-reveal", - label: l.filter_unread, - onClick: () => onFilter(FILTER.UNREAD) - }), REaCt().createElement(dropdowns.DropdownItem, { - className: "link-button", - icon: "sprite-fm-mono icon-notification-off", - label: view === MEETINGS ? l.filter_muted__meetings : l.filter_muted__chats, - onClick: () => onFilter(FILTER.MUTED) - })))), filter && REaCt().createElement(REaCt().Fragment, null, filter === FILTER.MUTED && REaCt().createElement("div", { - className: "lhp-filter-tag", - onClick: () => onFilter(FILTER.MUTED) - }, REaCt().createElement("span", null, view === MEETINGS ? l.filter_muted__meetings : l.filter_muted__chats), REaCt().createElement("i", { - className: "sprite-fm-mono icon-close-component" - })), filter === FILTER.UNREAD && REaCt().createElement("div", { - className: "lhp-filter-tag", - onClick: () => onFilter(FILTER.UNREAD) - }, REaCt().createElement("span", null, l.filter_unread), REaCt().createElement("i", { - className: "sprite-fm-mono icon-close-component" - }))))), view === MEETINGS && routingSection !== 'contacts' && REaCt().createElement(buttons.$, { - className: "mega-button small positive new-meeting-action", - label: l.new_meeting - }, REaCt().createElement("i", { - className: "dropdown-indicator sprite-fm-mono icon-arrow-down" - }), REaCt().createElement(dropdowns.Dropdown, { - className: "light", - noArrow: "true", - vertOffset: 4, - positionMy: "left top", - positionAt: "left bottom" - }, REaCt().createElement(dropdowns.DropdownItem, { - className: "link-button", - icon: "sprite-fm-mono icon-video-plus", - label: l.new_meeting_start, - onClick: startMeeting - }), REaCt().createElement("hr", null), REaCt().createElement(dropdowns.DropdownItem, { - className: "link-button", - icon: "sprite-fm-mono icon-calendar2", - label: l.schedule_meeting_start, - onClick: scheduleMeeting - }))), routingSection === 'contacts' && REaCt().createElement(buttons.$, { - className: "mega-button small positive", - label: l[71], - onClick: () => { - contactAddDialog(); - eventlog(500285); - } - })); -}; -const actions = Actions; -// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/applyDecoratedDescriptor.js -const applyDecoratedDescriptor = REQ_(793); -;// ./js/chat/ui/leftPanel/conversationsListItem.jsx - -let _dec, _dec2, _class; - - - - -const ConversationsListItem = (_dec = utils.Ay.SoonFcWrap(40, true), _dec2 = (0,mixins.N9)(0.7, 8), _class = class ConversationsListItem extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - this.state = { - isLoading: true - }; - } - isLoading() { - const mb = this.props.chatRoom.messagesBuff; - if (mb.haveMessages) { - return false; - } - return mb.messagesHistoryIsLoading() || mb.joined === false && mb.isDecrypting; - } - specShouldComponentUpdate() { - return !this.state.isLoading; - } - componentWillUnmount() { - super.componentWillUnmount(); - this.props.chatRoom.unbind('onUnreadCountUpdate.conversationsListItem'); - } - componentDidMount() { - super.componentDidMount(); - this.eventuallyScrollTo(); - const promise = this.isLoading(); - if (promise && promise.always) { - promise.always(() => { - if (this.isMounted()) { - this.setState({ - isLoading: false - }); - } - }); - } else if (promise === false) { - this.setState({ - isLoading: false - }); - } - this.props.chatRoom.rebind('onUnreadCountUpdate.conversationsListItem', () => { - this.safeForceUpdate(); - }); - } - componentDidUpdate() { - super.componentDidUpdate(); - this.eventuallyScrollTo(); - } - eventuallyScrollTo() { - const chatRoom = this.props.chatRoom || false; - if (chatRoom._scrollToOnUpdate) { - if (chatRoom.isCurrentlyActive) { - chatRoom.scrollToChat(); - } else { - chatRoom._scrollToOnUpdate = false; - } - } - } - getConversationTimestamp() { - const { - chatRoom - } = this.props; - if (chatRoom) { - const lastMessage = chatRoom.messagesBuff.getLatestTextMessage(); - const timestamp = lastMessage && lastMessage.delay || chatRoom.ctime; - return todayOrYesterday(timestamp * 1000) ? getTimeMarker(timestamp) : time2date(timestamp, 17); - } - return null; - } - getScheduledDateTime() { - const { - scheduledMeeting - } = this.props.chatRoom; - if (scheduledMeeting) { - const { - nextOccurrenceStart, - nextOccurrenceEnd - } = scheduledMeeting; - return { - date: time2date(nextOccurrenceStart / 1000, 19), - startTime: toLocaleTime(nextOccurrenceStart), - endTime: toLocaleTime(nextOccurrenceEnd) - }; - } - } - render() { - let classString = ""; - const {chatRoom} = this.props; - if (!chatRoom || !chatRoom.chatId) { - return null; - } - const roomId = chatRoom.chatId; - if (chatRoom.isCurrentlyActive) { - classString += " active"; - } - let nameClassString = "user-card-name conversation-name selectable-txt"; - let contactId; - let id; - let contact; - if (chatRoom.type === 'private') { - const handle = chatRoom.getParticipantsExceptMe()[0]; - contact = handle ? M.u[handle] : M.u[u_handle]; - if (!contact) { - return `Unknown conversation id for ${chatRoom.roomId}`; - } - id = `conversation_${htmlentities(contact.u)}`; - } else if (chatRoom.type === 'group') { - contactId = roomId; - id = `conversation_${contactId}`; - classString += ' groupchat'; - } else if (chatRoom.type === 'public') { - contactId = roomId; - id = `conversation_${contactId}`; - classString += ' groupchat public'; - } else { - return `Unknown room type for ${chatRoom.roomId}`; - } - const unreadCount = chatRoom.messagesBuff.getUnreadCount(); - let isUnread = false; - const notificationItems = []; - if (chatRoom.havePendingCall() && chatRoom.state !== ChatRoom.STATE.LEFT) { - notificationItems.push(REaCt().createElement("i", { - className: "tiny-icon white-handset", - key: "callIcon" - })); - } - if (unreadCount > 0) { - notificationItems.push(REaCt().createElement("span", { - key: "unreadCounter" - }, unreadCount > 9 ? "9+" : unreadCount)); - isUnread = true; - } - let lastMessageDiv = null; - const showHideMsg = mega.config.get('showHideChat'); - const lastMessage = showHideMsg ? '' : chatRoom.messagesBuff.getLatestTextMessage(); - let lastMsgDivClasses; - if (lastMessage) { - lastMsgDivClasses = `conversation-message${ isUnread ? " unread" : ""}`; - const renderableSummary = chatRoom.messagesBuff.getRenderableSummary(lastMessage); - if (chatRoom.havePendingCall() || chatRoom.haveActiveCall()) { - lastMsgDivClasses += " call"; - classString += " call-exists"; - } - lastMessageDiv = REaCt().createElement("div", { - className: lastMsgDivClasses - }, REaCt().createElement(utils.P9, null, renderableSummary)); - if (lastMessage.textContents && lastMessage.textContents[1] === Message.MANAGEMENT_MESSAGE_TYPES.VOICE_CLIP && lastMessage.getAttachmentMeta()[0]) { - const playTime = secondsToTimeShort(lastMessage.getAttachmentMeta()[0].playtime); - lastMessageDiv = REaCt().createElement("div", { - className: lastMsgDivClasses - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-audio-filled voice-message-icon" - }), playTime); - } - if (lastMessage.metaType && lastMessage.metaType === Message.MESSAGE_META_TYPE.GEOLOCATION) { - lastMessageDiv = REaCt().createElement("div", { - className: lastMsgDivClasses - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-location geolocation-icon" - }), l[20789]); - } - } else { - lastMsgDivClasses = "conversation-message"; - lastMessageDiv = showHideMsg ? '' : REaCt().createElement("div", { - className: lastMsgDivClasses - }, this.state.isLoading ? l[7006] : l[8000]); - } - if (chatRoom.type !== 'public') { - nameClassString += ' privateChat'; - } - let roomTitle = REaCt().createElement(utils.oM, null, megaChat.html(chatRoom.getRoomTitle())); - if (chatRoom.type === 'private') { - roomTitle = megaChat.WITH_SELF_NOTE && chatRoom.isNote ? REaCt().createElement("span", { - className: "note-chat-label" - }, l.note_label) : REaCt().createElement("span", null, REaCt().createElement("div", { - className: "user-card-wrapper" - }, REaCt().createElement(utils.oM, null, megaChat.html(chatRoom.getRoomTitle())))); - } - nameClassString += chatRoom.type === "private" || chatRoom.type === "group" ? ' badge-pad' : ''; - const { - scheduledMeeting, - isMeeting - } = chatRoom; - const isUpcoming = scheduledMeeting && scheduledMeeting.isUpcoming; - const { - startTime, - endTime - } = this.getScheduledDateTime() || {}; - const isEmptyNote = chatRoom.isNote && !chatRoom.hasMessages(); - return REaCt().createElement("li", { - ref: this.domRef, - id, - className: ` - ${classString} - ${isUpcoming ? 'upcoming-conversation' : ''} - ${this.props.className || ''} - `, - "data-room-id": roomId, - "data-jid": contactId, - onClick: ev => { - let _this$props$onConvers, _this$props; - return ((_this$props$onConvers = (_this$props = this.props).onConversationClick) == null ? void 0 : _this$props$onConvers.call(_this$props, ev)) || loadSubPage(chatRoom.getRoomUrl(false)); - } - }, REaCt().createElement("div", { - className: "conversation-avatar" - }, (chatRoom.type === 'group' || chatRoom.type === 'public') && REaCt().createElement("div", { - className: ` - chat-topic-icon - ${isMeeting ? 'meeting-icon' : ''} - ` - }, REaCt().createElement("i", { - className: isMeeting ? 'sprite-fm-mono icon-video-call-filled' : 'sprite-fm-uni icon-chat-group' - })), chatRoom.type === 'private' && contact && chatRoom.isNote ? REaCt().createElement("div", { - className: ` - note-chat-signifier - ${isEmptyNote ? 'note-chat-empty' : ''} - ` - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-file-text-thin-outline note-chat-icon" - })) : REaCt().createElement(ui_contacts.Avatar, { - contact - })), REaCt().createElement("div", { - className: "conversation-data" - }, REaCt().createElement("div", { - className: "conversation-data-top" - }, REaCt().createElement("div", { - className: `conversation-data-name ${nameClassString}` - }, roomTitle, chatRoom.isMuted() ? REaCt().createElement("i", { - className: "sprite-fm-mono icon-notification-off-filled muted-conversation-icon" - }) : null), chatRoom.isNote ? null : REaCt().createElement("div", { - className: "conversation-data-badges" - }, chatRoom.type === 'private' ? REaCt().createElement(ui_contacts.ContactPresence, { - contact - }) : null, chatRoom.type === 'group' || chatRoom.type === 'private' ? REaCt().createElement("i", { - className: "sprite-fm-uni icon-ekr-key simpletip", - "data-simpletip": l[20935] - }) : null, scheduledMeeting && scheduledMeeting.isUpcoming && scheduledMeeting.isRecurring && REaCt().createElement("i", { - className: "sprite-fm-mono icon-repeat-thin-solid" - }))), REaCt().createElement("div", { - className: "clear" - }), isUpcoming ? REaCt().createElement("div", { - className: "conversation-message-info" - }, REaCt().createElement("div", { - className: "conversation-scheduled-data" - }, REaCt().createElement("span", null, startTime), REaCt().createElement("span", null, "\xA0 - \xA0"), REaCt().createElement("span", null, endTime)), REaCt().createElement("div", { - className: "conversation-scheduled-data" - }, notificationItems.length > 0 ? REaCt().createElement("div", { - className: ` - unread-messages - items-${notificationItems.length} - unread-upcoming - ${unreadCount > 9 && notificationItems.length > 1 ? 'unread-spaced' : ''} - ` - }, notificationItems) : null)) : REaCt().createElement("div", { - className: "conversation-message-info" - }, isEmptyNote ? null : lastMessageDiv)), isUpcoming || isEmptyNote ? null : REaCt().createElement("div", { - className: "date-time-wrapper" - }, REaCt().createElement("div", { - className: "date-time" - }, this.getConversationTimestamp()), notificationItems.length > 0 ? REaCt().createElement("div", { - className: ` - unread-messages-container - ${unreadCount > 9 && notificationItems.length > 1 ? 'unread-spaced' : ''} - ` - }, REaCt().createElement("div", { - className: `unread-messages items-${notificationItems.length}` - }, notificationItems)) : null)); - } -}, (0,applyDecoratedDescriptor.A)(_class.prototype, "eventuallyScrollTo", [_dec], Object.getOwnPropertyDescriptor(_class.prototype, "eventuallyScrollTo"), _class.prototype), (0,applyDecoratedDescriptor.A)(_class.prototype, "render", [_dec2], Object.getOwnPropertyDescriptor(_class.prototype, "render"), _class.prototype), _class); - -;// ./js/chat/ui/leftPanel/conversationsList.jsx - - - - - - - -const ConversationsList = ({ - conversations, - className, - children -}) => { - return REaCt().createElement(perfectScrollbar.O, { - className: "chat-lp-scroll-area", - didMount: (id, ref) => { - megaChat.$chatTreePanePs = [...megaChat.$chatTreePanePs, { - id, - ref - }]; - }, - willUnmount: id => { - megaChat.$chatTreePanePs = megaChat.$chatTreePanePs.filter(ref => ref.id !== id); - }, - conversations - }, REaCt().createElement("ul", { - className: ` - conversations-pane - ${className || ''} - ` - }, children || conversations.map(c => c.roomId && REaCt().createElement(ConversationsListItem, (0,esm_extends.A)({ - key: c.roomId, - chatRoom: c - }, c.type === 'private' && { - contact: M.u[c.getParticipantsExceptMe()[0]] - }))))); -}; -const Chats = ({ - conversations, - onArchivedClicked, - filter -}) => { - conversations = Object.values(conversations || {}).filter(c => !c.isMeeting && c.isDisplayable() && (!filter || filter === FILTER.UNREAD && c.messagesBuff.getUnreadCount() > 0 || filter === FILTER.MUTED && c.isMuted())).sort(M.sortObjFn(c => c.lastActivity || c.ctime, -1)); - const noteChat = megaChat.getNoteChat(); - return REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("div", { - className: "conversations-holder" - }, filter ? null : REaCt().createElement("div", { - className: "conversations-category" - }, REaCt().createElement("span", null, l.filter_heading__recent)), conversations && conversations.length >= 1 ? REaCt().createElement(ConversationsList, { - conversations - }, megaChat.WITH_SELF_NOTE && noteChat && noteChat.isDisplayable() ? filter ? null : REaCt().createElement(ConversationsListItem, { - chatRoom: noteChat - }) : null, conversations.map(c => c.roomId && !c.isNote && REaCt().createElement(ConversationsListItem, (0,esm_extends.A)({ - key: c.roomId, - chatRoom: c - }, c.type === 'private' && { - contact: M.u[c.getParticipantsExceptMe()[0]] - })))) : REaCt().createElement("div", { - className: ` - ${NAMESPACE}-nil - ${filter ? `${NAMESPACE}-nil--chats` : ''} - ` - }, filter ? REaCt().createElement(REaCt().Fragment, null, filter === FILTER.MUTED && REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("i", { - className: "sprite-fm-mono icon-notification-off-filled" - }), REaCt().createElement("h3", null, l.filter_nil__muted_chats)), filter === FILTER.UNREAD && REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("i", { - className: "sprite-fm-mono icon-eye-thin-solid" - }), REaCt().createElement("h3", null, l.filter_nil__unread_messages))) : REaCt().createElement("span", null, l.no_chats_lhp)), megaChat.WITH_SELF_NOTE && conversations && conversations.length === 1 && noteChat && REaCt().createElement(ConversationsList, { - conversations - }, REaCt().createElement(ConversationsListItem, { - chatRoom: noteChat - }))), REaCt().createElement("div", { - className: `${NAMESPACE}-bottom` - }, REaCt().createElement("div", { - className: `${NAMESPACE}-bottom-control` - }, REaCt().createElement("div", { - className: "conversations-category", - onClick: onArchivedClicked - }, REaCt().createElement("span", null, l.filter_archived__chats), REaCt().createElement("i", { - className: "sprite-fm-mono icon-arrow-right" - }))))); -}; -const Archived = ({ - conversations, - archivedUnmounting, - onClose -}) => { - const archivedChats = Object.values(conversations || {}).filter(c => !c.isMeeting && c.isArchived()).sort(M.sortObjFn(c => c.lastActivity || c.ctime, -1)); - return REaCt().createElement("div", { - className: ` - ${NAMESPACE}-archived - ${archivedUnmounting ? 'with-unmount-animation' : ''} - ` - }, REaCt().createElement("div", { - className: `${NAMESPACE}-archived-head` - }, REaCt().createElement(meetings_button.A, { - className: "mega-button round", - icon: "sprite-fm-mono icon-arrow-left-regular-outline", - onClick: onClose - }), REaCt().createElement("h2", null, l.filter_archived__chats)), REaCt().createElement("div", { - className: `${NAMESPACE}-archived-content` - }, archivedChats && archivedChats.length ? REaCt().createElement(ConversationsList, { - conversations: archivedChats - }) : REaCt().createElement("div", { - className: `${NAMESPACE}-archived-empty` - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-archive" - }), REaCt().createElement("h3", null, l.filter_archived__nil_chats)))); -}; -class Meetings extends mixins.w9 { - constructor(props) { - let _megaChat$getCurrentM; - super(props); - this.TABS = { - UPCOMING: 0x00, - PAST: 0x01 - }; - this.domRef = REaCt().createRef(); - this.ongoingRef = REaCt().createRef(); - this.navigationRef = REaCt().createRef(); - this.state = { - tab: this.TABS.UPCOMING - }; - this.Navigation = ({ - conversations - }) => { - const { - UPCOMING, - PAST - } = this.TABS; - const { - tab - } = this.state; - const unreadMeetings = Object.values(conversations || {}).reduce((acc, curr) => { - if (curr.isDisplayable() && curr.isMeeting && curr.messagesBuff.getUnreadCount()) { - let _curr$scheduledMeetin; - acc[(_curr$scheduledMeetin = curr.scheduledMeeting) != null && _curr$scheduledMeetin.isUpcoming ? UPCOMING : PAST]++; - } - return acc; - }, { - [UPCOMING]: 0, - [PAST]: 0 - }); - return REaCt().createElement("div", { - ref: this.navigationRef, - className: ` - ${NAMESPACE}-meetings--navigation - ${this.props.leftPaneWidth < 230 ? 'narrow-width' : ''} - ` - }, REaCt().createElement(meetings_button.A, { - converstaions: conversations, - className: ` - mega-button - action - ${tab === UPCOMING ? 'is-active' : ''} - `, - onClick: () => this.setState({ - tab: UPCOMING - }) - }, REaCt().createElement("span", null, l.meetings_tab_upcoming, !!unreadMeetings[UPCOMING] && REaCt().createElement("div", { - className: "notification-indication" - }))), REaCt().createElement(meetings_button.A, { - converstaions: conversations, - className: ` - mega-button - action - ${tab === PAST ? 'is-active' : ''} - `, - onClick: () => this.setState({ - tab: PAST - }, () => eventlog(500254)) - }, REaCt().createElement("span", null, l.meetings_tab_past, !!unreadMeetings[PAST] && REaCt().createElement("div", { - className: "notification-indication" - })))); - }; - this.Holder = ({ - heading, - className, - children - }) => REaCt().createElement("div", { - className: ` - conversations-holder - ${className || ''} - ` - }, REaCt().createElement("div", { - className: ` - conversations-category - ` - }, heading && REaCt().createElement("span", null, heading)), children); - this.Ongoing = ({ - ongoingMeetings - }) => ongoingMeetings != null && ongoingMeetings.length ? REaCt().createElement("div", { - ref: this.ongoingRef, - className: `${NAMESPACE}-meetings--ongoing` - }, REaCt().createElement("strong", null, l.happening_now), REaCt().createElement(ConversationsList, { - conversations: ongoingMeetings - })) : null; - this.Upcoming = () => { - const { - upcomingMeetings, - nextOccurrences - } = megaChat.plugins.meetingsManager.filterUpcomingMeetings(this.props.conversations); - const upcomingItem = chatRoom => REaCt().createElement(ConversationsListItem, { - key: chatRoom.roomId, - chatRoom - }); - return REaCt().createElement(this.Holder, null, upcomingMeetings && upcomingMeetings.length ? REaCt().createElement(ConversationsList, { - conversations: upcomingMeetings - }, nextOccurrences.today && nextOccurrences.today.length ? REaCt().createElement("div", { - className: "conversations-group" - }, REaCt().createElement("div", { - className: "conversations-category category--label" - }, REaCt().createElement("span", null, l.upcoming__today)), nextOccurrences.today.map(upcomingItem)) : null, nextOccurrences.tomorrow && nextOccurrences.tomorrow.length ? REaCt().createElement("div", { - className: "conversations-group" - }, REaCt().createElement("div", { - className: "conversations-category category--label" - }, REaCt().createElement("span", null, l.upcoming__tomorrow)), nextOccurrences.tomorrow.map(upcomingItem)) : null, Object.keys(nextOccurrences.rest).length ? Object.keys(nextOccurrences.rest).map(date => REaCt().createElement("div", { - key: date, - className: "conversations-group" - }, REaCt().createElement("div", { - className: "conversations-category category--label" - }, REaCt().createElement("span", null, date)), nextOccurrences.rest[date].map(upcomingItem))) : null) : REaCt().createElement("div", { - className: `${NAMESPACE}-nil` - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-calendar-plus-thin-solid" - }), REaCt().createElement("span", null, l.meetings_upcoming_nil))); - }; - this.Past = () => { - const conversations = Object.values(this.props.conversations || {}); - const pastMeetings = conversations.filter(c => { - const { - isCanceled, - isPast, - isCompleted - } = c.scheduledMeeting || {}; - return c.isMeeting && c.isDisplayable() && (!c.scheduledMeeting || isCanceled || isPast || isCompleted) && !c.havePendingCall(); - }).sort(M.sortObjFn(c => c.lastActivity || c.ctime, -1)); - const archivedMeetings = conversations.filter(c => c.isMeeting && c.isArchived()).sort(M.sortObjFn(c => c.lastActivity || c.ctime, -1)); - return REaCt().createElement(this.Holder, null, REaCt().createElement(ConversationsList, { - conversations: pastMeetings - }, pastMeetings.length ? pastMeetings.map(chatRoom => chatRoom.roomId && REaCt().createElement(ConversationsListItem, { - key: chatRoom.roomId, - chatRoom - })) : REaCt().createElement("div", { - className: ` - ${NAMESPACE}-nil - ${archivedMeetings.length ? 'half-sized' : ''} - ` - }, archivedMeetings.length ? REaCt().createElement("strong", null, l.meetings_past_nil_heading) : null, REaCt().createElement("i", { - className: "sprite-fm-mono icon-video-thin-solid" - }), REaCt().createElement("span", null, l.meetings_past_nil)), archivedMeetings.length ? REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("div", { - className: "archived-separator" - }), REaCt().createElement("div", { - className: "conversations-category category--label" - }, REaCt().createElement("span", null, l.meetings_label_archived)), archivedMeetings.map(chatRoom => chatRoom.roomId && REaCt().createElement(ConversationsListItem, { - key: chatRoom.roomId, - chatRoom - }))) : null)); - }; - this.getContainerStyles = ongoingMeetings => { - if (ongoingMeetings != null && ongoingMeetings.length) { - let _this$ongoingRef, _this$navigationRef; - const ongoingHeight = (_this$ongoingRef = this.ongoingRef) == null || (_this$ongoingRef = _this$ongoingRef.current) == null ? void 0 : _this$ongoingRef.clientHeight; - const navigationHeight = (_this$navigationRef = this.navigationRef) == null || (_this$navigationRef = _this$navigationRef.current) == null ? void 0 : _this$navigationRef.clientHeight; - return { - style: { - maxHeight: `calc(100% - ${ongoingHeight + navigationHeight + 30}px)` - } - }; - } - return null; - }; - this.state.tab = this.TABS[(_megaChat$getCurrentM = megaChat.getCurrentMeeting()) != null && _megaChat$getCurrentM.isPast ? 'PAST' : 'UPCOMING']; - } - componentWillUnmount() { - super.componentWillUnmount(); - megaChat.off(`${megaChat.plugins.meetingsManager.EVENTS.OCCURRENCES_UPDATE}.${this.getUniqueId()}`); - } - componentDidMount() { - super.componentDidMount(); - megaChat.rebind(`${megaChat.plugins.meetingsManager.EVENTS.OCCURRENCES_UPDATE}.${this.getUniqueId()}`, () => this.safeForceUpdate()); - megaChat.rebind(megaChat.plugins.meetingsManager.EVENTS.INITIALIZE, (ev, scheduledMeeting) => this.isMounted() && this.setState({ - tab: this.TABS[scheduledMeeting != null && scheduledMeeting.isPast ? 'PAST' : 'UPCOMING'] - })); - } - render() { - const { - UPCOMING, - PAST - } = this.TABS; - const { - tab - } = this.state; - const ongoingMeetings = Object.values(this.props.conversations || {}).filter(c => c.isDisplayable() && c.isMeeting && c.havePendingCall()); - return REaCt().createElement("div", { - ref: this.domRef, - className: `${NAMESPACE}-meetings` - }, REaCt().createElement(this.Ongoing, { - ongoingMeetings - }), REaCt().createElement(this.Navigation, { - conversations: this.props.conversations - }), REaCt().createElement("div", (0,esm_extends.A)({ - className: ` - ${NAMESPACE}-meetings--content - ${tab === UPCOMING ? 'is-upcoming' : ''} - ${tab === PAST ? 'is-past' : ''} - ` - }, this.getContainerStyles(ongoingMeetings)), tab === UPCOMING && REaCt().createElement(this.Upcoming, null), tab === PAST && REaCt().createElement(this.Past, null))); - } -} -// EXTERNAL MODULE: ./js/chat/ui/updateObserver.jsx -const updateObserver = REQ_(501); -;// ./js/chat/ui/leftPanel/leftPanel.jsx - - - - - - - - -const NAMESPACE = 'lhp'; -const FILTER = { - MUTED: 'muted', - UNREAD: 'unread' -}; -class LeftPanel extends mixins.w9 { - constructor(props) { - super(props); - this.domRef = REaCt().createRef(); - this.contactRequestsListener = undefined; - this.fmConfigLeftPaneListener = undefined; - this.state = { - leftPaneWidth: Math.min(mega.config.get('leftPaneWidth') | 0, 400) || 384, - archived: false, - archivedUnmounting: false, - filter: '', - unreadChats: 0, - unreadMeetings: 0, - contactRequests: 0 - }; - this.toggleFilter = filter => { - this.setState(state => ({ - filter: state.filter === filter ? '' : filter - }), () => { - Object.values(megaChat.$chatTreePanePs).map(({ - ref - }) => ref.reinitialise == null ? void 0 : ref.reinitialise()); - }); - }; - this.state.contactRequests = Object.keys(M.ipc).length; - } - customIsEventuallyVisible() { - return M.chat; - } - renderLoading() { - return REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("span", { - className: "heading loading-sketch" - }), REaCt().createElement("ul", { - className: "conversations-pane loading-sketch" - }, Array.from({ - length: this.props.conversations.length - }, (el, i) => { - return REaCt().createElement("li", { - key: i - }, REaCt().createElement("div", { - className: "conversation-avatar" - }, REaCt().createElement("div", { - className: "chat-topic-icon" - })), REaCt().createElement("div", { - className: "conversation-data" - }, REaCt().createElement("div", { - className: "conversation-data-top" - }), REaCt().createElement("div", { - className: "conversation-message-info" - }, REaCt().createElement("div", { - className: "conversation-message" - })))); - }))); - } - componentWillUnmount() { - super.componentWillUnmount(); - megaChat.unbind(`onUnreadCountUpdate.${NAMESPACE}`); - mBroadcaster.removeListener(this.contactRequestsListener); - mBroadcaster.removeListener(this.fmConfigLeftPaneListener); - } - componentDidMount() { - let _$$leftPaneResizable; - super.componentDidMount(); - megaChat.rebind(`onUnreadCountUpdate.${NAMESPACE}`, (ev, { - unreadChats, - unreadMeetings - }) => { - this.setState({ - unreadChats, - unreadMeetings - }, () => this.safeForceUpdate()); - }); - this.contactRequestsListener = mBroadcaster.addListener('fmViewUpdate:ipc', () => this.setState({ - contactRequests: Object.keys(M.ipc).length - })); - $.leftPaneResizableChat = new FMResizablePane(this.domRef.current, { - ...(_$$leftPaneResizable = $.leftPaneResizable) == null ? void 0 : _$$leftPaneResizable.options, - minWidth: mega.flags.ab_ads ? 260 : 200 - }); - this.fmConfigLeftPaneListener = mBroadcaster.addListener('fmconfig:leftPaneWidth', value => this.setState(state => ({ - leftPaneWidth: value || state.leftPaneWidth - }))); - } - render() { - const { - view, - views, - conversations, - routingSection, - renderView, - startMeeting, - scheduleMeeting, - createNewChat - } = this.props; - const { - CHATS, - MEETINGS, - LOADING - } = views; - return REaCt().createElement("div", (0,esm_extends.A)({ - ref: this.domRef, - className: ` - fm-left-panel - chat-lp-body - ${NAMESPACE}-container - ${is_chatlink && 'hidden' || ''} - ${megaChat._joinDialogIsShown && 'hidden' || ''} - ` - }, this.state.leftPaneWidth && { - width: this.state.leftPaneWidth - }), REaCt().createElement("div", { - className: "left-pane-drag-handle" - }), REaCt().createElement(SearchPanel, null), REaCt().createElement(Navigation, { - view, - views, - routingSection, - unreadChats: this.state.unreadChats, - unreadMeetings: this.state.unreadMeetings, - contactRequests: this.state.contactRequests, - renderView: view => this.setState({ - filter: false - }, () => renderView(view)) - }), REaCt().createElement(actions, { - view, - views, - filter: this.state.filter, - routingSection, - startMeeting, - scheduleMeeting, - createNewChat, - onFilter: this.toggleFilter - }), this.state.archived && REaCt().createElement(Archived, { - conversations, - archivedUnmounting: this.state.archivedUnmounting, - onClose: () => this.setState({ - archivedUnmounting: true - }, () => tSleep(0.3).then(() => this.setState({ - archivedUnmounting: false, - archived: false - }))) - }), REaCt().createElement("div", { - className: ` - ${NAMESPACE}-conversations - ${view === MEETINGS ? 'meetings-view' : ''} - ${view === CHATS ? 'chats-view' : ''} - conversations - content-panel - active - ` - }, view === LOADING ? this.renderLoading() : REaCt().createElement(REaCt().Fragment, null, view === MEETINGS && REaCt().createElement(Meetings, { - conversations, - leftPaneWidth: this.state.leftPaneWidth - }), view === CHATS && REaCt().createElement(Chats, { - conversations, - filter: this.state.filter, - onArchivedClicked: () => this.setState({ - archived: true, - filter: false - }) - })))); - } -} -const leftPanel = (0,mixins.Zz)(updateObserver.Y)(LeftPanel); -;// ./js/chat/ui/meetings/workflow/freeCallEnded.jsx - - -const freeCallEnded_NAMESPACE = 'free-call-ended-dlg'; -class FreeCallEnded extends REaCt().Component { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - } - componentWillUnmount() { - if ($.dialog === freeCallEnded_NAMESPACE) { - closeDialog(); - } - } - componentDidMount() { - M.safeShowDialog(freeCallEnded_NAMESPACE, () => { - if (!this.domRef.current) { - throw new Error(`${freeCallEnded_NAMESPACE} dialog: component ${freeCallEnded_NAMESPACE} not mounted.`); - } - eventlog(500295); - return $(`#${freeCallEnded_NAMESPACE}`); - }); - } - render() { - const { - onClose - } = this.props; - return REaCt().createElement(modalDialogs.A.ModalDialog, { - id: freeCallEnded_NAMESPACE, - ref: this.domRef, - className: "mega-dialog", - dialogType: "action", - dialogName: freeCallEnded_NAMESPACE, - onClose - }, REaCt().createElement("header", null, REaCt().createElement("div", { - className: "free-call-ended graphic" - }, REaCt().createElement("img", { - src: `${staticpath}images/mega/chat-upgrade-rocket.png` - }))), REaCt().createElement("section", { - className: "content" - }, REaCt().createElement("div", { - className: "content-block" - }, REaCt().createElement("div", { - className: "dialog-body-text" - }, REaCt().createElement("h3", null, l.free_call_ended_dlg_text), REaCt().createElement("span", null, l.free_call_ended_dlg_subtext)))), REaCt().createElement("footer", null, REaCt().createElement("div", { - className: "footer-container" - }, REaCt().createElement("button", { - className: "mega-button positive large", - onClick: () => { - loadSubPage('pro'); - eventlog(500261); - onClose(); - } - }, REaCt().createElement("span", null, l.upgrade_now))))); - } -} -;// ./js/chat/ui/contactSelectorDialog.jsx - - - - -class ContactSelectorDialog extends mixins.w9 { - constructor(...args) { - super(...args); - this.dialogName = 'contact-selector-dialog'; - } - componentDidMount() { - super.componentDidMount(); - M.safeShowDialog(this.dialogName, () => $(`.${this.dialogName}`)); - } - componentWillUnmount() { - super.componentWillUnmount(); - if ($.dialog === this.dialogName) { - closeDialog(); - } - } - render() { - const { - active, - selectFooter, - exclude, - allowEmpty, - multiple, - topButtons, - showAddContact, - className, - multipleSelectedButtonLabel, - singleSelectedButtonLabel, - nothingSelectedButtonLabel, - onClose, - onSelectDone - } = this.props; - return REaCt().createElement(modalDialogs.A.ModalDialog, { - className: ` - popup - contacts-search - ${className} - ${this.dialogName} - `, - onClose - }, REaCt().createElement(ui_contacts.ContactPickerWidget, { - active, - className: "popup contacts-search small-footer", - contacts: M.u, - selectFooter, - megaChat, - withSelfNote: megaChat.WITH_SELF_NOTE, - exclude, - allowEmpty, - multiple, - topButtons, - showAddContact, - multipleSelectedButtonLabel, - singleSelectedButtonLabel, - nothingSelectedButtonLabel, - onClose, - onAddContact: () => { - eventlog(500237); - onClose(); - }, - onSelected: () => { - eventlog(500238); - onClose(); - }, - onSelectDone - })); - } -} -window.ContactSelectorDialogUI = { - ContactSelectorDialog -}; -const ui_contactSelectorDialog = ContactSelectorDialog; -;// ./js/chat/ui/conversations.jsx - - - - - - - - - - - - - - - -const VIEWS = { - CHATS: 0x00, - MEETINGS: 0x01, - LOADING: 0x02 -}; -const conversations_EVENTS = { - NAV_RENDER_VIEW: 'navRenderView' -}; -window.convAppConstants = { - VIEWS, - EVENTS: conversations_EVENTS -}; -class ConversationsApp extends mixins.w9 { - constructor(props) { - super(props); - this.domRef = REaCt().createRef(); - this.chatRoomRef = null; - this.occurrenceRef = null; - this.state = { - startGroupChatDialog: false, - startMeetingDialog: false, - scheduleMeetingDialog: false, - scheduleOccurrenceDialog: false, - freeCallEndedDialog: false, - contactSelectorDialog: false, - view: VIEWS.LOADING, - callExpanded: false, - ipcData: null - }; - this._cacheRouting(); - megaChat.rebind('onStartNewMeeting.convApp', () => this.startMeeting()); - } - startMeeting() { - if (megaChat.hasSupportForCalls) { - return (0,call.dQ)().then(() => this.setState({ - startMeetingDialog: true - })).catch(() => d && console.warn('Already in a call.')); - } - return showToast('warning', l[7211]); - } - _cacheRouting() { - this.routingSection = this.props.megaChat.routingSection; - this.routingSubSection = this.props.megaChat.routingSubSection; - this.routingParams = this.props.megaChat.routingParams; - } - hasOpenDialog() { - return [...document.querySelectorAll('.mega-dialog')].some(dialog => !!(dialog.offsetParent || dialog.offsetWidth || dialog.offsetHeight)); - } - specShouldComponentUpdate() { - if (this.routingSection !== this.props.megaChat.routingSection || this.routingSubSection !== this.props.megaChat.routingSubSection || this.routingParams !== this.props.megaChat.routingParams) { - this._cacheRouting(); - return true; - } - } - componentDidMount() { - super.componentDidMount(); - $(document).rebind('keydown.megaChatTextAreaFocus', e => { - if (!M.chat || e.megaChatHandled) { - return; - } - const { - currentlyOpenedChat - } = megaChat; - const currentRoom = megaChat.getCurrentRoom(); - if (currentlyOpenedChat) { - if (currentRoom && currentRoom.isReadOnly() || $(e.target).is(".messages-textarea, input, textarea") || (e.ctrlKey || e.metaKey || e.which === 19) && e.keyCode === 67 || e.keyCode === 91 || e.keyCode === 17 || e.keyCode === 27 || e.altKey || e.metaKey || e.ctrlKey || e.shiftKey || this.hasOpenDialog() || document.querySelector('textarea:focus,select:focus,input:focus')) { - return; - } - const $typeArea = $('.messages-textarea:visible:first'); - moveCursortoToEnd($typeArea); - e.megaChatHandled = true; - $typeArea.triggerHandler(e); - e.preventDefault(); - e.stopPropagation(); - return false; - } - }); - $(document).rebind('mouseup.megaChatTextAreaFocus', e => { - if (!M.chat || e.megaChatHandled || slideshowid) { - return; - } - const $target = $(e.target); - if (megaChat.currentlyOpenedChat) { - if ($target.is(".messages-textarea,a,input,textarea,select,button") || $target.is('i') && $target.parent().is('a,input,select,button') || $target.closest('.messages.scroll-area').length > 0 || $target.closest('.mega-dialog').length > 0 || this.hasOpenDialog() || document.querySelector('textarea:focus,select:focus,input:focus') || window.getSelection().toString()) { - return; - } - const $typeArea = $('.messages-textarea:visible:first'); - if ($typeArea.length === 1 && !$typeArea.is(":focus")) { - $typeArea.trigger("focus"); - e.megaChatHandled = true; - } - } - }); - megaChat.rebind(megaChat.plugins.meetingsManager.EVENTS.EDIT, (ev, chatOrOccurrence) => { - if (chatOrOccurrence instanceof ChatRoom || !chatOrOccurrence) { - this.chatRoomRef = chatOrOccurrence; - this.setState({ - scheduleMeetingDialog: true - }); - } else { - this.occurrenceRef = chatOrOccurrence; - this.setState({ - scheduleOccurrenceDialog: true - }); - } - }); - megaChat.rebind(conversations_EVENTS.NAV_RENDER_VIEW, ({ - data - }) => { - if (Object.values(VIEWS).includes(data)) { - this.renderView(data); - } - }); - megaChat.rebind('onCallTimeLimitExceeded', () => { - this.setState({ - freeCallEndedDialog: true - }); - }); - if (megaChat.WITH_SELF_NOTE && !megaChat.getNoteChat() && !is_chatlink) { - api.req({ - a: 'mcc', - u: [], - m: 0, - g: 0, - v: Chatd.VERSION - }).catch(dump); - } - this.requestReceivedListener = mBroadcaster.addListener('fmViewUpdate:ipc', () => { - this.setState({ - ipcData: this.makeIpcData() - }); - }); - this.setState({ - ipcData: this.makeIpcData() - }); - } - componentWillUnmount() { - super.componentWillUnmount(); - $(document).off('keydown.megaChatTextAreaFocus'); - mBroadcaster.removeListener('fmViewUpdate:ipc', this.requestReceivedListener); - } - componentDidUpdate(prevProps, prevState) { - this.handleOnboardingStep(); - const { - names: prevNames - } = prevState.ipcData; - const newIpcData = this.makeIpcData(); - const { - names: newNames - } = newIpcData; - if (newNames.size !== prevNames.size) { - this.setState({ - ipcData: newIpcData - }); - return; - } - let different = false; - for (const [email, name] of newNames) { - if (!prevNames.has(email) || prevNames.get(email) !== name) { - different = true; - break; - } - } - if (different) { - this.setState({ - ipcData: newIpcData - }); - } - } - handleOnboardingStep() { - if (this.state.view === VIEWS.LOADING) { - return; - } - megaChat.plugins.chatOnboarding.checkAndShowStep(); - } - renderView(view) { - this.setState({ - view - }, () => { - const { - $chatTreePanePs, - routingSection, - currentlyOpenedChat - } = megaChat; - Object.values($chatTreePanePs).forEach(ref => ref.reinitialise == null ? void 0 : ref.reinitialise()); - if (routingSection !== 'chat') { - loadSubPage('fm/chat'); - } - megaChat.currentlyOpenedView = view; - if (!currentlyOpenedChat) { - megaChat.renderListing(null, false).catch(dump); - } - }); - } - makeIpcData() { - let mixed = false; - const names = new Map(); - const data = Object.values(M.ipc).reduce((acc, curr) => { - const name = M.getNameByEmail(curr.m); - if (name !== curr.m) { - names.set(curr.m, name); - mixed = true; - } - return { - ...acc, - [curr.p]: { - ...curr, - name - } - }; - }, Object.create(null)); - return { - mixed, - data, - names - }; - } - render() { - const { - CHATS, - MEETINGS - } = VIEWS; - const { - routingSection, - chatUIFlags, - currentlyOpenedChat, - chats - } = megaChat; - const { - view, - startGroupChatDialog, - startMeetingDialog, - scheduleMeetingDialog, - scheduleOccurrenceDialog, - callExpanded, - freeCallEndedDialog, - contactSelectorDialog - } = this.state; - const isEmpty = chats && routingSection === 'chat' && !currentlyOpenedChat && !is_chatlink; - const isLoading = !currentlyOpenedChat && megaChat.allChatsHadInitialLoadedHistory() === false && routingSection !== 'contacts'; - const rightPane = REaCt().createElement("div", { - className: ` - fm-right-files-block - in-chat - ${is_chatlink ? 'chatlink' : ''} - ` - }, !isLoading && REaCt().createElement(chatToaster.A, { - isRootToaster: true - }), !isLoading && routingSection === 'contacts' && REaCt().createElement(contactsPanel.A, { - megaChat, - contacts: M.u, - received: this.state.ipcData, - sent: M.opc - }), !isLoading && routingSection === 'notFound' && REaCt().createElement("span", null, REaCt().createElement("center", null, "Section not found")), !isLoading && isEmpty && REaCt().createElement(conversationpanel.Yk, { - isMeeting: view === MEETINGS, - onNewChat: () => this.setState({ - contactSelectorDialog: true - }), - onStartMeeting: () => this.startMeeting(), - onScheduleMeeting: () => this.setState({ - scheduleMeetingDialog: true - }) - }), !isLoading && REaCt().createElement(conversationpanel.$h, (0,esm_extends.A)({}, this.props, { - className: routingSection === 'chat' ? '' : 'hidden', - routingSection, - currentlyOpenedChat, - isEmpty, - chatUIFlags, - onToggleExpandedFlag: () => this.setState(() => ({ - callExpanded: call.Ay.isExpanded() - })), - onMount: () => { - const chatRoom = megaChat.getCurrentRoom(); - const view = chatRoom && chatRoom.isMeeting ? MEETINGS : CHATS; - this.setState({ - view - }, () => { - megaChat.currentlyOpenedView = view; - }); - } - }))); - const noteChat = megaChat.getNoteChat(); - return REaCt().createElement("div", { - ref: this.domRef, - className: "conversationsApp" - }, contactSelectorDialog && REaCt().createElement(ui_contactSelectorDialog, { - className: `main-start-chat-dropdown ${leftPanel.NAMESPACE}-contact-selector`, - multiple: false, - topButtons: [{ - key: 'newGroupChat', - title: l[19483], - className: 'positive', - onClick: () => this.setState({ - startGroupChatDialog: true, - contactSelectorDialog: false - }) - }, ...megaChat.WITH_SELF_NOTE ? contactsPanel.A.hasContacts() || noteChat && noteChat.hasMessages() ? [] : [{ - key: 'noteChat', - title: l.note_label, - icon: 'sprite-fm-mono icon-file-text-thin-outline note-chat-icon', - onClick: () => { - closeDialog(); - loadSubPage(`fm/chat/p/${u_handle}`); - } - }] : []], - showAddContact: contactsPanel.A.hasContacts(), - onClose: () => this.setState({ - contactSelectorDialog: false - }), - onSelectDone: selected => { - if (selected.length === 1) { - return megaChat.createAndShowPrivateRoom(selected[0]).then(room => room.setActive()); - } - megaChat.createAndShowGroupRoomFor(selected); - } - }), startGroupChatDialog && REaCt().createElement(StartGroupChatWizard, { - name: "start-group-chat", - flowType: 1, - onClose: () => this.setState({ - startGroupChatDialog: false - }), - onConfirmClicked: () => this.setState({ - startGroupChatDialog: false - }) - }), startMeetingDialog && REaCt().createElement(Start, { - onStart: (topic, audio, video) => { - megaChat.createAndStartMeeting(topic, audio, video); - this.setState({ - startMeetingDialog: false - }); - }, - onClose: () => this.setState({ - startMeetingDialog: false - }) - }), scheduleMeetingDialog && REaCt().createElement(Schedule, { - chatRoom: this.chatRoomRef, - callExpanded, - onClose: () => { - this.setState({ - scheduleMeetingDialog: false - }, () => { - this.chatRoomRef = null; - }); - } - }), scheduleOccurrenceDialog && REaCt().createElement(Edit, { - chatRoom: this.occurrenceRef.scheduledMeeting.chatRoom, - scheduledMeeting: this.occurrenceRef.scheduledMeeting, - occurrenceId: this.occurrenceRef.uid, - callExpanded, - onClose: () => { - this.setState({ - scheduleOccurrenceDialog: false - }, () => { - this.occurrenceRef = null; - }); - } - }), freeCallEndedDialog && REaCt().createElement(FreeCallEnded, { - onClose: () => { - this.setState({ - freeCallEndedDialog: false - }); - } - }), REaCt().createElement(leftPanel, { - view, - views: VIEWS, - routingSection, - conversations: chats, - renderView: view => this.renderView(view), - startMeeting: () => { - this.startMeeting(); - eventlog(500293); - }, - scheduleMeeting: () => { - this.setState({ - scheduleMeetingDialog: true - }); - delay('chat-event-sm-button-main', () => eventlog(99918)); - }, - createNewChat: () => this.setState({ - contactSelectorDialog: true - }) - }), rightPane); - } -} -if (false) // removed by dead control flow -{} -const conversations = { - ConversationsApp -}; - -}, - -691 -(_, EXP_, REQ_) { - -"use strict"; - -// EXPORTS -REQ_.d(EXP_, { - nC: () => API, - kg: () => LABELS, - Ay: () => GifPanel -}); - -// EXTERNAL MODULE: external "React" -const React_ = REQ_(594); -const REaCt = REQ_.n(React_); -// EXTERNAL MODULE: ./js/ui/perfectScrollbar.jsx -const perfectScrollbar = REQ_(486); -;// ./js/chat/ui/gifPanel/searchField.jsx -let _SearchField; - - -class SearchField extends REaCt().Component { - render() { - const { - value, - searching, - onChange, - onReset, - onBack - } = this.props; - return REaCt().createElement("div", { - className: "gif-panel-search" - }, REaCt().createElement("div", { - className: "gif-search-field" - }, searching ? REaCt().createElement("i", { - className: "sprite-fm-mono icon-left", - onClick: onBack - }) : REaCt().createElement("i", { - className: "sprite-fm-mono icon-preview-reveal" - }), REaCt().createElement("input", { - ref: SearchField.inputRef, - type: "text", - placeholder: LABELS.SEARCH, - autoFocus: true, - value, - onChange - }), searching && REaCt().createElement("i", { - className: "sprite-fm-mono icon-close-component", - onClick: onReset - })), REaCt().createElement("div", { - className: "giphy-logo" - }, REaCt().createElement("img", { - src: `${staticpath }images/mega/giphy.gif`, - alt: "PWRD BY GIPHY" - }))); - } -} -_SearchField = SearchField; -SearchField.inputRef = REaCt().createRef(); -SearchField.focus = () => _SearchField.inputRef && _SearchField.inputRef.current && _SearchField.inputRef.current.focus(); -SearchField.hasValue = () => _SearchField.inputRef && _SearchField.inputRef.current && !!_SearchField.inputRef.current.value.length; -;// ./js/chat/ui/gifPanel/result.jsx - - -class Result extends REaCt().Component { - constructor(...args) { - super(...args); - this.resultRef = REaCt().createRef(); - } - componentDidMount() { - let _this$props$onMount, _this$props; - (_this$props$onMount = (_this$props = this.props).onMount) == null || _this$props$onMount.call(_this$props, this.resultRef.current); - } - componentWillUnmount() { - let _this$props$onUnmount, _this$props2; - (_this$props$onUnmount = (_this$props2 = this.props).onUnmount) == null || _this$props$onUnmount.call(_this$props2, this.resultRef.current, 'unobserve'); - } - render() { - const { - image, - title, - onClick - } = this.props; - return REaCt().createElement("div", { - className: ` - ${NODE_CONTAINER_CLASS} - ${onClick ? 'clickable' : ''} - `, - style: { - height: parseInt(image.height) - } - }, REaCt().createElement("div", { - ref: this.resultRef, - className: NODE_CLASS, - style: { - backgroundImage: HAS_INTERSECTION_OBSERVER ? '' : `url(${image.url})` - }, - "data-url": image.url, - onClick - }, REaCt().createElement("span", null, title))); - } -} -;// ./js/chat/ui/gifPanel/resultContainer.jsx - - - -const HAS_INTERSECTION_OBSERVER = typeof IntersectionObserver !== 'undefined'; -const NODE_CONTAINER_CLASS = 'node-container'; -const NODE_CLASS = 'node'; -const RESULT_CONTAINER_CLASS = 'gif-panel-results'; -const RESULTS_END_CLASS = 'results-end'; -const Nil = ({ - children -}) => REaCt().createElement("div", { - className: "no-results-container" -}, REaCt().createElement("div", { - className: "no-results-content" -}, REaCt().createElement("i", { - className: "huge-icon sad-smile" -}), REaCt().createElement("span", null, children))); -class ResultContainer extends REaCt().Component { - constructor(...args) { - super(...args); - this.intersectionObserver = null; - this.initializeIntersectionObserver = () => { - if (HAS_INTERSECTION_OBSERVER) { - this.intersectionObserver = new IntersectionObserver(entries => { - for (let i = 0; i < entries.length; i++) { - var _target$classList, _target$classList2; - const entry = entries[i]; - const {target} = entry; - if ((_target$classList = target.classList) != null && _target$classList.contains(NODE_CLASS)) { - target.style.backgroundImage = entry.isIntersecting ? `url(${target.dataset.url})` : null; - } - if (entry.isIntersecting && (_target$classList2 = target.classList) != null && _target$classList2.contains(RESULTS_END_CLASS)) { - this.props.onPaginate(); - } - } - }); - } - }; - this.toggleIntersectionObserver = (node, action = 'observe') => { - if (node && this.intersectionObserver) { - this.intersectionObserver[action](node); - } - }; - } - componentDidMount() { - this.initializeIntersectionObserver(); - } - componentWillUnmount() { - if (this.intersectionObserver) { - this.intersectionObserver.disconnect(); - this.intersectionObserver = null; - } - } - render() { - const { - loading, - results, - bottom, - unavailable, - onClick - } = this.props; - if (unavailable) { - return REaCt().createElement(Nil, null, LABELS.NOT_AVAILABLE); - } - if (loading && results.length < 1) { - return REaCt().createElement("div", { - className: RESULT_CONTAINER_CLASS - }, Array.from({ - length: API.LIMIT - }, (element, index) => REaCt().createElement("div", { - key: index, - className: NODE_CONTAINER_CLASS - }, REaCt().createElement("div", { - className: NODE_CLASS, - style: { - height: Math.floor(Math.random() * 150) + 100 - } - })))); - } - if (!loading && results.length < 1) { - return REaCt().createElement(Nil, null, LABELS.NO_RESULTS); - } - if (results.length) { - return REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("div", { - className: RESULT_CONTAINER_CLASS - }, results.map(({ - slug, - images: { - fixed_width_downsampled - }, - title - }, index) => { - return REaCt().createElement(Result, { - key: `${slug}--${index}`, - image: fixed_width_downsampled, - title, - onClick: () => onClick(results[index]), - onMount: this.toggleIntersectionObserver, - onUnmount: this.toggleIntersectionObserver - }); - })), REaCt().createElement("div", { - className: RESULTS_END_CLASS, - ref: node => this.toggleIntersectionObserver(node), - style: { - visibility: bottom ? 'visible' : 'hidden' - } - }, REaCt().createElement("img", { - className: "emoji", - alt: "\\ud83d\\ude10", - src: `${staticpath}/images/mega/twemojis/2_v2/72x72/1f610.png` - }), REaCt().createElement("strong", null, LABELS.END_OF_RESULTS))); - } - return null; - } -} -;// ./js/chat/ui/gifPanel/gifPanel.jsx - - - - -const GIF_PANEL_CLASS = 'gif-panel-wrapper'; -const MAX_HEIGHT = 550; -const API = { - HOSTNAME: 'https://giphy.mega.nz/', - ENDPOINT: 'v1/gifs', - SCHEME: 'giphy://', - convert: path => { - if (path && typeof path === 'string') { - const FORMAT = [API.SCHEME, API.HOSTNAME]; - if (path.indexOf(API.SCHEME) === 0 || path.indexOf(API.HOSTNAME) === 0) { - return String.prototype.replace.apply(path, path.indexOf(API.SCHEME) === 0 ? FORMAT : FORMAT.reverse()); - } - } - }, - LIMIT: 50, - OFFSET: 50 -}; -const LABELS = freeze({ - get SEARCH() { - return l[24025]; - }, - get NO_RESULTS() { - return l[24050]; - }, - get NOT_AVAILABLE() { - return l[24512]; - }, - get END_OF_RESULTS() { - return l[24156]; - } -}); -class GifPanel extends REaCt().Component { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - this.pathRef = ''; - this.controllerRef = null; - this.fetchRef = null; - this.delayProcID = null; - this.defaultState = { - value: '', - searching: false, - results: [], - loading: true, - offset: 0, - bottom: false, - unavailable: false - }; - this.state = { - ...this.defaultState - }; - this.getContainerHeight = () => window.innerHeight * 0.6 > MAX_HEIGHT ? MAX_HEIGHT : window.innerHeight * 0.6; - this.getFormattedPath = path => { - const PATH = path + (path.indexOf('?') === -1 ? '?' : '&'); - const LIMIT = `limit=${API.LIMIT}`; - return `${API.HOSTNAME + API.ENDPOINT}/${PATH + LIMIT}`; - }; - this.clickedOutsideComponent = ev => { - const $target = ev && $(ev.target); - return $target.parents(`.${GIF_PANEL_CLASS}`).length === 0 && ['.small-icon.tiny-reset', '.small-icon.gif'].every(outsideElement => !$target.is(outsideElement)); - }; - this.bindEvents = () => { - $(document).rebind('mousedown.gifPanel', ev => { - if (this.clickedOutsideComponent(ev)) { - this.props.onToggle(); - } - }).rebind('keydown.gifPanel', ({ - keyCode - }) => { - if (keyCode && keyCode === 27) { - return SearchField.hasValue() ? this.doReset() : this.props.onToggle(); - } - }); - }; - this.unbindEvents = () => { - if (this.delayProcID) { - delay.cancel(this.delayProcID); - } - $(document).unbind('.gifPanel'); - }; - this.doFetch = path => { - this.setState({ - loading: true, - unavailable: false - }, () => { - this.pathRef = path; - this.controllerRef = typeof AbortController === 'function' && new AbortController(); - this.fetchRef = fetch(this.getFormattedPath(path), { - signal: this.controllerRef.signal - }).then(response => response.json()).then(({ - data - }) => { - this.fetchRef = this.pathRef = null; - if (this.domRef.current) { - if (data && data.length) { - return this.setState(state => ({ - results: [...state.results, ...data], - loading: false - })); - } - return this.setState({ - bottom: true, - loading: false - }, () => this.resultContainerRef && this.resultContainerRef.reinitialise()); - } - }).catch(ex => { - return ex.name === 'AbortError' ? null : this.setState({ - unavailable: true - }); - }); - }); - }; - this.doPaginate = () => { - const { - value, - loading, - searching - } = this.state; - if (!loading) { - this.setState(state => ({ - offset: state.offset + API.OFFSET - }), () => { - this.doFetch(searching ? `search?q=${escape(value)}&offset=${this.state.offset}` : `trending?offset=${this.state.offset}`); - }); - } - }; - this.doReset = () => { - this.setState({ - ...this.defaultState - }, () => { - this.doFetch('trending'); - onIdle(() => SearchField.focus()); - this.resultContainerRef.scrollToY(0); - }); - }; - this.handleChange = ev => { - const { - value - } = ev.target; - const searching = value.length >= 2; - if (value.length === 0) { - return this.doReset(); - } - if (this.fetchRef !== null && this.pathRef === 'trending' && this.controllerRef) { - this.controllerRef.abort(); - this.fetchRef = this.pathRef = null; - } - this.setState(state => ({ - ...this.defaultState, - value, - searching, - results: searching ? [] : state.results - }), () => { - this.resultContainerRef.scrollToY(0); - this.delayProcID = searching ? delay('gif-search', () => this.doFetch(`search?q=${escape(value)}`), 1600) : null; - }); - }; - this.handleBack = () => this.doReset(); - this.doSend = result => { - const { - mp4, - webp, - mp4_size, - webp_size, - width, - height - } = result.images.fixed_height; - const message = Message.MANAGEMENT_MESSAGE_TYPES.MANAGEMENT + Message.MANAGEMENT_MESSAGE_TYPES.CONTAINS_META + Message.MESSAGE_META_TYPE.GIPHY + JSON.stringify({ - textMessage: result.title, - src: API.convert(mp4), - src_webp: API.convert(webp), - s: mp4_size, - s_webp: webp_size, - w: width, - h: height - }); - this.props.chatRoom.sendMessage(message); - this.props.onToggle(); - }; - } - componentDidMount() { - if (this.state.results && this.state.results.length === 0) { - this.doFetch('trending'); - } - this.bindEvents(); - } - componentWillUnmount() { - this.unbindEvents(); - } - render() { - const { - value, - searching, - results, - loading, - bottom, - unavailable - } = this.state; - return REaCt().createElement("div", { - ref: this.domRef, - className: "gif-panel-wrapper" - }, REaCt().createElement("div", { - className: "gif-panel", - style: { - height: this.getContainerHeight() - } - }, REaCt().createElement("div", { - className: "gif-panel-header" - }, REaCt().createElement(SearchField, { - value, - searching, - onChange: this.handleChange, - onReset: this.doReset, - onBack: this.handleBack - })), REaCt().createElement("div", { - className: "gif-panel-content" - }, REaCt().createElement(perfectScrollbar.O, { - ref: container => { - this.resultContainerRef = container; - }, - options: { - 'suppressScrollX': true - } - }, REaCt().createElement(ResultContainer, { - results, - loading, - bottom, - unavailable, - onPaginate: this.doPaginate, - onClick: this.doSend - }))))); - } -} - -}, - -814 -(_, EXP_, REQ_) { - -"use strict"; - -// EXPORTS -REQ_.d(EXP_, { - A: () => HistoryPanel -}); - -// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/applyDecoratedDescriptor.js -const applyDecoratedDescriptor = REQ_(793); -// EXTERNAL MODULE: external "React" -const React_ = REQ_(594); -const REaCt = REQ_.n(React_); -// EXTERNAL MODULE: ./js/chat/mixins.js -const mixins = REQ_(137); -// EXTERNAL MODULE: ./js/ui/utils.jsx -const utils = REQ_(314); -;// ./js/chat/ui/messages/alterParticipants.jsx -const React = REQ_(594); -const ContactsUI = REQ_(251); -const ConversationMessageMixin = REQ_(446).M; - -class AltPartsConvMessage extends ConversationMessageMixin { - haveMoreContactListeners() { - if (!this.props.message || !this.props.message.meta) { - return false; - } - const { - included, - excluded - } = this.props.message.meta; - return array.unique([...included || [], ...excluded || []]); - } - render() { - const self = this; - const {message} = this.props; - const contact = self.getContact(); - const timestampInt = self.getTimestamp(); - const timestamp = self.getTimestampAsString(); - const datetime = React.createElement("div", { - className: "message date-time simpletip", - "data-simpletip": time2date(timestampInt, 17) - }, timestamp); - let displayName; - if (contact) { - displayName = M.getNameByHandle(contact.u); - } else { - displayName = contact; - } - const messages = []; - message.meta.included.forEach((h) => { - const otherContact = M.u[h] ? M.u[h] : { - 'u': h, - h, - 'c': 0 - }; - const avatar = React.createElement(ContactsUI.Avatar, { - contact: otherContact, - chatRoom: self.props.chatRoom, - className: "message avatar-wrapper small-rounded-avatar" - }); - const otherDisplayName = M.getNameByHandle(otherContact.u); - const isSelfJoin = h === contact.u; - let text = isSelfJoin ? l[23756] : l[8907]; - if (self.props.chatRoom.isMeeting) { - text = isSelfJoin ? l.meeting_mgmt_user_joined : l.meeting_mgmt_user_added; - } - text = text.replace('%1', megaChat.html(otherDisplayName)); - if (!isSelfJoin) { - text = text.replace('%2', `${megaChat.html(displayName)}`); - } - messages.push(React.createElement("div", { - className: "message body", - "data-id": `id${ message.messageId}`, - key: `${message.messageId }_${ h}` - }, avatar, React.createElement("div", { - className: "message content-area small-info-txt selectable-txt" - }, React.createElement(ContactsUI.ContactButton, { - className: "message", - contact: otherContact, - chatRoom: self.props.chatRoom, - label: React.createElement(utils.zT, null, otherDisplayName) - }), datetime, React.createElement("div", { - className: "message text-block" - }, React.createElement(utils.P9, null, text))))); - }); - message.meta.excluded.forEach((h) => { - const otherContact = M.u[h] ? M.u[h] : { - 'u': h, - h, - 'c': 0 - }; - const avatar = React.createElement(ContactsUI.Avatar, { - contact: otherContact, - chatRoom: self.props.chatRoom, - className: "message avatar-wrapper small-rounded-avatar" - }); - const otherDisplayName = M.getNameByHandle(otherContact.u); - let text; - if (otherContact.u === contact.u) { - text = self.props.chatRoom.isMeeting ? l.meeting_mgmt_left : l[8908]; - } else { - text = (self.props.chatRoom.isMeeting ? l.meeting_mgmt_kicked : l[8906]).replace("%s", `${megaChat.html(displayName)}`); - } - messages.push(React.createElement("div", { - className: "message body", - "data-id": `id${ message.messageId}`, - key: `${message.messageId }_${ h}` - }, avatar, React.createElement("div", { - className: "message content-area small-info-txt selectable-txt" - }, React.createElement(ContactsUI.ContactButton, { - className: "message", - chatRoom: self.props.chatRoom, - contact: otherContact, - label: React.createElement(utils.zT, null, otherDisplayName) - }), datetime, React.createElement("div", { - className: "message text-block" - }, React.createElement(utils.P9, null, text))))); - }); - return React.createElement("div", null, messages); - } -} - -;// ./js/chat/ui/messages/truncated.jsx -const truncated_React = REQ_(594); -const truncated_ContactsUI = REQ_(251); -const truncated_ConversationMessageMixin = REQ_(446).M; - -class TruncatedMessage extends truncated_ConversationMessageMixin { - render() { - const self = this; - let cssClasses = "message body"; - const {message} = this.props; - const {chatRoom} = this.props.message; - const contact = self.getContact(); - const timestampInt = self.getTimestamp(); - const timestamp = self.getTimestampAsString(); - let datetime = truncated_React.createElement("div", { - className: "message date-time simpletip", - "data-simpletip": time2date(timestampInt, 17) - }, timestamp); - let displayName; - if (contact) { - displayName = M.getNameByHandle(contact.u); - } else { - displayName = contact; - } - let avatar = null; - if (this.props.grouped) { - cssClasses += " grouped"; - } else { - avatar = truncated_React.createElement(truncated_ContactsUI.Avatar, { - contact, - className: "message avatar-wrapper small-rounded-avatar", - chatRoom - }); - datetime = truncated_React.createElement("div", { - className: "message date-time simpletip", - "data-simpletip": time2date(timestampInt, 17) - }, timestamp); - } - return truncated_React.createElement("div", { - className: cssClasses, - "data-id": `id${ message.messageId}`, - key: message.messageId - }, avatar, truncated_React.createElement("div", { - className: "message content-area small-info-txt selectable-txt" - }, truncated_React.createElement(truncated_ContactsUI.ContactButton, { - contact, - className: "message", - label: truncated_React.createElement(utils.zT, null, displayName), - chatRoom - }), datetime, truncated_React.createElement("div", { - className: "message text-block" - }, l[8905]))); - } -} - -;// ./js/chat/ui/messages/privilegeChange.jsx -const privilegeChange_React = REQ_(594); -const privilegeChange_ContactsUI = REQ_(251); -const privilegeChange_ConversationMessageMixin = REQ_(446).M; - -class PrivilegeChange extends privilegeChange_ConversationMessageMixin { - haveMoreContactListeners() { - if (!this.props.message.meta || !this.props.message.meta.targetUserId) { - return false; - } - const uid = this.props.message.meta.targetUserId; - if (uid && M.u[uid]) { - return uid; - } - return false; - } - render() { - const self = this; - const {message} = this.props; - const {chatRoom} = this.props.message; - const contact = self.getContact(); - const timestampInt = self.getTimestamp(); - const timestamp = self.getTimestampAsString(); - const datetime = privilegeChange_React.createElement("div", { - className: "message date-time simpletip", - "data-simpletip": time2date(timestampInt, 17) - }, timestamp); - let displayName; - if (contact) { - displayName = M.getNameByHandle(contact.u); - } else { - displayName = contact; - } - const messages = []; - const otherContact = M.u[message.meta.targetUserId] ? M.u[message.meta.targetUserId] : { - 'u': message.meta.targetUserId, - 'h': message.meta.targetUserId, - 'c': 0 - }; - const avatar = privilegeChange_React.createElement(privilegeChange_ContactsUI.Avatar, { - contact: otherContact, - className: "message avatar-wrapper small-rounded-avatar", - chatRoom - }); - const otherDisplayName = M.getNameByHandle(otherContact.u); - let newPrivilegeText = ""; - if (message.meta.privilege === 3) { - newPrivilegeText = l.priv_change_to_op; - } else if (message.meta.privilege === 2) { - newPrivilegeText = l.priv_change_to_std; - } else if (message.meta.privilege === 0) { - newPrivilegeText = l.priv_change_to_ro; - } - const text = newPrivilegeText.replace('[S]', '').replace('[/S]', '').replace('%s', `${megaChat.html(displayName)}`); - messages.push(privilegeChange_React.createElement("div", { - className: "message body", - "data-id": `id${ message.messageId}`, - key: message.messageId - }, avatar, privilegeChange_React.createElement("div", { - className: "message content-area small-info-txt selectable-txt" - }, privilegeChange_React.createElement(privilegeChange_ContactsUI.ContactButton, { - className: "message", - chatRoom: self.props.chatRoom, - contact: otherContact, - label: privilegeChange_React.createElement(utils.zT, null, otherDisplayName) - }), datetime, privilegeChange_React.createElement("div", { - className: "message text-block" - }, privilegeChange_React.createElement(utils.P9, null, text))))); - return privilegeChange_React.createElement("div", null, messages); - } -} - -;// ./js/chat/ui/messages/topicChange.jsx -const topicChange_React = REQ_(594); -const topicChange_ContactsUI = REQ_(251); -const topicChange_ConversationMessageMixin = REQ_(446).M; - -class TopicChange extends topicChange_ConversationMessageMixin { - render() { - const self = this; - const {message} = this.props; - const {megaChat} = this.props.message.chatRoom; - const {chatRoom} = this.props.message; - if (message.meta.isScheduled) { - return null; - } - const contact = self.getContact(); - const timestampInt = self.getTimestamp(); - const timestamp = self.getTimestampAsString(); - const datetime = topicChange_React.createElement("div", { - className: "message date-time simpletip", - "data-simpletip": time2date(timestampInt, 17) - }, timestamp); - let displayName; - if (contact) { - displayName = M.getNameByHandle(contact.u); - } else { - displayName = contact; - } - const messages = []; - const avatar = topicChange_React.createElement(topicChange_ContactsUI.Avatar, { - contact, - chatRoom, - className: "message avatar-wrapper small-rounded-avatar" - }); - const topic = megaChat.html(message.meta.topic); - const oldTopic = megaChat.html(message.meta.oldTopic) || ''; - messages.push(topicChange_React.createElement("div", { - className: "message body", - "data-id": `id${ message.messageId}`, - key: message.messageId - }, avatar, topicChange_React.createElement("div", { - className: "message content-area small-info-txt selectable-txt" - }, topicChange_React.createElement(topicChange_ContactsUI.ContactButton, { - className: "message", - chatRoom, - contact, - label: topicChange_React.createElement(utils.zT, null, displayName) - }), datetime, topicChange_React.createElement("div", { - className: "message text-block" - }, topicChange_React.createElement(utils.P9, null, (chatRoom.scheduledMeeting ? l.schedule_mgmt_title.replace('%1', `${oldTopic}`) : l[9081]).replace('%s', `${topic}`)))))); - return topicChange_React.createElement("div", null, messages); - } -} - -;// ./js/chat/ui/messages/closeOpenMode.jsx -const closeOpenMode_React = REQ_(594); -const closeOpenMode_ContactsUI = REQ_(251); -const closeOpenMode_ConversationMessageMixin = REQ_(446).M; - -class CloseOpenModeMessage extends closeOpenMode_ConversationMessageMixin { - render() { - const self = this; - let cssClasses = "message body"; - const {message} = this.props; - const contact = self.getContact(); - const timestampInt = self.getTimestamp(); - const timestamp = self.getTimestampAsString(); - let datetime = closeOpenMode_React.createElement("div", { - className: "message date-time", - title: time2date(timestampInt) - }, timestamp); - let displayName; - if (contact) { - displayName = M.getNameByHandle(contact.u); - } else { - displayName = contact; - } - let avatar = null; - if (this.props.grouped) { - cssClasses += " grouped"; - } else { - avatar = closeOpenMode_React.createElement(closeOpenMode_ContactsUI.Avatar, { - contact, - className: "message avatar-wrapper small-rounded-avatar", - chatRoom: this.props.chatRoom - }); - datetime = closeOpenMode_React.createElement("div", { - className: "message date-time", - title: time2date(timestampInt) - }, timestamp); - } - return closeOpenMode_React.createElement("div", { - className: cssClasses, - "data-id": `id${ message.messageId}`, - key: message.messageId - }, avatar, closeOpenMode_React.createElement("div", { - className: "message content-area small-info-txt selectable-txt" - }, closeOpenMode_React.createElement("div", { - className: "message user-card-name" - }, closeOpenMode_React.createElement(utils.zT, null, displayName)), datetime, closeOpenMode_React.createElement("div", { - className: "message text-block" - }, l[20569]))); - } -} - -;// ./js/chat/ui/messages/chatHandle.jsx -const chatHandle_React = REQ_(594); -const chatHandle_ContactsUI = REQ_(251); -const chatHandle_ConversationMessageMixin = REQ_(446).M; - -class ChatHandleMessage extends chatHandle_ConversationMessageMixin { - render() { - const self = this; - let cssClasses = "message body"; - const {message} = this.props; - const contact = self.getContact(); - const timestampInt = self.getTimestamp(); - const timestamp = self.getTimestampAsString(); - let datetime = chatHandle_React.createElement("div", { - className: "message date-time", - title: time2date(timestampInt) - }, timestamp); - let displayName; - if (contact) { - displayName = M.getNameByHandle(contact.u); - } else { - displayName = contact; - } - let avatar = null; - if (this.props.grouped) { - cssClasses += " grouped"; - } else { - avatar = chatHandle_React.createElement(chatHandle_ContactsUI.Avatar, { - contact, - className: "message avatar-wrapper small-rounded-avatar", - chatRoom: this.props.chatRoom - }); - datetime = chatHandle_React.createElement("div", { - className: "message date-time", - title: time2date(timestampInt) - }, timestamp); - } - return chatHandle_React.createElement("div", { - className: cssClasses, - "data-id": `id${ message.messageId}`, - key: message.messageId - }, avatar, chatHandle_React.createElement("div", { - className: "message content-area small-info-txt selectable-txt" - }, chatHandle_React.createElement("div", { - className: "message user-card-name" - }, chatHandle_React.createElement(utils.zT, null, displayName)), datetime, chatHandle_React.createElement("div", { - className: "message text-block" - }, message.meta.handleUpdate === 1 ? l[20570] : l[20571]))); - } -} - -// EXTERNAL MODULE: ./js/chat/ui/messages/generic.jsx + 14 modules -const generic = REQ_(890); -// EXTERNAL MODULE: ./js/ui/perfectScrollbar.jsx -const perfectScrollbar = REQ_(486); -// EXTERNAL MODULE: ./js/chat/ui/messages/mixin.jsx -const mixin = REQ_(446); -// EXTERNAL MODULE: ./js/chat/ui/contacts.jsx -const contacts = REQ_(251); -;// ./js/chat/ui/messages/retentionChange.jsx - - - - -class RetentionChange extends mixin.M { - render() { - const { - message - } = this.props; - const contact = this.getContact(); - return REaCt().createElement("div", { - className: "message body", - "data-id": `id${ message.messageId}`, - key: message.messageId - }, REaCt().createElement(contacts.Avatar, { - contact, - className: "message avatar-wrapper small-rounded-avatar" - }), REaCt().createElement("div", { - className: "message content-area small-info-txt selectable-txt" - }, REaCt().createElement(contacts.ContactButton, { - contact, - className: "message", - label: REaCt().createElement(utils.zT, null, M.getNameByHandle(contact.u)) - }), REaCt().createElement("div", { - className: "message date-time simpletip", - "data-simpletip": time2date(this.getTimestamp(), 17) - }, this.getTimestampAsString()), REaCt().createElement("div", { - className: "message text-block" - }, message.getMessageRetentionSummary()))); - } -} -// EXTERNAL MODULE: ./js/chat/ui/meetings/call.jsx + 11 modules -const call = REQ_(3); -// EXTERNAL MODULE: ./js/chat/ui/messages/scheduleMetaChange.jsx -const scheduleMetaChange = REQ_(757); -;// ./js/chat/ui/historyPanel.jsx - -let _dec, _class; - - - - - - - - - - - - - - -const HistoryPanel = (_dec = (0,mixins.hG)(450, true), _class = class HistoryPanel extends mixins.w9 { - constructor(props) { - super(props); - this.$container = null; - this.$messages = null; - this.domRef = REaCt().createRef(); - this.state = { - editing: false, - toast: false - }; - this.renderNotice = label => REaCt().createElement("div", { - className: "dropdown body dropdown-arrow down-arrow tooltip not-sent-notification-cancel hidden" - }, REaCt().createElement("i", { - className: "dropdown-white-arrow" - }), REaCt().createElement("div", { - className: "dropdown notification-text" - }, REaCt().createElement("i", { - className: "small-icon conversations" - }), label)); - this.renderLoadingSpinner = () => REaCt().createElement("div", { - style: { - top: '50%' - }, - className: ` - loading-spinner - js-messages-loading - light - manual-management - ${this.loadingShown ? '' : 'hidden'} - ` - }, REaCt().createElement("div", { - className: "main-loader", - style: { - position: 'fixed', - top: '50%', - left: '50%' - } - })); - this.renderNavigationToast = () => { - const { - chatRoom - } = this.props; - const unreadCount = chatRoom.messagesBuff.getUnreadCount(); - return REaCt().createElement("div", { - className: ` - theme-dark-forced - messages-toast - ${this.state.toast ? 'active' : ''} - `, - onClick: () => { - this.setState({ - toast: false - }, () => { - this.messagesListScrollable.scrollToBottom(); - chatRoom.scrolledToBottom = true; - }); - } - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-down" - }), unreadCount > 0 && REaCt().createElement("span", null, unreadCount > 9 ? '9+' : unreadCount)); - }; - this.onKeyboardScroll = ({ - keyCode - }) => { - let _scrollbar$domRef; - const scrollbar = this.messagesListScrollable; - const domNode = scrollbar == null || (_scrollbar$domRef = scrollbar.domRef) == null ? void 0 : _scrollbar$domRef.current; - if (domNode && this.isComponentEventuallyVisible() && !this.state.attachCloudDialog) { - const scrollPositionY = scrollbar.getScrollPositionY(); - const offset = parseInt(domNode.style.height); - const PAGE = { - UP: 33, - DOWN: 34 - }; - switch (keyCode) { - case PAGE.UP: - scrollbar.scrollToY(scrollPositionY - offset, true); - this.onMessagesScrollUserScroll(scrollbar, 100); - break; - case PAGE.DOWN: - if (!scrollbar.isAtBottom()) { - scrollbar.scrollToY(scrollPositionY + offset, true); - } - break; - } - } - }; - this.onMessagesScrollUserScroll = (ps, offset = 5) => { - const { - chatRoom - } = this.props; - const { - messagesBuff - } = chatRoom; - const scrollPositionY = ps.getScrollPositionY(); - if (messagesBuff.messages.length === 0) { - chatRoom.scrolledToBottom = true; - return; - } - if (ps.isCloseToBottom(30) === true) { - if (!chatRoom.scrolledToBottom) { - messagesBuff.detachMessages(); - } - chatRoom.scrolledToBottom = true; - } else { - chatRoom.scrolledToBottom = false; - } - if (!this.scrollPullHistoryRetrieval && !messagesBuff.isRetrievingHistory && (ps.isAtTop() || scrollPositionY < offset && ps.getScrollHeight() > 500) && messagesBuff.haveMoreHistory()) { - ps.disable(); - this.scrollPullHistoryRetrieval = true; - this.lastScrollPosition = scrollPositionY; - let msgAppended = 0; - const scrYOffset = ps.getScrollHeight(); - chatRoom.one('onMessagesBuffAppend.pull', () => { - msgAppended++; - }); - chatRoom.off('onHistoryDecrypted.pull'); - chatRoom.one('onHistoryDecrypted.pull', () => { - chatRoom.off('onMessagesBuffAppend.pull'); - if (msgAppended > 0) { - this._reposOnUpdate = scrYOffset; - } - this.scrollPullHistoryRetrieval = -1; - }); - messagesBuff.retrieveChatHistory(); - } - if (this.lastScrollPosition !== scrollPositionY) { - this.lastScrollPosition = scrollPositionY; - } - delay('chat-toast', this.initToast, 200); - }; - this.initToast = () => { - let _this$messagesListScr; - const { - chatRoom - } = this.props; - return this.isMounted() && this.setState({ - toast: !chatRoom.scrolledToBottom && !((_this$messagesListScr = this.messagesListScrollable) != null && _this$messagesListScr.isCloseToBottom != null && _this$messagesListScr.isCloseToBottom(30)) - }, () => this.state.toast ? null : chatRoom.trigger('onChatIsFocused')); - }; - this.handleWindowResize = this._handleWindowResize.bind(this); - } - customIsEventuallyVisible() { - return this.props.chatRoom.isCurrentlyActive; - } - UNSAFE_componentWillMount() { - let _chatRoom$messagesBuf; - const { - chatRoom - } = this.props; - chatRoom.rebind('onHistoryDecrypted.cp', () => this.eventuallyUpdate()); - this._messagesBuffChangeHandler = (_chatRoom$messagesBuf = chatRoom.messagesBuff) == null ? void 0 : _chatRoom$messagesBuf.addChangeListener(SoonFc(() => { - if (this.isComponentEventuallyVisible()) { - let _this$domRef; - $('.js-messages-scroll-area', (_this$domRef = this.domRef) == null ? void 0 : _this$domRef.current).trigger('forceResize', [true]); - } - this.refreshUI(); - })); - } - componentDidMount() { - super.componentDidMount(); - const { - chatRoom, - onMount - } = this.props; - window.addEventListener('resize', this.handleWindowResize); - window.addEventListener('keydown', this.handleKeyDown); - this.$container = $(`.conversation-panel[data-room-id="${chatRoom.chatId}"]`); - this.eventuallyInit(); - chatRoom.trigger('onHistoryPanelComponentDidMount'); - if (onMount) { - onMount(this); - } - } - componentWillUnmount() { - super.componentWillUnmount(); - const { - chatRoom - } = this.props; - if (this._messagesBuffChangeHandler) { - let _chatRoom$messagesBuf2; - (_chatRoom$messagesBuf2 = chatRoom.messagesBuff) == null || _chatRoom$messagesBuf2.removeChangeListener(this._messagesBuffChangeHandler); - delete this._messagesBuffChangeHandler; - } - window.removeEventListener('resize', this.handleWindowResize); - window.removeEventListener('keydown', this.handleKeyDown); - $(document).off(`fullscreenchange.megaChat_${chatRoom.roomId}`); - $(document).off(`keydown.keyboardScroll_${chatRoom.roomId}`); - } - componentDidUpdate(prevProps, prevState) { - let _self$domRef; - const self = this; - self.eventuallyInit(false); - const domNode = (_self$domRef = self.domRef) == null ? void 0 : _self$domRef.current; - const jml = domNode && domNode.querySelector('.js-messages-loading'); - if (jml) { - if (self.loadingShown) { - jml.classList.remove('hidden'); - } else { - jml.classList.add('hidden'); - } - } - self.handleWindowResize(); - if (prevState.editing === false && self.state.editing !== false && self.messagesListScrollable) { - self.messagesListScrollable.reinitialise(false); - Soon(() => { - if (self.editDomElement && self.editDomElement.length === 1) { - self.messagesListScrollable.scrollToElement(self.editDomElement[0], false); - } - }); - } - if (self._reposOnUpdate !== undefined) { - const ps = self.messagesListScrollable; - ps.__prevPosY = ps.getScrollHeight() - self._reposOnUpdate + self.lastScrollPosition; - ps.scrollToY(ps.__prevPosY, true); - } - } - eventuallyInit(doResize) { - let _this$domRef2; - if (this.initialised) { - return; - } - const domNode = (_this$domRef2 = this.domRef) == null ? void 0 : _this$domRef2.current; - if (domNode) { - this.initialised = true; - } else { - return; - } - this.$messages = $('.messages.scroll-area > .perfectScrollbarContainer', this.$container); - this.$messages.droppable({ - tolerance: 'pointer', - drop(e, ui) { - $.doDD(e, ui, 'drop', 1); - }, - over(e, ui) { - $.doDD(e, ui, 'over', 1); - }, - out(e, ui) { - $.doDD(e, ui, 'out', 1); - } - }); - this.lastScrollPosition = null; - this.props.chatRoom.scrolledToBottom = true; - if (doResize !== false) { - this.handleWindowResize(); - } - } - _handleWindowResize(e, scrollToBottom) { - if (!M.chat) { - return; - } - if (!this.isMounted()) { - this.componentWillUnmount(); - return; - } - if (!this.isComponentEventuallyVisible()) { - return; - } - const self = this; - self.eventuallyInit(false); - if (!self.$messages) { - return; - } - if (call.Ay.isExpanded()) { - const $container = $('.meetings-call'); - const $messages = $('.js-messages-scroll-area', $container); - const $textarea = $('.chat-textarea-block', $container); - const $sidebar = $('.sidebar', $container); - const scrollBlockHeight = parseInt($sidebar.outerHeight(), 10) - parseInt($textarea.outerHeight(), 10) - 72; - if ($sidebar.hasClass('chat-opened') && scrollBlockHeight !== $messages.outerHeight()) { - $messages.css('height', scrollBlockHeight); - self.refreshUI(true); - } - return; - } - const scrollBlockHeight = $('.chat-content-block', self.$container).outerHeight() - ($('.chat-topic-block', self.$container).outerHeight() || 0) - (is_chatlink ? $('.join-chat-block', self.$container).outerHeight() : $('.messages-block .chat-textarea-block', self.$container).outerHeight()); - if (scrollBlockHeight !== self.$messages.outerHeight()) { - self.$messages.css('height', scrollBlockHeight); - $('.messages.main-pad', self.$messages).css('min-height', scrollBlockHeight); - self.refreshUI(true); - } else { - self.refreshUI(scrollToBottom); - } - } - refreshUI() { - if (this.isComponentEventuallyVisible()) { - const room = this.props.chatRoom; - room.renderContactTree(); - room.megaChat.refreshConversations(); - room.trigger('RefreshUI'); - if (room.scrolledToBottom) { - delay(`hp:reinit-scroll:${this.getUniqueId()}`, () => { - if (this.messagesListScrollable) { - this.messagesListScrollable.reinitialise(true, true); - } - }, 30); - } - } - } - isLoading() { - const {chatRoom} = this.props; - if (chatRoom.historyTimedOut) { - return false; - } - const mb = chatRoom.messagesBuff; - return this.scrollPullHistoryRetrieval === true || chatRoom.activeSearches || mb.messagesHistoryIsLoading() || mb.joined === false || mb.isDecrypting; - } - specShouldComponentUpdate() { - return !this.loadingShown && this.isComponentEventuallyVisible(); - } - enableScrollbar() { - const ps = this.messagesListScrollable; - ps.enable(); - this._reposOnUpdate = undefined; - this.lastScrollPosition = ps.__prevPosY | 0; - } - editMessage(messageId) { - const self = this; - self.setState({ - 'editing': messageId - }); - self.props.chatRoom.scrolledToBottom = false; - } - onMessageEditDone(v, messageContents) { - const self = this; - const room = this.props.chatRoom; - room.scrolledToBottom = true; - self.editDomElement = null; - const currentContents = v.textContents; - v.edited = false; - if (messageContents === false || messageContents === currentContents) { - let _self$messagesListScr; - (_self$messagesListScr = self.messagesListScrollable) == null || _self$messagesListScr.scrollToBottom(true); - } else if (messageContents) { - let _self$messagesListScr2; - room.trigger('onMessageUpdating', v); - room.megaChat.plugins.chatdIntegration.updateMessage(room, v.internalId ? v.internalId : v.orderValue, messageContents); - if (v.getState && (v.getState() === Message.STATE.NOT_SENT || v.getState() === Message.STATE.SENT) && !v.requiresManualRetry) { - if (v.textContents) { - v.textContents = messageContents; - } - if (v.emoticonShortcutsProcessed) { - v.emoticonShortcutsProcessed = false; - } - if (v.emoticonsProcessed) { - v.emoticonsProcessed = false; - } - if (v.messageHtml) { - delete v.messageHtml; - } - v.trigger('onChange', [v, "textContents", "", messageContents]); - megaChat.plugins.richpreviewsFilter.processMessage({}, v, false, true); - } - (_self$messagesListScr2 = self.messagesListScrollable) == null || _self$messagesListScr2.scrollToBottom(true); - } else if (messageContents.length === 0) { - this.props.onDeleteClicked(v); - } - self.setState({ - 'editing': false - }); - self.refreshUI(); - Soon(() => { - $('.chat-textarea-block:visible textarea').focus(); - }, 300); - } - render() { - const self = this; - const room = this.props.chatRoom; - if (!room || !room.roomId) { - return null; - } - const contacts = room.getParticipantsExceptMe(); - let contactHandle; - let contact; - let avatarMeta; - let contactName = ""; - if (contacts && contacts.length === 1) { - contactHandle = contacts[0]; - contact = M.u[contactHandle]; - avatarMeta = contact ? generateAvatarMeta(contact.u) : {}; - contactName = avatarMeta.fullName; - } else if (contacts && contacts.length > 1) { - contactName = room.getRoomTitle(); - } - let messagesList = []; - if (this.isLoading()) { - self.loadingShown = true; - } else { - const mb = room.messagesBuff; - if (this.scrollPullHistoryRetrieval < 0) { - this.scrollPullHistoryRetrieval = false; - self.enableScrollbar(); - } - delete self.loadingShown; - if (room.historyTimedOut || mb.joined === true && !self.scrollPullHistoryRetrieval && mb.haveMoreHistory() === false) { - const $$WELCOME_MESSAGE = ({ - heading, - title, - info, - className - }) => REaCt().createElement("div", { - className: ` - messages - welcome-message - ${className || ''} - ` - }, REaCt().createElement(utils.P9, { - tag: "h1", - content: heading - }), title && REaCt().createElement("span", null, title), info); - messagesList = [...messagesList, room.isNote ? $$WELCOME_MESSAGE({ - heading: l.note_heading, - info: REaCt().createElement("p", null, REaCt().createElement("i", { - className: "sprite-fm-mono icon-file-text-thin-outline note-chat-icon" - }), l.note_description), - className: 'note-chat-info' - }) : $$WELCOME_MESSAGE({ - heading: room.scheduledMeeting || !contactName ? megaChat.html(room.getRoomTitle()) : l[8002].replace('%s', `${megaChat.html(contactName)}`), - title: l[8080], - info: REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("p", null, REaCt().createElement("i", { - className: "sprite-fm-mono icon-lock" - }), REaCt().createElement(utils.P9, { - content: l[8540].replace("[S]", "").replace("[/S]", "") - })), REaCt().createElement("p", null, REaCt().createElement("i", { - className: "sprite-fm-mono icon-accept" - }), REaCt().createElement(utils.P9, { - content: l[8539].replace("[S]", "").replace("[/S]", "") - }))) - })]; - } - } - let lastTimeMarker; - let lastMessageFrom = null; - let lastGroupedMessageTimeStamp = null; - let grouped = false; - for (let i = 0; i < room.messagesBuff.messages.length; i++) { - let v = room.messagesBuff.messages.getItem(i); - if (!v.protocol && v.revoked !== true) { - let shouldRender = true; - if (v.isManagement && v.isManagement() === true && v.isRenderableManagement() === false || v.deleted === true) { - shouldRender = false; - } - const timestamp = v.delay; - const curTimeMarker = getTimeMarker(timestamp); - if (shouldRender === true && curTimeMarker && lastTimeMarker !== curTimeMarker) { - lastTimeMarker = curTimeMarker; - messagesList.push(REaCt().createElement("div", { - className: "message date-divider selectable-txt", - key: `${v.messageId }_marker`, - title: time2date(timestamp) - }, curTimeMarker)); - grouped = false; - lastMessageFrom = null; - lastGroupedMessageTimeStamp = null; - } - if (shouldRender === true) { - let {userId} = v; - if (!userId && contact && contact.u) { - userId = contact.u; - } - if (v instanceof Message && v.dialogType !== "truncated") { - if (!lastMessageFrom || userId && lastMessageFrom === userId) { - if (timestamp - lastGroupedMessageTimeStamp < 300) { - grouped = true; - } else { - grouped = false; - lastMessageFrom = userId; - lastGroupedMessageTimeStamp = timestamp; - } - } else { - grouped = false; - lastMessageFrom = userId; - if (lastMessageFrom === userId) { - lastGroupedMessageTimeStamp = timestamp; - } else { - lastGroupedMessageTimeStamp = null; - } - } - } else { - grouped = false; - lastMessageFrom = null; - lastGroupedMessageTimeStamp = null; - } - } - if ((v.dialogType === "remoteCallEnded" || v.dialogType === "remoteCallStarted") && v && v.wrappedChatDialogMessage) { - v = v.wrappedChatDialogMessage; - } - if (v.dialogType) { - let messageInstance = null; - if (v.dialogType === 'alterParticipants') { - messageInstance = REaCt().createElement(AltPartsConvMessage, { - message: v, - key: v.messageId, - contact: Message.getContactForMessage(v), - grouped, - chatRoom: room - }); - } else if (v.dialogType === 'truncated') { - messageInstance = REaCt().createElement(TruncatedMessage, { - message: v, - key: v.messageId, - contact: Message.getContactForMessage(v), - grouped, - chatRoom: room - }); - } else if (v.dialogType === 'privilegeChange') { - messageInstance = REaCt().createElement(PrivilegeChange, { - message: v, - key: v.messageId, - contact: Message.getContactForMessage(v), - grouped, - chatRoom: room - }); - } else if (v.dialogType === 'topicChange') { - messageInstance = REaCt().createElement(TopicChange, { - message: v, - key: v.messageId, - contact: Message.getContactForMessage(v), - grouped, - chatRoom: room - }); - } else if (v.dialogType === 'openModeClosed') { - messageInstance = REaCt().createElement(CloseOpenModeMessage, { - message: v, - key: v.messageId, - contact: Message.getContactForMessage(v), - grouped, - chatRoom: room - }); - } else if (v.dialogType === 'chatHandleUpdate') { - messageInstance = REaCt().createElement(ChatHandleMessage, { - message: v, - key: v.messageId, - contact: Message.getContactForMessage(v), - grouped, - chatRoom: room - }); - } else if (v.dialogType === 'messageRetention') { - messageInstance = REaCt().createElement(RetentionChange, { - message: v, - key: v.messageId, - contact: Message.getContactForMessage(v) - }); - } else if (v.dialogType === 'scheduleMeta') { - if (v.meta.onlyTitle) { - messageInstance = REaCt().createElement(TopicChange, { - message: v, - key: v.messageId, - contact: Message.getContactForMessage(v), - grouped, - chatRoom: v.chatRoom - }); - } else { - if (v.meta.topicChange) { - messagesList.push(REaCt().createElement(TopicChange, { - message: v, - key: `${v.messageId}-topic`, - contact: Message.getContactForMessage(v), - grouped, - chatRoom: v.chatRoom - })); - } - messageInstance = REaCt().createElement(scheduleMetaChange.A, { - message: v, - key: v.messageId, - mode: v.meta.mode, - chatRoom: room, - grouped, - link: v.chatRoom.publicLink, - contact: Message.getContactForMessage(v) - }); - } - } - messagesList.push(messageInstance); - } else { - if (!v.chatRoom) { - v.chatRoom = room; - } - messagesList.push(REaCt().createElement(generic.A, { - message: v, - state: v.state, - key: v.messageId, - contact: Message.getContactForMessage(v), - grouped, - onUpdate: () => { - self.onResizeDoUpdate(); - }, - editing: self.state.editing === v.messageId || self.state.editing === v.pendingMessageId, - onEditStarted: ((v, $domElement) => { - self.editDomElement = $domElement; - self.setState({ - 'editing': v.messageId - }); - self.forceUpdate(); - }).bind(this, v), - chatRoom: room, - onEditDone: this.onMessageEditDone.bind(this, v), - onDeleteClicked: msg => { - if (this.props.onDeleteClicked) { - this.props.onDeleteClicked(msg); - } - }, - onResized: () => { - this.handleWindowResize(); - }, - onEmojiBarChange: () => { - this.handleWindowResize(); - } - })); - } - } - } - return REaCt().createElement("div", { - ref: this.domRef, - className: ` - messages - scroll-area - ${this.props.className || ''} - ` - }, REaCt().createElement(perfectScrollbar.O, { - className: "js-messages-scroll-area perfectScrollbarContainer", - ref: ref => { - let _this$props$onMessage, _this$props; - this.messagesListScrollable = ref; - $(document).rebind(`keydown.keyboardScroll_${room.roomId}`, this.onKeyboardScroll); - (_this$props$onMessage = (_this$props = this.props).onMessagesListScrollableMount) == null || _this$props$onMessage.call(_this$props, ref); - }, - chatRoom: room, - messagesBuff: room.messagesBuff, - editDomElement: this.state.editDomElement, - editingMessageId: this.state.editing, - confirmDeleteDialog: this.state.confirmDeleteDialog, - renderedMessagesCount: messagesList.length, - options: { - suppressScrollX: true - }, - isLoading: room.messagesBuff.messagesHistoryIsLoading() || room.activeSearches > 0 || this.loadingShown, - onFirstInit: ps => { - ps.scrollToBottom(true); - room.scrolledToBottom = 1; - }, - onUserScroll: this.onMessagesScrollUserScroll - }, REaCt().createElement("div", { - className: "messages main-pad" - }, REaCt().createElement("div", { - className: "messages content-area" - }, this.renderLoadingSpinner(), messagesList))), this.renderNavigationToast()); - } -}, (0,applyDecoratedDescriptor.A)(_class.prototype, "enableScrollbar", [_dec], Object.getOwnPropertyDescriptor(_class.prototype, "enableScrollbar"), _class.prototype), _class); - - -}, - -815 -(_, EXP_, REQ_) { - -"use strict"; -REQ_.d(EXP_, { -Q: () => InviteParticipantsPanel -}); -const react0__ = REQ_(594); -const react0 = REQ_.n(react0__); -const _meetings_call_jsx1__ = REQ_(3); -const _ui_miniui_jsx2__ = REQ_(818); -const _ui_buttons_jsx3__ = REQ_(994); -const _ui_dropdowns_jsx4__ = REQ_(911); - - - - - -const NAMESPACE = 'invite-panel'; -class InviteParticipantsPanel extends react0().Component { - constructor(props) { - super(props); - this.domRef = react0().createRef(); - this.state = { - link: '', - copied: false - }; - this.retrieveChatLink(); - } - retrieveChatLink(cim) { - const { - chatRoom - } = this.props; - if (!chatRoom.topic) { - return; - } - this.loading = chatRoom.updatePublicHandle(false, cim).always(() => { - delete this.loading; - if (this.domRef.current) { - if (chatRoom.publicLink) { - this.setState({ - link: `${getBaseUrl()}/${chatRoom.publicLink}` - }); - } else { - this.setState({ - link: false - }); - } - } - }); - } - getInviteBody(encode) { - const { - chatRoom - } = this.props; - const { - link - } = this.state; - const { - scheduledMeeting - } = chatRoom; - let body = l.invite_body_text; - if (scheduledMeeting) { - const { - nextOccurrenceStart - } = chatRoom.scheduledMeeting; - body = l.invite_body_text_scheduled.replace('%4', time2date(nextOccurrenceStart / 1000, 20)).replace('%5', toLocaleTime(nextOccurrenceStart)); - } - body = body.replace(/\[BR]/g, '\n').replace('%1', u_attr.name).replace('%2', chatRoom.getRoomTitle()).replace('%3', link); - if (encode) { - return typeof body.toWellFormatted === 'function' ? body.toWellFormatted() : body.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, '').replace(/[\uD800-\uDBFF]/g, '\uFFFD').replace(/[\uDC00-\uDFFF]/g, '\uFFFD'); - } - return body; - } - render() { - const { - chatRoom, - disableLinkToggle, - onAddParticipants - } = this.props; - const { - link, - copied - } = this.state; - const inCall = _meetings_call_jsx1__.Ay.isExpanded(); - if (this.loading) { - return react0().createElement("div", { - ref: this.domRef, - className: ` - ${NAMESPACE} - ${inCall ? 'theme-dark-forced' : ''} - ` - }, react0().createElement("header", null), react0().createElement("section", { - className: "content" - }, react0().createElement("div", { - className: "content-block" - }))); - } - const canInvite = !!(chatRoom.iAmOperator() || chatRoom.options[MCO_FLAGS.OPEN_INVITE]) && onAddParticipants; - const canToggleLink = !disableLinkToggle && chatRoom.iAmOperator() && (chatRoom.isMeeting || chatRoom.topic); - const mailto = `mailto:?to=&subject=${l.invite_subject_text}&body=${this.getInviteBody(true)}`; - const copyText = chatRoom.isMeeting ? l.copy_meeting_link : l[1394]; - return react0().createElement("div", { - ref: this.domRef, - className: ` - ${NAMESPACE} - ${inCall ? 'theme-dark-forced' : ''} - ` - }, react0().createElement("header", null, react0().createElement("h3", null, l.invite_participants)), react0().createElement("section", { - className: "content" - }, canToggleLink && chatRoom.type !== 'group' && react0().createElement("div", { - className: "content-block link-block" - }, react0().createElement("div", { - className: "text-wrapper" - }, react0().createElement("span", { - className: "link-label" - }, l.invite_toggle_link_label), react0().createElement("div", { - className: `link-description ${inCall ? '' : 'hidden'}` - }, l.invite_toggle_link_desc)), react0().createElement(_ui_miniui_jsx2__.A.ToggleCheckbox, { - className: "meeting-link-toggle", - checked: !!link, - value: !!link, - onToggle: () => { - if (this.loading) { - return; - } - if (link) { - this.loading = chatRoom.updatePublicHandle(true).always(() => { - delete this.loading; - if (this.domRef.current) { - this.setState({ - link: false - }); - } - }); - } else { - this.retrieveChatLink(true); - } - } - })), link && react0().createElement("div", { - className: "content-block" - }, react0().createElement(_ui_buttons_jsx3__.$, { - className: "flat-button", - icon: `sprite-fm-mono icon-${copied ? 'check' : 'link-thin-outline'}`, - label: copied ? l.copied : copyText, - onClick: () => { - if (copied) { - return; - } - delay('chat-event-inv-copylink', () => eventlog(99964)); - copyToClipboard(link); - this.setState({ - copied: true - }, () => { - tSleep(3).then(() => { - if (this.domRef.current) { - this.setState({ - copied: false - }); - } - }); - }); - } - })), link && react0().createElement("div", { - className: "content-block" - }, react0().createElement(_ui_buttons_jsx3__.$, { - className: "flat-button", - label: l.share_chat_link, - icon: "sprite-fm-mono icon-share-02-thin-outline" - }, react0().createElement(_ui_dropdowns_jsx4__.Dropdown, { - className: ` - button-group-menu - invite-dropdown - ${inCall ? 'theme-dark-forced' : ''} - `, - noArrow: true, - positionAt: "left bottom", - collision: "none", - horizOffset: 79, - vertOffset: 6, - ref: r => { - this.dropdownRef = r; - }, - onBeforeActiveChange: e => { - if (e) { - delay('chat-event-inv-dropdown', () => eventlog(99965)); - $(document.body).trigger('closeAllDropdownsExcept', this.dropdownRef); - } - } - }, react0().createElement(_ui_dropdowns_jsx4__.DropdownItem, { - key: "send-invite", - className: ` - ${inCall ? 'theme-dark-forced' : ''} - `, - icon: "sprite-fm-mono icon-mail-thin-outline", - label: l.share_chat_link_invite, - onClick: () => { - delay('chat-event-inv-email', () => eventlog(99966)); - window.open(mailto, '_self', 'noopener,noreferrer'); - } - }), react0().createElement(_ui_dropdowns_jsx4__.DropdownItem, { - key: "copy-invite", - className: ` - ${inCall ? 'theme-dark-forced' : ''} - `, - label: l.copy_chat_link_invite, - icon: "sprite-fm-mono icon-square-copy", - onClick: () => { - delay('chat-event-inv-copy', () => eventlog(99967)); - copyToClipboard(this.getInviteBody(), l.invite_copied); - } - })))), canInvite && (link || canToggleLink) && chatRoom.type !== 'group' && react0().createElement("div", { - className: "content-block invite-panel-divider" - }, l.invite_dlg_divider), canInvite && react0().createElement("div", { - className: "content-block add-participant-block" - }, react0().createElement(_ui_buttons_jsx3__.$, { - className: "flat-button", - icon: "sprite-fm-mono icon-user-square-thin-outline", - label: l.add_participants, - onClick: () => { - delay('chat-event-inv-add-participant', () => eventlog(99968)); - onAddParticipants(); - } - })))); - } -} - -}, - -280 -(_, EXP_, REQ_) { - -"use strict"; -REQ_.d(EXP_, { -A: () => Link -}); -const react0__ = REQ_(594); -const react0 = REQ_.n(react0__); - -class Link extends react0().Component { - constructor(props) { - super(props); - this.IS_CLICK_URL = undefined; - this.IS_CLICK_URL = this.props.to && (this.props.to.startsWith('/') || this.props.to.includes('mega.io')); - } - componentDidMount() { - if (this.IS_CLICK_URL) { - clickURLs(); - } - } - render() { - const { - className, - to, - target, - children, - onClick - } = this.props; - if (this.IS_CLICK_URL) { - return react0().createElement("a", { - className: ` - clickurl - ${className || ''} - `, - href: to, - target - }, children); - } - return react0().createElement("a", { - className, - href: "#", - onClick: ev => { - if (onClick) { - ev.preventDefault(); - return onClick(ev); - } - return null; - } - }, children); - } -} - -}, - -959 -(_, EXP_, REQ_) { - -"use strict"; -REQ_.d(EXP_, { -A: () => __WEBPACK_DEFAULT_EXPORT__ -}); -const react0__ = REQ_(594); -const react0 = REQ_.n(react0__); - -class Group extends react0().Component { - constructor(props) { - super(props); - this.containerRef = react0().createRef(); - this.state = { - expanded: false - }; - this.doToggle = this.doToggle.bind(this); - } - toggleEvents() { - return this.state.expanded ? $(document).rebind(`mousedown.${Group.NAMESPACE}`, ev => !this.containerRef.current.contains(ev.target) && this.doToggle()).rebind(`keydown.${Group.NAMESPACE}`, ({ - keyCode - }) => keyCode && keyCode === 27 && this.doToggle()) : $(document).unbind(`.${Group.NAMESPACE}`); - } - doToggle() { - this.setState(state => ({ - expanded: !state.expanded - }), () => this.toggleEvents()); - } - render() { - const { - active, - warn, - onHold, - screenSharing, - children - } = this.props; - if (children && children.length) { - return react0().createElement("div", { - ref: this.containerRef, - className: Group.BASE_CLASS - }, react0().createElement("div", { - className: ` - ${Group.BASE_CLASS}-menu - ${this.state.expanded ? 'expanded' : ''} - `, - onClick: this.doToggle - }, children.map(item => { - return item && react0().createElement("div", { - key: item.key, - className: `${Group.BASE_CLASS}-item` - }, item); - })), react0().createElement("button", { - className: "mega-button theme-light-forced round large", - onClick: this.doToggle - }, active && react0().createElement("div", { - className: "info-indicator active" - }), warn && react0().createElement("div", { - className: "info-indicator warn simpletip", - "data-simpletip": l.screen_share_crop_tip, - "data-simpletipposition": "top", - "data-simpletipoffset": "5", - "data-simpletip-class": "theme-dark-forced" - }, react0().createElement("i", { - className: "sprite-fm-mono icon-exclamation-filled" - })), react0().createElement("i", { - className: ` - sprite-fm-mono - ${screenSharing ? 'icon-end-screenshare' : ''} - ${!onHold && !screenSharing && 'icon-options'} - ` - }))); - } - return null; - } -} -Group.NAMESPACE = 'buttonGroup'; -Group.BASE_CLASS = 'button-group'; -class Button extends react0().Component { - constructor(...args) { - super(...args); - this.buttonRef = react0().createRef(); - } - componentDidUpdate() { - if (this.props.simpletip) { - $(this.buttonRef.current).trigger('simpletipUpdated'); - } - } - componentDidMount() { - if (this.props.didMount) { - this.props.didMount(this); - } - } - render() { - const { - children, - className, - style, - simpletip, - icon, - onClick - } = this.props; - return react0().createElement("button", { - ref: this.buttonRef, - className: ` - ${className ? className : ''} - ${simpletip ? 'simpletip' : ''} - `, - style, - "data-simpletip": simpletip == null ? void 0 : simpletip.label, - "data-simpletipposition": simpletip == null ? void 0 : simpletip.position, - "data-simpletipoffset": simpletip == null ? void 0 : simpletip.offset, - "data-simpletip-class": simpletip == null ? void 0 : simpletip.className, - onClick - }, icon && react0().createElement("i", { - className: `sprite-fm-mono ${icon}` - }), children); - } -} -Button.Group = Group; -const __WEBPACK_DEFAULT_EXPORT__ = Button; - -}, - -3 -(_, EXP_, REQ_) { - -"use strict"; - -// EXPORTS -REQ_.d(EXP_, { - Fj: () => EXPANDED_FLAG, - g: () => MODE, - ZE: () => TYPE, - gR: () => VIEW, - Ay: () => Call, - dQ: () => inProgressAlert, - P: () => isGuest -}); - -// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/extends.js -const esm_extends = REQ_(168); -// EXTERNAL MODULE: external "React" -const React_ = REQ_(594); -const REaCt = REQ_.n(React_); -// EXTERNAL MODULE: ./js/chat/mixins.js -const mixins = REQ_(137); -// EXTERNAL MODULE: ./js/chat/ui/meetings/stream.jsx + 7 modules -const stream = REQ_(415); -// EXTERNAL MODULE: ./js/chat/ui/composedTextArea.jsx + 1 modules -const composedTextArea = REQ_(77); -// EXTERNAL MODULE: ./js/chat/ui/historyPanel.jsx + 7 modules -const historyPanel = REQ_(814); -// EXTERNAL MODULE: ./js/chat/ui/contacts.jsx -const ui_contacts = REQ_(251); -// EXTERNAL MODULE: ./js/ui/perfectScrollbar.jsx -const perfectScrollbar = REQ_(486); -;// ./js/chat/ui/meetings/collapse.jsx - -class Collapse extends REaCt().Component { - constructor(...args) { - super(...args); - this.state = { - expanded: true - }; - } - render() { - const { - expanded - } = this.state; - const { - heading, - badge, - children - } = this.props; - return REaCt().createElement("div", { - className: "collapse" - }, heading && REaCt().createElement("div", { - className: "collapse-head", - onClick: () => this.setState(state => ({ - expanded: !state.expanded - })) - }, REaCt().createElement("i", { - className: ` - sprite-fm-mono - ${expanded ? 'icon-arrow-down' : 'icon-arrow-up'} - ` - }), REaCt().createElement("h5", null, heading), badge !== undefined && badge > 0 && REaCt().createElement("span", { - className: "participants-count" - }, badge)), expanded && children); - } -} -// EXTERNAL MODULE: ./js/ui/utils.jsx -const utils = REQ_(314); -// EXTERNAL MODULE: ./js/chat/ui/meetings/button.jsx -const meetings_button = REQ_(959); -// EXTERNAL MODULE: ./js/chat/ui/contactsPanel/contactsPanel.jsx + 20 modules -const contactsPanel = REQ_(173); -// EXTERNAL MODULE: ./js/chat/ui/meetings/videoNodeMenu.jsx -const videoNodeMenu = REQ_(539); -// EXTERNAL MODULE: ./js/chat/ui/meetings/videoNode.jsx -const videoNode = REQ_(414); -;// ./js/chat/ui/meetings/participants.jsx - - - - - - - - - - - - -class Participant extends mixins.w9 { - constructor(props) { - super(props); - this.domRef = REaCt().createRef(); - this.raisedHandListener = undefined; - this.baseIconClass = 'sprite-fm-mono'; - this.state = { - raisedHandPeers: [] - }; - this.state.raisedHandPeers = this.props.raisedHandPeers || []; - } - componentDidMount() { - super.componentDidMount(); - this.props.source.registerConsumer(this); - this.raisedHandListener = mBroadcaster.addListener('meetings:raisedHand', raisedHandPeers => this.setState({ - raisedHandPeers - }, () => this.safeForceUpdate())); - } - componentWillUnmount() { - super.componentWillUnmount(); - this.props.source.deregisterConsumer(this); - mBroadcaster.removeListener(this.raisedHandListener); - } - onAvChange() { - this.safeForceUpdate(); - } - render() { - const { - call, - mode, - chatRoom, - source, - contact, - handle, - name, - recorderCid, - onCallMinimize, - onSpeakerChange, - onModeChange - } = this.props; - const { - isOnHold, - videoMuted, - audioMuted, - clientId - } = source; - const hasRelationship = contactsPanel.A.hasRelationship(contact); - return REaCt().createElement("div", { - ref: this.domRef, - className: "participant-wrapper" - }, this.state.raisedHandPeers.includes(handle) && !isOnHold ? REaCt().createElement("div", { - className: "participant-signifier" - }, REaCt().createElement("i", { - className: "sprite-fm-uni icon-raise-hand" - })) : REaCt().createElement(ui_contacts.Avatar, { - contact: M.u[handle] - }), REaCt().createElement("div", { - className: "name" - }, handle === u_handle ? REaCt().createElement(utils.zT, null, `${name} ${l.me}`) : REaCt().createElement(ui_contacts.ContactAwareName, { - contact: M.u[handle], - emoji: true - }), Call.isModerator(chatRoom, handle) && REaCt().createElement("span", null, REaCt().createElement("i", { - className: `${this.baseIconClass} icon-admin-outline` - }))), REaCt().createElement("div", { - className: "status" - }, recorderCid === clientId || recorderCid === call.sfuClient.cid && handle === u_handle ? REaCt().createElement("div", { - className: "recording-status" - }, REaCt().createElement("span", null)) : null, REaCt().createElement("i", { - className: ` - ${this.baseIconClass} - ${videoMuted ? 'icon-video-off-thin-outline inactive' : 'icon-video-thin-outline'} - ` - }), REaCt().createElement(videoNode.Gz, { - source - }), REaCt().createElement("div", { - className: "participants-menu theme-dark-forced" - }, REaCt().createElement("div", { - className: "participants-menu-toggle" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-side-menu" - })), REaCt().createElement("div", { - className: "participants-menu-content" - }, REaCt().createElement("ul", null, hasRelationship ? REaCt().createElement("li", null, REaCt().createElement(meetings_button.A, { - icon: "sprite-fm-mono icon-info", - onClick: () => { - onCallMinimize(); - loadSubPage(`fm/chat/contacts/${handle}`); - } - }, REaCt().createElement("span", null, l[6859]))) : null, chatRoom.iAmOperator() && u_handle !== handle && !audioMuted && REaCt().createElement("li", null, REaCt().createElement(meetings_button.A, { - icon: "sprite-fm-mono icon-mic-off-thin-outline", - onClick: () => { - call.sfuClient.mutePeer(clientId); - megaChat.plugins.userHelper.getUserNickname(handle).catch(dump).always(name => { - ChatToast.quick(l.you_muted_peer.replace('%NAME', name || '')); - }); - } - }, REaCt().createElement("span", null, l[16214]))), hasRelationship ? REaCt().createElement("li", null, REaCt().createElement(meetings_button.A, { - icon: "sprite-fm-mono icon-chat", - onClick: () => { - onCallMinimize(); - loadSubPage(`fm/chat/p/${handle}`); - } - }, REaCt().createElement("span", null, l.send_message))) : null, chatRoom.iAmOperator() && u_handle !== handle && REaCt().createElement("li", null, REaCt().createElement(videoNodeMenu.EH, { - stream: source, - chatRoom - })), REaCt().createElement("li", null, REaCt().createElement(videoNodeMenu.yU, { - mode, - stream: source, - onSpeakerChange, - onModeChange - })), call.isPublic && chatRoom.iAmOperator() && u_handle !== handle && REaCt().createElement("li", null, REaCt().createElement(meetings_button.A, { - icon: "sprite-fm-mono icon-disabled-filled", - onClick: () => chatRoom.trigger('onRemoveUserRequest', handle) - }, REaCt().createElement("span", null, l[8867])))))))); - } -} -class Participants extends mixins.w9 { - get allPeersMuted() { - return Object.values(this.props.peers).filter(p => p instanceof CallManager2.Peer).every(p => p.audioMuted); - } - constructor(props) { - super(props); - this.domRef = REaCt().createRef(); - this.muteRef = REaCt().createRef(); - this.NAMESPACE = 'participants'; - this.FILTER = { - IN_CALL: 0, - CHAT_PARTICIPANTS: 1 - }; - this.state = { - filter: this.FILTER.IN_CALL, - noResponsePeers: [], - ringingPeers: [], - allPeersMuted: undefined - }; - this.doHangUp = handle => { - if (handle) { - const { - call, - chatRoom - } = this.props; - return this.isMounted() && this.setState(state => ({ - ringingPeers: state.ringingPeers.filter(p => p !== handle) - }), () => chatRoom.ringUser(handle, call.callId, 0)); - } - }; - this.doCall = handle => { - if (handle) { - const { - call, - chatRoom - } = this.props; - this.setState(state => ({ - ringingPeers: [...state.ringingPeers, handle] - }), () => { - chatRoom.ringUser(handle, call.callId, 1); - if (chatRoom.options.w) { - let _call$sfuClient; - call == null || (_call$sfuClient = call.sfuClient) == null || _call$sfuClient.wrAllowJoin([handle]); - } - tSleep(40).then(() => { - this.doHangUp(handle); - return Object.keys(chatRoom.uniqueCallParts).includes(handle) ? null : this.setState(state => ({ - noResponsePeers: [...state.noResponsePeers, handle] - })); - }); - }); - } - }; - this.getCallState = handle => { - const { - noResponsePeers, - ringingPeers - } = this.state; - if (this.props.initialCallRinging || ringingPeers.includes(handle)) { - return l.call_state_calling; - } - if (noResponsePeers.includes(handle)) { - return l.call_state_no_response; - } - return l.call_state_not_in_call; - }; - this.getCallParticipants = () => { - const { - call, - mode, - chatRoom, - recorderCid, - raisedHandPeers, - onCallMinimize, - onSpeakerChange, - onModeChange - } = this.props; - const peers = Object.values(this.props.peers); - const $$PEER = peer => peer && REaCt().createElement("li", { - key: `${peer.clientId || ''}-${peer.userHandle}` - }, REaCt().createElement(Participant, { - call, - mode, - chatRoom, - source: peer.userHandle ? peer : call.getLocalStream(), - contact: M.u[peer.userHandle] || undefined, - handle: peer.userHandle || u_handle, - name: peer.name || M.getNameByHandle(u_handle), - recorderCid, - raisedHandPeers, - onCallMinimize, - onSpeakerChange, - onModeChange - })); - let $$RAISED = []; - for (const userHandle of call.sfuClient.raisedHands) { - const peer = peers.find(p => (p.userHandle || p.localPeerStream.userHandle) === userHandle); - $$RAISED = [...$$RAISED, $$PEER(peer)]; - } - const $$REST = peers.filter(p => ![...call.sfuClient.raisedHands].includes(p.userHandle || p.localPeerStream.userHandle)).sort((a, b) => !!a.userHandle - !!b.userHandle).map(peer => $$PEER(peer)); - return REaCt().createElement("ul", null, $$RAISED, $$REST); - }; - this.getChatParticipants = () => { - const { - chatRoom, - initialCallRinging - } = this.props; - const { - ringingPeers - } = this.state; - const callParticipants = Object.keys(chatRoom.uniqueCallParts); - const chatParticipants = chatRoom.getParticipantsExceptMe().filter(h => !callParticipants.includes(h)); - if (chatParticipants != null && chatParticipants.length) { - return REaCt().createElement(REaCt().Fragment, null, chatParticipants.length > 1 ? (() => { - const isRingingAll = initialCallRinging || JSON.stringify(ringingPeers) === JSON.stringify(chatParticipants); - return REaCt().createElement(meetings_button.A, { - className: ` - mega-button - action - neutral - call-control-all - ${isRingingAll ? 'disabled' : ''} - `, - icon: "sprite-fm-mono phone-call-01", - onClick: () => isRingingAll ? null : chatParticipants.map(handle => this.doCall(handle)) - }, l.call_all_button); - })() : null, REaCt().createElement("ul", null, chatParticipants.map(handle => { - const contact = M.u[handle]; - const isRinging = initialCallRinging || ringingPeers.includes(handle); - return REaCt().createElement("li", { - key: handle - }, REaCt().createElement(ui_contacts.Avatar, { - contact - }), REaCt().createElement("div", { - className: "name" - }, REaCt().createElement(ui_contacts.ContactAwareName, { - contact: M.u[handle], - emoji: true - }), REaCt().createElement("span", { - className: ` - user-card-presence - ${megaChat.userPresenceToCssClass(contact.presence)} - ` - }), Call.isModerator(chatRoom, handle) && REaCt().createElement("span", null, REaCt().createElement("i", { - className: "sprite-fm-mono icon-admin-outline" - })), REaCt().createElement("div", { - className: "call-state" - }, this.getCallState(handle))), isRinging ? null : REaCt().createElement("div", { - className: "call-control" - }, REaCt().createElement(meetings_button.A, { - className: "mega-button action neutral", - onClick: () => this.doCall(handle) - }, l.call_button))); - }))); - } - return REaCt().createElement("div", { - className: "participants-empty" - }, REaCt().createElement("span", { - className: "empty-check-icon" - }), REaCt().createElement("h3", null, l.all_participants_in_call)); - }; - this.renderParticipantsList = () => { - const { - filter, - raisedHandPeers - } = this.state; - return REaCt().createElement("div", { - className: ` - participants-list - ${filter === this.FILTER.IN_CALL ? '' : 'with-chat-participants'} - ${this.props.guest ? 'guest' : ''} - ` - }, REaCt().createElement(perfectScrollbar.O, { - filter, - raisedHandPeers, - options: { - 'suppressScrollX': true - } - }, filter === this.FILTER.IN_CALL ? this.getCallParticipants() : this.getChatParticipants())); - }; - this.renderMuteAllControl = () => { - const { - allPeersMuted - } = this.state; - const simpletip = { - label: l.mute_all_tooltip, - position: 'top', - className: 'theme-dark-forced' - }; - return REaCt().createElement(meetings_button.A, { - ref: this.muteRef, - simpletip: allPeersMuted ? null : simpletip, - className: ` - mega-button - action - ${this.NAMESPACE}-mute - ${allPeersMuted ? 'disabled' : ''} - `, - icon: "sprite-fm-mono icon-mic-off-thin-outline", - onClick: () => { - let _this$muteRef, _muteRef$buttonRef; - const muteRef = (_this$muteRef = this.muteRef) == null ? void 0 : _this$muteRef.current; - const buttonRef = (_muteRef$buttonRef = muteRef.buttonRef) == null ? void 0 : _muteRef$buttonRef.current; - return allPeersMuted ? null : this.setState({ - allPeersMuted: true - }, () => { - this.props.call.sfuClient.mutePeer(); - ChatToast.quick(l.you_muted_all_peers); - if (buttonRef) { - $(buttonRef).trigger('simpletipClose'); - } - }); - } - }, allPeersMuted ? l.all_muted : l.mute_all); - }; - this.state.allPeersMuted = this.allPeersMuted; - } - componentWillUnmount() { - super.componentWillUnmount(); - ['onCallPeerJoined', 'onPeerAvChange'].map(event => this.props.chatRoom.off(`${event}.${this.NAMESPACE}`)); - } - componentDidMount() { - super.componentDidMount(); - this.props.chatRoom.rebind(`onCallPeerJoined.${this.NAMESPACE}`, (ev, userHandle) => { - const { - noResponsePeers, - ringingPeers - } = this.state; - this.setState({ - noResponsePeers: noResponsePeers.includes(userHandle) ? noResponsePeers.filter(h => h !== userHandle) : noResponsePeers, - ringingPeers: ringingPeers.includes(userHandle) ? ringingPeers.filter(h => h !== userHandle) : ringingPeers - }); - }).rebind(`onPeerAvChange.${this.NAMESPACE}`, () => this.isMounted() && this.setState({ - allPeersMuted: this.allPeersMuted - })); - } - render() { - const { - IN_CALL, - CHAT_PARTICIPANTS - } = this.FILTER; - const { - withInvite, - chatRoom, - peers, - onInviteToggle - } = this.props; - const { - filter - } = this.state; - return REaCt().createElement("div", { - ref: this.domRef, - className: this.NAMESPACE - }, chatRoom.type === 'private' ? null : REaCt().createElement("div", { - className: `${this.NAMESPACE}-nav` - }, REaCt().createElement(meetings_button.A, { - className: filter === IN_CALL ? 'active' : '', - onClick: () => this.setState({ - filter: IN_CALL - }) - }, l.call_heading_in_call), REaCt().createElement(meetings_button.A, { - className: filter === CHAT_PARTICIPANTS ? 'active' : '', - onClick: () => this.setState({ - filter: CHAT_PARTICIPANTS - }) - }, l.call_heading_not_in_call)), filter === IN_CALL ? REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("div", { - className: `${this.NAMESPACE}-actions` - }, withInvite && REaCt().createElement(meetings_button.A, { - className: ` - mega-button - action - ${this.NAMESPACE}-invite - `, - icon: "sprite-fm-mono icon-user-plus-thin-outline", - onClick: onInviteToggle - }, l[8726]), chatRoom.iAmOperator() && this.renderMuteAllControl()), REaCt().createElement(Collapse, (0,esm_extends.A)({}, this.props, { - filter, - heading: l[16217], - badge: (peers == null ? void 0 : peers.length) + 1 - }), this.renderParticipantsList())) : this.renderParticipantsList()); - } -} -;// ./js/chat/ui/meetings/guest.jsx - - -class Guest extends REaCt().Component { - constructor(...args) { - super(...args); - this.state = { - copy: '' - }; - } - componentDidMount() { - this.setState({ - copy: `${l.free_storage_info__call.replace('%s', bytesToSize(mega.bstrg, 0))}` - }); - } - render() { - const { - copy - } = this.state; - return REaCt().createElement("div", { - className: "guest-register" - }, REaCt().createElement("div", { - className: "guest-register-content" - }, REaCt().createElement(meetings_button.A, { - className: "close-guest-register", - icon: "icon-close-component", - onClick: this.props.onGuestClose - }, REaCt().createElement("span", null, l[148])), REaCt().createElement("div", null, REaCt().createElement("i", { - className: "sprite-fm-illustration-wide registration" - }), REaCt().createElement("span", null, copy)), REaCt().createElement(meetings_button.A, { - className: "mega-button positive register-button", - onClick: () => loadSubPage('register') - }, l.sign_up_btn))); - } -} -;// ./js/chat/ui/meetings/sidebar.jsx - - - - - - - - -const inviteAllowed = chatRoom => { - if (chatRoom) { - return chatRoom.type !== 'private' && !!(chatRoom.options[MCO_FLAGS.OPEN_INVITE] || Call.isModerator(chatRoom, u_handle) || chatRoom.publicLink); - } - return false; -}; -class Sidebar extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - this.historyPanel = null; - this.renderHead = ({ - title, - children - }) => { - return REaCt().createElement("div", { - className: "sidebar-head" - }, REaCt().createElement(meetings_button.A, { - simpletip: { - label: l.close_sidebar, - className: 'theme-dark-forced' - }, - className: "mega-button action small left", - icon: "icon-collapse-right", - onClick: this.props.onSidebarClose - }, REaCt().createElement("span", null, l.close_sidebar)), REaCt().createElement("h2", null, title), children || null); - }; - this.renderParticipantsView = () => { - const { - call, - mode, - peers, - initialCallRinging, - chatRoom, - guest, - recorderCid, - raisedHandPeers, - onInviteToggle, - onCallMinimize, - onSpeakerChange, - onModeChange - } = this.props; - const withInvite = inviteAllowed(chatRoom); - return REaCt().createElement(REaCt().Fragment, null, this.renderHead({ - title: l[16217] - }), REaCt().createElement(Participants, { - withInvite, - call, - mode, - peers, - initialCallRinging, - chatRoom, - guest, - recorderCid, - raisedHandPeers, - onInviteToggle, - onCallMinimize, - onSpeakerChange, - onModeChange - })); - }; - this.renderChatView = () => { - const { - chatRoom, - typingAreaText, - onDeleteMessage, - onTypingAreaChanged - } = this.props; - return REaCt().createElement(REaCt().Fragment, null, this.renderHead({ - title: l.chats - }), REaCt().createElement(historyPanel.A, { - ref: ref => { - this.historyPanel = ref; - }, - chatRoom, - className: "in-call", - onDeleteClicked: onDeleteMessage - }), REaCt().createElement(composedTextArea.A, { - chatRoom, - parent: this, - containerRef: this.domRef, - typingAreaText, - onTypingAreaChanged - })); - }; - } - render() { - const { - view, - guest, - onGuestClose - } = this.props; - return REaCt().createElement("div", { - className: "sidebar-wrapper theme-dark-forced" - }, REaCt().createElement("div", { - ref: this.domRef, - className: ` - sidebar - ${view === VIEW.CHAT ? 'chat-opened' : 'theme-dark-forced'} - ` - }, view === VIEW.PARTICIPANTS && this.renderParticipantsView(), view === VIEW.CHAT && this.renderChatView(), guest && view !== VIEW.CHAT && REaCt().createElement(Guest, { - onGuestClose - }))); - } -} -// EXTERNAL MODULE: ./js/ui/modalDialogs.jsx + 1 modules -const modalDialogs = REQ_(318); -;// ./js/chat/ui/meetings/workflow/invite/search.jsx -let _Search; - - -class Search extends REaCt().Component { - render() { - const { - value, - placeholder, - onChange - } = this.props; - return REaCt().createElement("div", { - className: `${Invite.NAMESPACE}-field` - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-preview-reveal" - }), REaCt().createElement("input", { - type: "text", - autoFocus: true, - placeholder: l[23750].replace('[X]', placeholder), - ref: Search.inputRef, - value, - onChange - })); - } -} -_Search = Search; -Search.inputRef = REaCt().createRef(); -Search.focus = () => { - return _Search.inputRef && _Search.inputRef.current && _Search.inputRef.current.focus(); -}; -;// ./js/chat/ui/meetings/workflow/invite/footer.jsx - - -const Footer = ({ - selected, - onClose, - onAdd -}) => { - return REaCt().createElement("footer", null, REaCt().createElement("div", { - className: "footer-container" - }, REaCt().createElement(meetings_button.A, { - className: "mega-button", - onClick: onClose - }, l.msg_dlg_cancel), REaCt().createElement(meetings_button.A, { - className: ` - mega-button - positive - ${selected.length > 0 ? '' : 'disabled'} - `, - onClick: onAdd - }, l.add))); -}; -const footer = Footer; -;// ./js/chat/ui/meetings/workflow/invite/nil.jsx - - -const Nil = () => { - return REaCt().createElement("div", { - className: `${Invite.NAMESPACE}-nil` - }, REaCt().createElement("div", { - className: "fm-empty-contacts-bg" - }), REaCt().createElement("h2", null, HAS_CONTACTS() ? l[8674] : l[784])); -}; -const nil = Nil; -// EXTERNAL MODULE: ./js/chat/ui/link.jsx -const ui_link = REQ_(280); -;// ./js/chat/ui/meetings/workflow/invite/invite.jsx - - - - - - - - - - - -const HAS_CONTACTS = () => { - const keys = M.u.keys(); - for (let i = 0; i < keys.length; i++) { - if (M.u[keys[i]].c === 1) { - return true; - } - } -}; -class Invite extends REaCt().Component { - constructor(props) { - super(props); - this.domRef = REaCt().createRef(); - this.wrapperRef = REaCt().createRef(); - this.state = { - loading: true, - value: '', - searching: false, - contacts: [], - contactsInitial: [], - frequents: [], - frequentsInitial: [], - selected: [], - excluded: [], - input: false - }; - this.getSortedContactsList = (frequents, excluded) => { - frequents = frequents || this.state.frequents; - excluded = excluded || this.state.excluded; - const filteredContacts = []; - (this.props.contacts || M.u).forEach(contact => { - if (contact.c === 1 && !frequents.includes(contact.u) && !excluded.includes(contact.u)) { - filteredContacts.push(contact); - } - }); - const sortFn = M.getSortByNameFn2(1); - filteredContacts.sort((a, b) => sortFn(a, b)); - return filteredContacts; - }; - this.doMatch = (value, collection) => { - value = value.toLowerCase(); - return collection.filter(contact => { - contact = typeof contact === 'string' ? M.getUserByHandle(contact) : contact; - const name = M.getNameByHandle(contact.u || contact).toLowerCase(); - const email = contact.m && contact.m.toLowerCase(); - return name.includes(value) || email.includes(value); - }); - }; - this.handleSearch = ev => { - const { - value - } = ev.target; - const searching = value.length >= 2; - const frequents = searching ? this.doMatch(value, this.state.frequentsInitial) : this.state.frequentsInitial; - const contacts = searching ? this.doMatch(value, this.state.contactsInitial) : this.state.contactsInitial; - this.setState({ - value, - searching, - frequents, - contacts - }, () => { - const wrapperRef = this.wrapperRef && this.wrapperRef.current; - if (wrapperRef && searching) { - wrapperRef.reinitialise(); - wrapperRef.scrollToY(0); - } - }); - }; - this.handleSelect = userHandle => { - this.setState(state => ({ - selected: state.selected.includes(userHandle) ? state.selected.filter(c => c !== userHandle) : [...state.selected, userHandle] - }), () => Search.focus()); - }; - this.handleAdd = () => { - const { - selected - } = this.state; - const { - call, - chatRoom, - onClose - } = this.props; - if (selected.length > 0) { - if (chatRoom.options.w) { - let _call$sfuClient; - call == null || (_call$sfuClient = call.sfuClient) == null || _call$sfuClient.wrAllowJoin(selected); - } - chatRoom == null || chatRoom.trigger('onAddUserRequest', [selected]); - onClose == null || onClose(); - } - }; - this.getFrequentContacts = () => megaChat.getFrequentContacts().then(response => { - if (!this.domRef.current) { - return; - } - const frequents = []; - const maxFreq = Math.max(response.length - ui_contacts.MAX_FREQUENTS, 0); - for (let i = response.length - 1; i >= maxFreq; i--) { - const contact = response[i]; - if (!this.state.excluded.includes(contact.userId)) { - frequents.push(contact.userId); - } - } - this.setState({ - frequents, - frequentsInitial: frequents, - contacts: this.getSortedContactsList(frequents), - loading: false - }); - }); - this.getFilteredFrequents = () => { - const { - frequents, - selected - } = this.state; - if (frequents.length === 0) { - return false; - } - return frequents.map(userHandle => { - return REaCt().createElement(ui_contacts.ContactCard, { - key: userHandle, - contact: M.u[userHandle], - chatRoom: false, - className: ` - contacts-search - short - ${selected.includes(userHandle) ? 'selected' : ''} - `, - noContextButton: true, - noContextMenu: true, - selectable: true, - onClick: () => this.handleSelect(userHandle) - }); - }); - }; - this.getFilteredContacts = () => { - const { - contacts, - frequents, - excluded, - selected - } = this.state; - const $$CONTACTS = []; - for (let i = 0; i < contacts.length; i++) { - const contact = contacts[i]; - const { - u: userHandle - } = contact; - if (!frequents.includes(userHandle) && !excluded.includes(userHandle)) { - $$CONTACTS.push(REaCt().createElement(ui_contacts.ContactCard, { - key: userHandle, - contact, - chatRoom: false, - className: ` - contacts-search - short - ${selected.includes(userHandle) ? 'selected' : ''} - `, - noContextButton: true, - noContextMenu: true, - selectable: true, - onClick: () => this.handleSelect(userHandle) - })); - } - } - return $$CONTACTS.length === 0 ? false : $$CONTACTS; - }; - this.renderContent = () => { - const frequentContacts = this.getFilteredFrequents(); - const contactsFiltered = this.getFilteredContacts(); - if (HAS_CONTACTS()) { - const { - contacts, - frequents - } = this.state; - const $$RESULT_TABLE = (header, children) => REaCt().createElement("div", { - className: "contacts-search-subsection" - }, REaCt().createElement("div", { - className: "contacts-list-header" - }, header), REaCt().createElement("div", { - className: "contacts-search-list" - }, children)); - if (frequents.length === 0 && contacts.length === 0) { - return REaCt().createElement(nil, null); - } - return REaCt().createElement(perfectScrollbar.O, { - ref: this.wrapperRef, - options: { - 'suppressScrollX': true - } - }, frequentContacts ? $$RESULT_TABLE(l[20141], frequentContacts) : '', contactsFiltered ? $$RESULT_TABLE(l[165], contactsFiltered) : ''); - } - return REaCt().createElement(nil, null); - }; - this.renderLoading = () => { - return REaCt().createElement("div", { - className: `${Invite.NAMESPACE}-loading` - }, REaCt().createElement("h2", null, l[1456])); - }; - this.state.excluded = this.props.chatRoom ? this.props.chatRoom.getParticipantsExceptMe() : []; - this.state.contacts = this.state.contactsInitial = this.getSortedContactsList(); - } - componentDidMount() { - this.getFrequentContacts(); - } - render() { - const { - NAMESPACE - } = Invite; - const { - value, - loading, - selected, - contactsInitial - } = this.state; - const { - chatRoom, - call, - onClose - } = this.props; - const { - isMeeting, - publicLink - } = chatRoom || {}; - const callPartsLength = chatRoom.getCallParticipants().length; - return REaCt().createElement(modalDialogs.A.ModalDialog, (0,esm_extends.A)({}, this.state, { - ref: this.domRef, - name: NAMESPACE, - className: ` - ${NAMESPACE} - dialog-template-tool - `, - callPartsLength, - hideOverlay: true, - onClose - }), REaCt().createElement("div", { - className: `${NAMESPACE}-head` - }, REaCt().createElement("h2", null, isMeeting ? l.invite_participants : l[8726]), isMeeting && publicLink && REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("p", null, l.copy_and_share), REaCt().createElement("div", { - className: "link-input-container" - }, REaCt().createElement(meetings_button.A, { - className: `mega-button large positive ${publicLink ? '' : 'disabled'}`, - onClick: () => publicLink && copyToClipboard(`${getBaseUrl()}/${publicLink}`, l[371]) - }, !publicLink ? l[7006] : l[1394]))), HAS_CONTACTS() && REaCt().createElement(Search, { - value, - placeholder: contactsInitial.length, - onChange: this.handleSearch - }), call.sfuClient.callLimits && call.sfuClient.callLimits.usr && callPartsLength >= call.sfuClient.callLimits.usr && REaCt().createElement("div", { - className: `${NAMESPACE}-user-limit-banner` - }, call.organiser === u_handle ? (0,utils.lI)(l.invite_limit_banner_organiser, '[A]', ui_link.A, { - className: 'invite-limit-link', - onClick() { - window.open(`${getBaseUrl()}/pro`, '_blank', 'noopener,noreferrer'); - eventlog(500260); - } - }) : l.invite_limit_banner_host)), REaCt().createElement("div", { - className: "fm-dialog-body" - }, REaCt().createElement("div", { - className: `${NAMESPACE}-contacts` - }, loading ? this.renderLoading() : this.renderContent())), REaCt().createElement(footer, { - selected, - onAdd: this.handleAdd, - onClose - })); - } -} -Invite.NAMESPACE = 'invite-meeting'; -;// ./js/chat/ui/meetings/workflow/ephemeral.jsx - - - -const Ephemeral = ({ - ephemeralAccounts, - onClose -}) => { - const ephemeralAccount = ephemeralAccounts && ephemeralAccounts[ephemeralAccounts.length - 1]; - return REaCt().createElement(modalDialogs.A.ModalDialog, { - name: "ephemeral-dialog", - dialogType: "message", - icon: "sprite-fm-uni icon-info", - title: REaCt().createElement(ui_contacts.ContactAwareName, { - emoji: true, - contact: M.u[ephemeralAccount] - }), - noCloseOnClickOutside: true, - buttons: [{ - key: 'ok', - label: l[81], - onClick: onClose - }], - onClose - }, REaCt().createElement("p", null, l.ephemeral_info)); -}; -const workflow_ephemeral = Ephemeral; -;// ./js/chat/ui/meetings/offline.jsx - - -const Offline = ({ - onCallEnd, - onClose -}) => { - return REaCt().createElement(modalDialogs.A.ModalDialog, { - name: "reconnect-dialog", - dialogType: "message", - icon: "sprite-fm-uni icon-warning", - title: l.no_internet, - noCloseOnClickOutside: true, - buttons: [{ - key: 'ok', - label: l.msg_dlg_cancel, - onClick: onClose - }, { - key: 'leave', - label: l[5883], - className: 'negative', - onClick: onCallEnd - }], - onClose - }, REaCt().createElement("p", null, l.no_connection)); -}; -const meetings_offline = Offline; -// EXTERNAL MODULE: ./js/chat/ui/conversationpanel.jsx + 15 modules -const conversationpanel = REQ_(438); -// EXTERNAL MODULE: ./js/chat/ui/meetings/streamControls.jsx -const streamControls = REQ_(489); -;// ./js/chat/ui/meetings/sidebarControls.jsx - - - -const SidebarControls = ({ - npeers, - view, - sidebar, - call, - chatRoom, - onChatToggle, - onParticipantsToggle, - onInviteToggle -}) => { - const notifications = chatRoom.getUnreadCount(); - const isOnHold = !!((call == null ? void 0 : call.av) & Av.onHold); - const canInvite = chatRoom.type !== 'private' && !!(chatRoom.iAmOperator() || chatRoom.options[MCO_FLAGS.OPEN_INVITE] || chatRoom.publicLink); - return REaCt().createElement("div", { - className: "sidebar-controls" - }, REaCt().createElement("ul", { - className: isOnHold ? 'disabled' : '' - }, canInvite && REaCt().createElement("li", { - onClick: isOnHold ? null : onInviteToggle - }, REaCt().createElement(meetings_button.A, { - className: ` - mega-button - theme-dark-forced - call-action - round - `, - icon: "icon-user-plus-thin-outline" - }), REaCt().createElement("span", { - className: "control-label" - }, l[8726])), REaCt().createElement("li", { - onClick: isOnHold ? null : onChatToggle - }, REaCt().createElement(meetings_button.A, { - className: ` - mega-button - theme-dark-forced - call-action - round - ${sidebar && view === VIEW.CHAT ? 'selected' : ''} - ${isOnHold ? 'disabled' : ''} - `, - icon: sidebar && view === VIEW.CHAT ? 'icon-chat-filled' : 'icon-message-chat-circle-thin' - }), REaCt().createElement("span", { - className: "control-label" - }, l.chat_call_button), notifications > 0 && REaCt().createElement("span", { - className: "notification-badge notifications-count" - }, notifications > 9 ? '9+' : notifications)), REaCt().createElement("li", { - onClick: isOnHold ? null : onParticipantsToggle - }, REaCt().createElement(meetings_button.A, { - className: ` - mega-button - theme-dark-forced - call-action - round - ${sidebar && view === VIEW.PARTICIPANTS ? 'selected' : ''} - ${isOnHold ? 'disabled' : ''} - `, - icon: sidebar && view === VIEW.PARTICIPANTS ? 'icon-users-thin-solid' : 'icon-users-thin-outline' - }), REaCt().createElement("span", { - className: "control-label" - }, l.participants_call_button), REaCt().createElement("span", { - className: ` - notification-badge - participants-count - theme-dark-forced - ${npeers + 1 > 99 ? 'large' : ''} - ` - }, npeers + 1)))); -}; -const sidebarControls = SidebarControls; -// EXTERNAL MODULE: ./js/chat/ui/inviteParticipantsPanel.jsx -const inviteParticipantsPanel = REQ_(815); -// EXTERNAL MODULE: ./js/ui/dropdowns.jsx -const dropdowns = REQ_(911); -;// ./js/chat/ui/meetings/call.jsx - - - - - - - - - - - - - - - - - -const NAMESPACE = 'meetings-call'; -const EXPANDED_FLAG = 'in-call'; -const MOUSE_OUT_DELAY = 2500; -const MODE = { - THUMBNAIL: 1, - MAIN: 2, - MINI: 3 -}; -const VIEW = { - DEFAULT: 0, - CHAT: 1, - PARTICIPANTS: 2 -}; -const TYPE = { - AUDIO: 1, - VIDEO: 2 -}; -const isGuest = () => !u_type; -const inProgressAlert = (isJoin, chatRoom) => { - return new Promise((resolve, reject) => { - if (megaChat.haveAnyActiveCall()) { - if (window.sfuClient) { - const { - chatRoom: activeCallRoom - } = megaChat.activeCall; - const peers = activeCallRoom ? activeCallRoom.getParticipantsExceptMe(activeCallRoom.getCallParticipants()).map(h => M.getNameByHandle(h)) : []; - let body = isJoin ? l.cancel_to_join : l.cancel_to_start; - if (peers.length) { - body = mega.utils.trans.listToString(peers, isJoin ? l.cancel_with_to_join : l.cancel_with_to_start); - } - msgDialog('warningb', null, l.call_in_progress, body, null, 1); - return reject(); - } - if (chatRoom.getCallParticipants().includes(u_handle)) { - return resolve(); - } - return msgDialog(`warningb:!^${l[2005]}!${isJoin ? l.join_call_anyway : l.start_call_anyway}`, null, isJoin ? l.join_multiple_calls_title : l.start_multiple_calls_title, isJoin ? l.join_multiple_calls_text : l.start_multiple_calls_text, join => { - if (join) { - return resolve(); - } - return reject(); - }, 1); - } - resolve(); - }); -}; -window.inProgressAlert = inProgressAlert; -class RecordingConsentDialog extends REaCt().Component { - componentWillUnmount() { - if ($.dialog && $.dialog === RecordingConsentDialog.dialogName) { - closeDialog(); - } - } - render() { - const { - peers, - recorderCid, - onCallEnd, - onClose - } = this.props; - const recordingPeer = peers[recorderCid]; - const recorderName = nicknames.getNickname(recordingPeer).substr(0, ChatToastIntegration.MAX_NAME_CHARS); - return REaCt().createElement(modalDialogs.A.ModalDialog, { - dialogName: RecordingConsentDialog.dialogName, - className: ` - mega-dialog - dialog-template-message - info - `, - stopKeyPropagation: true, - noCloseOnClickOutside: true - }, REaCt().createElement("header", null, REaCt().createElement("div", { - className: "graphic" - }, REaCt().createElement("i", { - className: "info sprite-fm-uni icon-info" - })), REaCt().createElement("div", { - className: "info-container" - }, REaCt().createElement("h3", { - id: "msgDialog-title" - }, l.call_recorded_heading), REaCt().createElement("p", { - className: "text" - }, REaCt().createElement(utils.P9, null, l.call_recorded_body.replace('[A]', ``).replace('[/A]', ''))))), REaCt().createElement("footer", null, REaCt().createElement("div", { - className: "footer-container" - }, REaCt().createElement("div", { - className: "space-between" - }, REaCt().createElement(meetings_button.A, { - className: "mega-button", - onClick: onCallEnd - }, REaCt().createElement("span", null, l[5883])), REaCt().createElement(meetings_button.A, { - className: "mega-button positive", - onClick: () => { - onClose(); - ChatToast.quick(l.user_recording_toast.replace('%NAME', recorderName)); - } - }, REaCt().createElement("span", null, l.ok_button)))))); - } -} -RecordingConsentDialog.dialogName = `${"meetings-call"}-consent`; -class Call extends mixins.w9 { - constructor(props) { - super(props); - this.domRef = REaCt().createRef(); - this.recordingConsentDialog = `${NAMESPACE}-consent`; - this.ephemeralAddListener = undefined; - this.delayProcID = null; - this.pCallTimer = null; - this.offlineDelayed = undefined; - this.callStartTimeout = undefined; - this.flagMap = attribCache.bitMapsManager.exists('obv4') ? attribCache.bitMapsManager.get('obv4') : undefined; - this.timeoutBannerRef = REaCt().createRef(); - this.state = { - mode: undefined, - view: VIEW.PARTICIPANTS, - sidebar: false, - forcedLocal: false, - hovered: false, - invite: false, - ephemeral: false, - offline: false, - ephemeralAccounts: [], - everHadPeers: false, - guest: isGuest(), - waitingRoomPeers: [], - raisedHandPeers: [], - raisedHandToast: false, - initialCallRinging: false, - onboardingUI: false, - onboardingRecording: false, - onboardingRaise: false, - recorderCid: undefined, - recordingConsentDialog: false, - recordingConsented: false, - recordingActivePeer: undefined, - recordingTooltip: false, - invitePanel: false, - presenterThumbSelected: false, - timeoutBanner: false, - showTimeoutUpgrade: false, - activeElement: false - }; - this.handleRetryTimeout = () => { - const { - call, - chatRoom - } = this.props; - if ((call == null ? void 0 : call.sfuClient.connState) === SfuClient.ConnState.kDisconnectedRetrying) { - this.handleCallEnd(); - chatRoom.trigger('onRetryTimeout'); - megaChat.playSound(megaChat.SOUNDS.CALL_END); - } - }; - this.handleCallOnline = () => { - if (this.pCallTimer) { - this.pCallTimer.abort(); - this.pCallTimer = null; - } - this.setState({ - offline: false, - raisedHandPeers: [...this.props.call.sfuClient.raisedHands] - }); - }; - this.customIsEventuallyVisible = () => true; - this.renderRaisedHandToast = () => { - const { - raisedHandPeers - } = this.state; - window.toaster.main.hideAll(); - toaster.main.show({ - buttons: [{ - text: l[16797], - onClick: () => this.setState({ - sidebar: true, - view: VIEW.PARTICIPANTS, - raisedHandToast: false - }, () => window.toaster.main.hideAll()) - }], - onClose: () => this.setState({ - raisedHandToast: false - }, () => window.toaster.main.hideAll()), - classes: ['theme-dark-forced', 'call-toast'], - icons: ['sprite-fm-uni icon-raise-hand'], - timeout: 0, - content: (() => { - const peerName = M.getNameByHandle(raisedHandPeers[0]); - const peersCount = raisedHandPeers.length; - const withCurrentPeer = raisedHandPeers.includes(u_handle); - const CONTENT = { - 1: () => l.raise_peer_raised.replace('%s', peerName), - 2: () => { - const message = withCurrentPeer ? l.raise_self_peers_raised : l.raise_two_raised; - return mega.icu.format(message, peersCount - 1).replace('%s', peerName); - }, - rest: () => { - const message = withCurrentPeer ? l.raise_self_peers_raised : l.raise_peers_raised; - return mega.icu.format(message, withCurrentPeer ? peersCount - 1 : peersCount); - } - }; - return (CONTENT[peersCount] || CONTENT.rest)(); - })() - }); - }; - this.bindCallEvents = () => { - const { - chatRoom - } = this.props; - chatRoom.rebind(`onCallPeerLeft.${NAMESPACE}`, (ev, { - userHandle, - clientId - }) => { - const { - minimized, - peers, - call, - chatRoom - } = this.props; - if (clientId === this.state.recorderCid) { - chatRoom.trigger('onRecordingStopped', { - userHandle, - clientId - }); - } - if (minimized) { - this.setState({ - mode: peers.length === 0 ? MODE.THUMBNAIL : MODE.MINI - }, () => { - call.setViewMode(this.state.mode); - }); - } - }); - chatRoom.rebind(`onCallPeerJoined.${NAMESPACE}`, () => { - const { - minimized, - peers, - call - } = this.props; - if (minimized) { - this.setState({ - mode: peers.length === 0 ? MODE.THUMBNAIL : MODE.MINI - }, () => { - call.setViewMode(this.state.mode); - }); - } - if (call.hasOtherParticipant()) { - if (!this.state.everHadPeers) { - this.setState({ - everHadPeers: true - }); - } - clearTimeout(this.callStartTimeout); - } - }); - chatRoom.rebind(`onCallLeft.${NAMESPACE}`, () => this.props.minimized && this.props.onCallEnd()); - chatRoom.rebind(`wrOnUsersEntered.${NAMESPACE}`, (ev, users) => Object.entries(users).forEach(([handle, host]) => { - return host || this.state.waitingRoomPeers.includes(handle) ? null : this.isMounted() && this.setState({ - waitingRoomPeers: [...this.state.waitingRoomPeers, handle] - }, () => { - const { - waitingRoomPeers - } = this.state; - if (waitingRoomPeers && waitingRoomPeers.length === 1) { - megaChat.playSound(megaChat.SOUNDS.CALL_JOIN_WAITING); - } - mBroadcaster.sendMessage('meetings:peersWaiting', waitingRoomPeers); - }); - })); - const usrwr = (e, users) => { - users = typeof users === 'string' ? [users] : users; - return this.isMounted() && this.setState({ - waitingRoomPeers: this.state.waitingRoomPeers.filter(h => !users.includes(h)) - }, () => mBroadcaster.sendMessage('meetings:peersWaiting', this.state.waitingRoomPeers)); - }; - chatRoom.rebind(`wrOnUserLeft.${NAMESPACE}`, usrwr); - chatRoom.rebind(`wrOnUsersAllow.${NAMESPACE}`, usrwr); - chatRoom.rebind(`wrOnUserDump.${NAMESPACE}`, (ev, users) => Object.entries(users).forEach(([handle, host]) => { - return host || this.state.waitingRoomPeers.includes(handle) ? null : this.isMounted() && this.setState({ - waitingRoomPeers: [...this.state.waitingRoomPeers, handle] - }); - })); - chatRoom.rebind(`onRecordingStarted.${NAMESPACE}`, (ev, { - userHandle, - clientId - }) => { - if (!this.state.recorderCid) { - return this.state.recordingConsented ? this.setState({ - recorderCid: clientId - }, () => { - ChatToast.quick(l.user_recording_toast.replace('%NAME', nicknames.getNickname(userHandle).substr(0, ChatToastIntegration.MAX_NAME_CHARS))); - }) : (() => { - closeDialog(); - M.safeShowDialog(RecordingConsentDialog.dialogName, () => this.setState({ - recorderCid: clientId, - recordingConsentDialog: true - })); - })(); - } - }); - chatRoom.rebind(`onRecordingStopped.${NAMESPACE}`, (ev, { - userHandle, - clientId - }) => { - const { - recorderCid - } = this.state; - this.setState({ - recordingConsentDialog: false, - recorderCid: clientId === recorderCid ? false : recorderCid - }, () => window.sfuClient && clientId === recorderCid && ChatToast.quick(l.user_recording_nop_toast.replace('%NAME', nicknames.getNickname(userHandle).substr(0, ChatToastIntegration.MAX_NAME_CHARS)))); - }); - chatRoom.rebind(`onMutedBy.${NAMESPACE}`, (ev, { - cid - }) => { - megaChat.plugins.userHelper.getUserNickname(this.props.peers[cid]).catch(dump).always(name => { - ChatToast.quick(l.muted_by.replace('%NAME', name || '')); - }); - }); - chatRoom.rebind(`onCallEndTimeUpdated.${NAMESPACE}`, ({ - data - }) => { - this.setState({ - timeoutBanner: !!data, - showTimeoutUpgrade: this.props.call.organiser === u_handle && data - Date.now() >= 120e3 - }, () => { - if (this.state.timeoutBanner) { - this.timeoutBannerInterval = this.timeoutBannerInterval || setInterval(() => this.updateTimeoutDuration(), 1000); - } else { - clearInterval(this.timeoutBannerInterval); - delete this.timeoutBannerInterval; - } - }); - }); - chatRoom.rebind(`onRaisedHandAdd.${NAMESPACE}`, (ev, { - userHandle - }) => this.isMounted() && this.setState(state => ({ - raisedHandPeers: [...state.raisedHandPeers, userHandle] - }), () => { - const { - raisedHandPeers - } = this.state; - if (userHandle !== u_handle && !this.props.minimized) { - this.setState({ - raisedHandToast: true - }, () => this.renderRaisedHandToast()); - } - mBroadcaster.sendMessage('meetings:raisedHand', raisedHandPeers); - })); - chatRoom.rebind(`onRaisedHandDel.${NAMESPACE}`, (ev, { - userHandle - }) => this.isMounted() && this.setState(state => ({ - raisedHandPeers: state.raisedHandPeers.filter(h => h !== userHandle) - }), () => { - const { - raisedHandPeers, - raisedHandToast - } = this.state; - mBroadcaster.sendMessage('meetings:raisedHand', raisedHandPeers); - if (raisedHandPeers && raisedHandPeers.length) { - return raisedHandToast ? this.renderRaisedHandToast() : null; - } - return this.setState({ - raisedHandToast: false - }, () => window.toaster.main.hideAll()); - })); - chatRoom.rebind(`onRecordingActivePeer.${NAMESPACE}`, (ev, { - userHandle - }) => this.setState({ - recordingActivePeer: userHandle - })); - }; - this.unbindCallEvents = () => ['onCallPeerLeft', 'onCallPeerJoined', 'onCallLeft', 'wrOnUsersAllow', 'wrOnUsersEntered', 'wrOnUserLeft', 'alterUserPrivilege', 'onCallState', 'onRecordingStarted', 'onRecordingStopped', 'onRecordingActivePeer', 'onCallEndTimeUpdated', 'onRaisedHandAdd', 'onRaisedHandDel'].map(event => this.props.chatRoom.off(`${event}.${NAMESPACE}`)); - this.handleCallMinimize = () => { - const { - call, - peers, - onCallMinimize - } = this.props; - const { - mode, - sidebar, - view - } = this.state; - const { - callToutId, - stayOnEnd, - presenterStreams - } = call; - Call.STATE.PREVIOUS = mode !== MODE.MINI ? { - mode, - sidebar, - view - } : Call.STATE.PREVIOUS; - const doMinimize = () => { - onCallMinimize(); - window.toaster.main.hideAll(); - }; - mega.ui.mInfoPanel.hide(); - return peers.length > 0 || presenterStreams.has(u_handle) ? this.setState({ - mode: MODE.MINI, - sidebar: false - }, () => { - doMinimize(); - call.setViewMode(MODE.MINI); - }) : (() => { - doMinimize(); - if (typeof callToutId !== 'undefined' && !stayOnEnd) { - onIdle(() => call.showTimeoutDialog()); - } - })(); - }; - this.handleCallExpand = async () => { - mega.ui.mInfoPanel.hide(); - return new Promise(resolve => { - this.setState({ - ...Call.STATE.PREVIOUS - }, () => { - this.props.onCallExpand(); - resolve(); - }); - }); - }; - this.handleStreamToggle = action => { - const { - peers - } = this.props; - if (action === stream.hK.ADD && peers.length === stream.$A) { - return; - } - return action === stream.hK.ADD ? peers.addFakeDupStream() : peers.removeFakeDupStream(); - }; - this.handleSpeakerChange = (source, presenterThumbSelected) => { - if (source) { - this.handleModeChange(MODE.MAIN); - const sourceId = source.isLocal ? 0 : source.clientId; - if (sourceId !== this.props.call.pinnedCid) { - this.props.call.setPinnedCid(sourceId); - } else { - this.props.call.setPinnedCid(sourceId, !source.hasScreen || presenterThumbSelected === this.state.presenterThumbSelected); - } - const { - pinnedCid - } = this.props.call; - this.setState({ - forcedLocal: !!(source.isLocal && pinnedCid !== null), - presenterThumbSelected: pinnedCid === null ? false : !!presenterThumbSelected && source.hasScreen - }); - } else if (source === null) { - this.setState({ - presenterThumbSelected: !!presenterThumbSelected - }); - } - }; - this.handleModeChange = mode => { - this.props.call.setViewMode(mode); - this.setState({ - mode, - forcedLocal: false - }); - }; - this.handleChatToggle = () => { - if (this.state.sidebar && this.state.view === VIEW.CHAT) { - return this.setState({ - ...Call.STATE.DEFAULT - }); - } - return this.setState({ - sidebar: true, - view: VIEW.CHAT - }); - }; - this.handleParticipantsToggle = forceOpen => { - if (forceOpen !== true) { - forceOpen = false; - } - if (this.state.sidebar && this.state.view === VIEW.CHAT) { - return this.setState({ - sidebar: true, - view: VIEW.PARTICIPANTS - }); - } - return this.setState({ - sidebar: forceOpen ? true : !this.state.sidebar, - view: VIEW.PARTICIPANTS - }); - }; - this.handleInviteToggle = () => { - if (Object.values(M.u.toJS()).some(u => u.c === 1)) { - const participants = (0,conversationpanel.zV)(this.props.chatRoom); - if ((0,conversationpanel.e4)(participants)) { - msgDialog(`confirmationa:!^${l[8726]}!${l.msg_dlg_cancel}`, null, `${l.all_contacts_added}`, `${l.all_contacts_added_to_chat}`, res => { - if (res) { - contactAddDialog(null, false); - } - }); - } else { - this.setState({ - invite: !this.state.invite - }); - } - } else { - msgDialog(`confirmationa:!^${l[8726]}!${l.msg_dlg_cancel}`, null, `${l.no_contacts}`, `${l.no_contacts_text}`, resp => { - if (resp) { - contactAddDialog(null, false); - } - }); - } - }; - this.handleHoldToggle = async () => { - await this.props.call.toggleHold(); - mBroadcaster.sendMessage('meetings:toggleHold'); - }; - this.handleScreenSharingToggle = () => { - const { - call - } = this.props; - const userAgent = navigator.userAgent.match(/Chrom(e|ium)\/(\d+)\./); - const version = parseInt(userAgent[2], 10); - if (version === 92) { - return msgDialog('info', undefined, l[47], l.chrome_screensharing); - } - return call.toggleScreenSharing(); - }; - this.handleCallEnd = () => { - let _this$props$call; - mega.ui.mInfoPanel.hide(); - (_this$props$call = this.props.call) == null || _this$props$call.destroy(SfuClient.TermCode.kUserHangup); - }; - this.handleEphemeralAdd = handle => handle && this.setState(state => ({ - ephemeral: true, - ephemeralAccounts: [...state.ephemeralAccounts, handle] - })); - this.handleStayConfirm = () => { - const { - call - } = this.props; - call.handleStayConfirm(); - onIdle(() => this.safeForceUpdate()); - }; - this.handleRecordingToggle = () => { - const { - call, - chatRoom - } = this.props; - if (chatRoom.isMeeting) { - eventlog(500286); - } else { - eventlog(500287); - } - if (this.state.recorderCid) { - return msgDialog(`confirmation:!^${l.stop_recording_dialog_cta}!${l.stop_recording_nop_dialog_cta}`, undefined, l.stop_recording_dialog_heading, l.stop_recording_dialog_body, cb => cb && sfuClient.recordingStop(), 1); - } - msgDialog(`warningb:!^${l.start_recording_dialog_cta}!${l.msg_dlg_cancel}`, null, l.notify_participants_dialog_heading, l.notify_participants_dialog_body, cb => { - if (cb || cb === null) { - return; - } - call.sfuClient.recordingStart(this.onWeStoppedRecording).then(() => { - call.recorderCid = this.state.recorderCid; - this.setState({ - recorderCid: call.sfuClient.cid - }); - this.handleModeChange(MODE.MAIN); - call.recordActiveStream(); - ChatToast.quick(l.started_recording_toast); - }).catch(dump); - }, 1); - }; - this.onWeStoppedRecording = err => this.isMounted() && this.setState({ - recorderCid: undefined, - recordingActivePeer: undefined - }, () => err ? ChatToast.quick(`${l.stopped_recording_toast} Error: ${err.message || err}`) : ChatToast.quick(l.stopped_recording_toast)); - this.renderRecordingControl = () => { - const { - chatRoom, - call, - peers - } = this.props; - const { - recorderCid, - recordingTooltip, - recordingActivePeer - } = this.state; - const isModerator = Call.isModerator(chatRoom, u_handle); - const $$CONTAINER = ({ - className, - onClick, - children - }) => REaCt().createElement("div", { - className: ` - recording-control - ${localStorage.callDebug ? 'with-offset' : ''} - ${className || ''} - `, - onClick - }, children); - if (recorderCid) { - const isRecorder = isModerator && recorderCid === call.sfuClient.cid; - const recordingPeer = peers[recorderCid]; - return REaCt().createElement($$CONTAINER, { - recordingTooltip, - className: "recording-fixed" - }, REaCt().createElement("div", (0,esm_extends.A)({ - className: ` - recording-ongoing - simpletip - ${isRecorder ? '' : 'plain-background'} - ` - }, recorderCid !== call.sfuClient.cid && { - 'data-simpletip': l.host_recording.replace('%NAME', nicknames.getNickname(recordingPeer) || megaChat.plugins.userHelper.SIMPLETIP_USER_LOADER), - 'data-simpletipposition': 'top', - 'data-simpletipoffset': 5, - 'data-simpletip-class': 'theme-dark-forced' - }), REaCt().createElement("span", { - className: ` - recording-icon - button - ${recordingTooltip ? 'active-dropdown' : ''} - ${isRecorder ? 'clickable' : ''} - `, - onMouseEnter: () => isRecorder && this.setState({ - recordingTooltip: true - }), - onMouseOut: () => isRecorder && delay('meetings-rec-hover', () => this.setState({ - recordingTooltip: false - }), 1250) - }, "REC ", REaCt().createElement("i", null), REaCt().createElement(dropdowns.Dropdown, { - className: "recording-info theme-dark-forced", - active: recordingTooltip, - noArrow: false, - positionMy: "center top", - positionAt: "center bottom", - vertOffset: 40, - horizOffset: 30 - }, REaCt().createElement("div", null, "Currently recording: ", nicknames.getNickname(recordingActivePeer)))), isRecorder && REaCt().createElement("span", { - className: "recording-toggle", - onClick: this.handleRecordingToggle - }, l.record_stop_button))); - } - const isOnHold = !!((call == null ? void 0 : call.av) & Av.onHold); - return isModerator && REaCt().createElement($$CONTAINER, { - className: isOnHold ? 'disabled' : '', - onClick: () => { - this.setState({ - onboardingRecording: false, - hovered: false - }, () => { - this.flagMap.setSync(OBV4_FLAGS.CHAT_CALL_RECORDING, 1); - this.flagMap.safeCommit(); - }); - return isOnHold || recorderCid && recorderCid !== call.sfuClient.cid ? null : this.handleRecordingToggle(); - } - }, REaCt().createElement(meetings_button.A, { - className: ` - mega-button - theme-dark-forced - call-action - round - recording-start - ${isOnHold ? 'disabled' : ''} - ` - }, REaCt().createElement("div", null, REaCt().createElement("i", null))), REaCt().createElement("span", { - className: "record-label" - }, l.record_start_button)); - }; - this.setActiveElement = activeElement => this.setState({ - activeElement - }); - const { - SOUNDS - } = megaChat; - [SOUNDS.RECONNECT, SOUNDS.CALL_END, SOUNDS.CALL_JOIN_WAITING].map(sound => ion.sound.preload(sound)); - this.state.mode = props.call.viewMode; - this.setOnboarding(); - this.handleMouseMove = this.handleMouseMove.bind(this); - this.handleMouseOut = this.handleMouseOut.bind(this); - } - handleMouseMove() { - this.setState({ - hovered: true - }); - if (this.delayProcID) { - delay.cancel(this.delayProcID); - this.delayProcID = null; - } - } - handleMouseOut() { - if (this.state.hovered) { - this.delayProcID = delay('meetings-call-hover', () => { - if (this.isMounted()) { - this.setState({ - hovered: false - }); - } - }, MOUSE_OUT_DELAY); - } - } - handleCallOffline() { - if (!this.pCallTimer) { - (this.pCallTimer = tSleep(30)).then(() => { - this.setState({ - offline: true - }); - }); - } - } - setOnboarding() { - this.state.onboardingUI = this.state.hovered = this.flagMap && !this.flagMap.getSync(OBV4_FLAGS.CHAT_CALL_UI); - if (!this.state.onboardingUI) { - this.state.onboardingRecording = this.state.hovered = this.flagMap && !this.flagMap.getSync(OBV4_FLAGS.CHAT_CALL_RECORDING); - } - if (!this.state.onboardingUI && !this.state.onboardingRecording) { - this.state.onboardingRaise = this.state.hovered = this.flagMap && !this.flagMap.getSync(OBV4_FLAGS.CHAT_CALL_RAISE); - } - } - handleInvitePanelToggle() { - delay('chat-event-inv-call', () => eventlog(99962)); - this.setState({ - invitePanel: !this.state.invitePanel - }); - } - handleInviteOrAdd() { - const { - chatRoom - } = this.props; - if (chatRoom.type === 'group') { - return this.handleInviteToggle(); - } - loadingDialog.show('fetchchatlink'); - chatRoom.updatePublicHandle(false, false, true).catch(dump).always(() => { - loadingDialog.hide('fetchchatlink'); - if (!this.isMounted()) { - return; - } - if (!chatRoom.iAmOperator() && chatRoom.options[MCO_FLAGS.OPEN_INVITE] && !chatRoom.publicLink) { - this.handleInviteToggle(); - } else if (chatRoom.type === 'public' && !chatRoom.topic) { - this.handleInviteToggle(); - } else { - this.handleInvitePanelToggle(); - } - }); - } - renderTimeLimitBanner() { - return REaCt().createElement("div", { - className: "call-time-limit-banner theme-dark-forced" - }, REaCt().createElement("span", { - ref: this.timeoutBannerRef - }, this.timeoutString), REaCt().createElement("span", { - className: "call-limit-banner-action", - onClick: () => { - clearInterval(this.timeoutBannerInterval); - delete this.timeoutBannerInterval; - this.setState({ - timeoutBanner: false - }); - } - }, l[2005]), this.state.showTimeoutUpgrade && REaCt().createElement(ui_link.A, { - className: "call-limit-banner-action", - onClick: () => { - window.open(`${getBaseUrl()}/pro`, '_blank', 'noopener,noreferrer'); - eventlog(500262); - } - }, l.upgrade_now)); - } - get timeoutString() { - const { - call - } = this.props; - if (call.callEndTime === 0) { - return ''; - } - const remainSeconds = Math.max(0, Math.ceil((call.callEndTime - Date.now()) / 1000)); - if (call.organiser === u_handle) { - if (remainSeconds < 60) { - return mega.icu.format(l.free_call_banner_organiser_ending_sec, remainSeconds); - } - if (remainSeconds <= 120) { - return mega.icu.format(l.free_call_banner_organiser_ending, Math.ceil(remainSeconds / 60)); - } - return mega.icu.format(l.free_call_banner_organiser_warning, Math.ceil(remainSeconds / 60)); - } - if (remainSeconds < 60) { - return mega.icu.format(l.free_call_banner_ending_sec, remainSeconds); - } - if (remainSeconds <= 120) { - return mega.icu.format(l.free_call_banner_ending, Math.ceil(remainSeconds / 60)); - } - return mega.icu.format(l.free_call_banner_warning, Math.ceil(remainSeconds / 60)); - } - updateTimeoutDuration() { - if (this.timeoutBannerRef) { - const { - current - } = this.timeoutBannerRef; - const newStr = this.timeoutString; - if (newStr && current && current.innerText !== newStr) { - current.innerText = newStr; - } - if (this.state.showTimeoutUpgrade && this.props.call.callEndTime - Date.now() <= 12e4) { - this.setState({ - showTimeoutUpgrade: false - }); - } - } - } - componentWillUnmount() { - super.componentWillUnmount(); - const { - minimized, - willUnmount, - chatRoom - } = this.props; - chatRoom.megaChat.off(`sfuConnClose.${NAMESPACE}`); - chatRoom.megaChat.off(`sfuConnOpen.${NAMESPACE}`); - chatRoom.megaChat.off(`onSpeakerChange.${NAMESPACE}`); - chatRoom.megaChat.off(`onPeerAvChange.${NAMESPACE}`); - mBroadcaster.removeListener(this.ephemeralAddListener); - mBroadcaster.removeListener(this.pageChangeListener); - clearTimeout(this.callStartTimeout); - delay.cancel('callOffline'); - if ($.dialog) { - closeDialog(); - } - if (this.timeoutBannerInterval) { - clearInterval(this.timeoutBannerInterval); - } - window.toaster.main.hideAll(); - this.unbindCallEvents(); - willUnmount == null || willUnmount(minimized); - } - componentDidMount() { - super.componentDidMount(); - const { - call, - didMount, - chatRoom - } = this.props; - this.ephemeralAddListener = mBroadcaster.addListener('meetings:ephemeralAdd', handle => this.handleEphemeralAdd(handle)); - this.pageChangeListener = mBroadcaster.addListener('pagechange', () => { - const currentRoom = megaChat.getCurrentRoom(); - if (Call.isExpanded() && (!M.chat || currentRoom && currentRoom.chatId !== chatRoom.chatId)) { - this.handleCallMinimize(); - } - }); - chatRoom.megaChat.rebind(`sfuConnOpen.${NAMESPACE}`, () => this.handleCallOnline()); - chatRoom.megaChat.rebind(`sfuConnClose.${NAMESPACE}`, () => this.handleCallOffline()); - chatRoom.rebind(`onCallState.${NAMESPACE}`, (ev, { - arg - }) => this.setState({ - initialCallRinging: arg - })); - const { - tresizer - } = $; - chatRoom.rebind(`onPeerAvChange.${NAMESPACE}`, tresizer); - chatRoom.rebind(`onSpeakerChange.${NAMESPACE}`, tresizer); - this.callStartTimeout = setTimeout(() => { - if (!mega.config.get('callemptytout') && !call.hasOtherParticipant()) { - call.left = true; - call.initCallTimeout(); - } - }, 300000); - setTimeout(() => { - let _call$peers; - return ((_call$peers = call.peers) == null ? void 0 : _call$peers.length) && !call.hasOtherParticipant() && this.setState({ - everHadPeers: true - }); - }, 2e3); - if (sessionStorage.previewMedia) { - const { - audio, - video - } = JSON.parse(sessionStorage.previewMedia); - sessionStorage.removeItem('previewMedia'); - tSleep(2).then(() => audio && call.sfuClient.muteAudio()).then(() => video && call.sfuClient.muteCamera()).catch(dump); - } - this.bindCallEvents(); - didMount == null || didMount(); - } - componentDidUpdate() { - if (typeof psa !== 'undefined') { - psa.repositionMeetingsCall(); - } - } - render() { - let _ref; - const { - minimized, - peers, - call, - chatRoom, - parent, - typingAreaText, - onDeleteMessage, - onTypingAreaChanged - } = this.props; - const { - mode, - view, - sidebar, - hovered, - forcedLocal, - invite, - ephemeral, - ephemeralAccounts, - guest, - offline, - onboardingUI, - onboardingRecording, - onboardingRaise, - everHadPeers, - initialCallRinging, - waitingRoomPeers, - recorderCid, - raisedHandPeers, - recordingConsentDialog, - invitePanel, - presenterThumbSelected, - timeoutBanner, - activeElement - } = this.state; - const { - stayOnEnd - } = call; - const hasOnboarding = onboardingUI || onboardingRecording || onboardingRaise; - const STREAM_PROPS = { - mode, - peers, - sidebar, - hovered: hasOnboarding || hovered, - forcedLocal, - call, - view, - chatRoom, - parent, - stayOnEnd, - everHadPeers, - waitingRoomPeers, - recorderCid, - presenterThumbSelected, - raisedHandPeers, - activeElement, - hasOtherParticipants: call.hasOtherParticipant(), - isOnHold: call.sfuClient.isOnHold, - isFloatingPresenter: (_ref = mode === MODE.MINI && !forcedLocal ? call.getActiveStream() : call.getLocalStream()) == null ? void 0 : _ref.hasScreen, - onSpeakerChange: this.handleSpeakerChange, - onModeChange: this.handleModeChange, - onInviteToggle: this.handleInviteToggle, - onStayConfirm: this.handleStayConfirm - }; - return REaCt().createElement("div", { - ref: this.domRef, - className: ` - meetings-call - ${minimized ? 'minimized' : ''} - ${timeoutBanner ? 'with-timeout-banner' : ''} - ${activeElement ? 'with-active-element' : ''} - `, - onMouseMove: hasOnboarding ? null : this.handleMouseMove, - onMouseOut: hasOnboarding ? null : this.handleMouseOut - }, timeoutBanner && this.renderTimeLimitBanner(), REaCt().createElement(stream.Ay, (0,esm_extends.A)({}, STREAM_PROPS, { - minimized, - ephemeralAccounts, - onCallMinimize: this.handleCallMinimize, - onCallExpand: this.handleCallExpand, - onCallEnd: this.handleCallEnd, - onStreamToggle: this.handleStreamToggle, - onRecordingToggle: () => call.sfuClient.recordingStop(), - onChatToggle: this.handleChatToggle, - onParticipantsToggle: this.handleParticipantsToggle, - onAudioClick: () => call.toggleAudio(), - onVideoClick: () => call.toggleVideo(), - onScreenSharingClick: this.handleScreenSharingToggle, - onHoldClick: this.handleHoldToggle, - onVideoDoubleClick: this.handleSpeakerChange, - setActiveElement: this.setActiveElement - })), sidebar && REaCt().createElement(Sidebar, (0,esm_extends.A)({}, STREAM_PROPS, { - guest, - initialCallRinging, - typingAreaText, - onGuestClose: () => this.setState({ - guest: false - }), - onSidebarClose: () => this.setState({ - ...Call.STATE.DEFAULT - }), - onDeleteMessage, - onCallMinimize: this.handleCallMinimize, - onInviteToggle: () => this.handleInviteOrAdd(), - onTypingAreaChanged - })), minimized ? null : REaCt().createElement(REaCt().Fragment, null, this.renderRecordingControl(), REaCt().createElement(streamControls.Ay, { - call, - minimized, - peers, - chatRoom, - recorderCid, - hovered, - raisedHandPeers, - onboardingRaise, - onOnboardingRaiseDismiss: () => { - this.setState({ - onboardingRaise: false, - hovered: false - }, () => { - this.flagMap.setSync(OBV4_FLAGS.CHAT_CALL_RAISE, 1); - this.flagMap.safeCommit(); - }); - }, - onRecordingToggle: () => this.setState({ - recorderCid: undefined - }, () => call.sfuClient.recordingStop()), - onAudioClick: () => call.toggleAudio(), - onVideoClick: () => call.toggleVideo(), - onScreenSharingClick: this.handleScreenSharingToggle, - onCallEnd: this.handleCallEnd, - onStreamToggle: this.handleStreamToggle, - onHoldClick: this.handleHoldToggle, - setActiveElement: this.setActiveElement - }), REaCt().createElement(sidebarControls, { - call, - chatRoom, - npeers: peers.length, - mode, - view, - sidebar, - onChatToggle: this.handleChatToggle, - onParticipantsToggle: this.handleParticipantsToggle, - onInviteToggle: () => this.handleInviteOrAdd() - })), invite && REaCt().createElement(Invite, { - contacts: M.u, - call, - chatRoom, - onClose: () => this.setState({ - invite: false - }) - }), ephemeral && REaCt().createElement(workflow_ephemeral, { - ephemeralAccounts, - onClose: () => this.setState({ - ephemeral: false - }) - }), offline && REaCt().createElement(meetings_offline, { - onClose: () => { - if (offline) { - this.setState({ - offline: false - }, () => delay('call:timeout', this.handleRetryTimeout, 3e4)); - } - }, - onCallEnd: () => { - this.setState({ - offline: false - }, () => this.handleRetryTimeout()); - } - }), onboardingUI && REaCt().createElement("div", { - className: `${NAMESPACE}-onboarding` - }, REaCt().createElement("div", { - className: "mega-dialog mega-onboarding-dialog dialog-template-message onboarding-UI", - id: "ob-dialog", - role: "dialog", - "aria-labelledby": "ob-dialog-title", - "aria-modal": "true" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-tooltip-arrow tooltip-arrow top", - id: "ob-dialog-arrow" - }), REaCt().createElement("header", null, REaCt().createElement("div", null, REaCt().createElement("h2", { - id: "ob-dialog-title" - }, l.onboarding_call_title), REaCt().createElement("p", { - id: "ob-dialog-text" - }, l.onboarding_call_body))), REaCt().createElement("footer", null, REaCt().createElement("div", { - className: "footer-container" - }, REaCt().createElement("button", { - className: "mega-button js-next small theme-light-forced", - onClick: () => { - this.setState({ - onboardingUI: false, - onboardingRecording: chatRoom.iAmOperator() && !this.flagMap.getSync(OBV4_FLAGS.CHAT_CALL_RECORDING) - }, () => { - this.flagMap.setSync(OBV4_FLAGS.CHAT_CALL_UI, 1); - this.flagMap.safeCommit(); - this.setState({ - onboardingRaise: !this.state.onboardingRecording && !this.flagMap.getSync(OBV4_FLAGS.CHAT_CALL_RAISE) - }); - }); - } - }, REaCt().createElement("span", null, l.ok_button)))))), onboardingRecording && Call.isModerator(chatRoom, u_handle) && REaCt().createElement("div", { - className: `${NAMESPACE}-onboarding` - }, REaCt().createElement("div", { - className: "mega-dialog mega-onboarding-dialog dialog-template-message onboarding-recording", - id: "ob-dialog", - role: "dialog", - "aria-labelledby": "ob-dialog-title", - "aria-modal": "true" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-tooltip-arrow tooltip-arrow bottom", - id: "ob-dialog-arrow" - }), REaCt().createElement("header", null, REaCt().createElement("div", null, REaCt().createElement("h2", { - id: "ob-dialog-title" - }, l.recording_onboarding_title), REaCt().createElement("p", { - id: "ob-dialog-text" - }, l.recording_onboarding_body_intro), REaCt().createElement("p", { - id: "ob-dialog-text" - }, l.recording_onboarding_body_details))), REaCt().createElement("footer", null, REaCt().createElement("div", { - className: "footer-container" - }, REaCt().createElement(ui_link.A, { - className: "link-button", - to: "https://help.mega.io/chats-meetings/chats/call-recording", - target: "_blank" - }, l[8742]), REaCt().createElement("button", { - className: "mega-button js-next small theme-light-forced", - onClick: () => { - this.setState({ - onboardingRecording: false, - onboardingRaise: true - }, () => { - this.flagMap.setSync(OBV4_FLAGS.CHAT_CALL_RECORDING, 1); - this.flagMap.safeCommit(); - }); - } - }, REaCt().createElement("span", null, l.ok_button)))))), recordingConsentDialog && REaCt().createElement(RecordingConsentDialog, { - peers, - recorderCid, - onClose: () => this.setState({ - recordingConsentDialog: false, - recordingConsented: true - }), - onCallEnd: this.handleCallEnd - }), invitePanel && REaCt().createElement(modalDialogs.A.ModalDialog, { - className: "theme-dark-forced", - onClose: () => { - this.setState({ - invitePanel: false - }); - }, - dialogName: "chat-link-dialog", - chatRoom - }, REaCt().createElement(inviteParticipantsPanel.Q, { - chatRoom, - onAddParticipants: () => { - this.setState({ - invitePanel: false - }, () => this.handleInviteToggle()); - } - }))); - } -} -Call.STATE = { - DEFAULT: { - sidebar: false - }, - PREVIOUS: { - mode: null, - sidebar: null, - view: null - } -}; -Call.isModerator = (chatRoom, handle) => { - if (chatRoom && handle) { - return chatRoom.members[handle] === ChatRoom.MembersSet.PRIVILEGE_STATE.OPERATOR; - } - return false; -}; -Call.isExpanded = () => document.body.classList.contains(EXPANDED_FLAG); -Call.getUnsupportedBrowserMessage = () => navigator.userAgent.match(/Chrom(e|ium)\/(\d+)\./) ? l.alert_unsupported_browser_version : l.alert_unsupported_browser; - -}, - -972 -(_, EXP_, REQ_) { - -"use strict"; -REQ_.d(EXP_, { -C: () => withHostsObserver -}); -const _extends0__ = REQ_(168); -const react1__ = REQ_(594); -const react1 = REQ_.n(react1__); -const _mixins_js2__ = REQ_(137); -const _ui_modalDialogs_jsx3__ = REQ_(318); -const _contacts_jsx4__ = REQ_(251); -const _ui_buttons_jsx5__ = REQ_(994); - - - - - - -const withHostsObserver = Component => { - return class extends _mixins_js2__.w9 { - constructor(...args) { - super(...args); - this.state = { - dialog: false, - selected: [] - }; - this.hasHost = participants => participants.some(handle => this.props.chatRoom.members[handle] === ChatRoom.MembersSet.PRIVILEGE_STATE.OPERATOR); - this.toggleDialog = () => { - this.setState(state => ({ - dialog: !state.dialog, - selected: [] - }), () => this.safeForceUpdate()); - }; - this.renderDialog = () => { - let _this$props$participa; - const { - selected - } = this.state; - return react1().createElement(_ui_modalDialogs_jsx3__.A.ModalDialog, (0,_extends0__.A)({}, this.state, { - className: "assign-host contact-picker-widget", - dialogName: "assign-host-dialog", - dialogType: "tool", - onClose: () => this.setState({ - dialog: false - }, () => this.safeForceUpdate()) - }), react1().createElement("header", null, react1().createElement("h2", null, l.assign_host_title)), react1().createElement("div", { - className: "content-block" - }, react1().createElement(_contacts_jsx4__.ContactPickerWidget, { - className: "popup contacts-search small-footer", - contacts: (_this$props$participa = this.props.participants) == null ? void 0 : _this$props$participa.filter(h => h !== u_handle), - multiple: true, - hideSearch: true, - disableFrequents: true, - participantsList: true, - disableDoubleClick: true, - emailTooltips: true, - nothingSelectedButtonLabel: l.add_hosts_placeholder, - onClose: () => this.setState({ - dialog: false - }), - onSelected: selected => this.setState({ - selected - }, () => this.safeForceUpdate()) - })), react1().createElement("footer", null, react1().createElement("div", { - className: "footer-container" - }, react1().createElement(_ui_buttons_jsx5__.$, { - label: l.msg_dlg_cancel, - className: "mega-button", - onClick: this.toggleDialog - }), react1().createElement(_ui_buttons_jsx5__.$, { - label: l.assign_and_leave, - className: ` - mega-button - positive - ${selected.length ? '' : 'disabled'} - `, - onClick: () => selected.length && this.assignAndLeave() - })))); - }; - this.assignAndLeave = () => { - const { - chatRoom, - onLeave - } = this.props; - const { - selected - } = this.state; - for (let i = selected.length; i--;) { - chatRoom.trigger('alterUserPrivilege', [selected[i], ChatRoom.MembersSet.PRIVILEGE_STATE.OPERATOR]); - } - this.toggleDialog(); - onLeave == null || onLeave(); - $(document).trigger('closeDropdowns'); - }; - this.confirmLeave = ({ - title, - body, - cta, - altCta - }) => { - msgDialog(`confirmationa:!^${cta}!${altCta || l.msg_dlg_cancel}`, null, title, body, cb => { - if (cb) { - this.toggleDialog(); - } else if (cb === false) { - let _this$props$onConfirm, _this$props; - (_this$props$onConfirm = (_this$props = this.props).onConfirmDenied) == null || _this$props$onConfirm.call(_this$props); - } - }, 1); - }; - } - render() { - return react1().createElement(react1().Fragment, null, react1().createElement(Component, (0,_extends0__.A)({}, this.props, { - confirmLeave: this.confirmLeave, - hasHost: this.hasHost - })), this.state.dialog && this.renderDialog()); - } - }; -}; - -}, - -772 -(_, EXP_, REQ_) { - -"use strict"; -REQ_.d(EXP_, { -Q: () => withMicObserver -}); -const _extends0__ = REQ_(168); -const react1__ = REQ_(594); -const react1 = REQ_.n(react1__); -const _mixins2__ = REQ_(137); -const _button_jsx3__ = REQ_(959); - - - - -const withMicObserver = Component => class extends _mixins2__.w9 { - constructor(props) { - super(props); - this.namespace = `SO-${Component.NAMESPACE}`; - this.inputObserver = `onNoMicInput.${this.namespace}`; - this.sendObserver = `onAudioSendDenied.${this.namespace}`; - this.state = { - signal: true, - blocked: false - }; - this.renderSignalWarning = this.renderSignalWarning.bind(this); - this.renderBlockedWarning = this.renderBlockedWarning.bind(this); - } - bindObservers() { - this.props.chatRoom.rebind(this.inputObserver, () => this.setState({ - signal: false - })).rebind(this.sendObserver, () => { - this.setState({ - blocked: true - }, () => { - if (this.props.minimized) { - const toast = new ChatToast(l.max_speakers_toast, { - icon: 'sprite-fm-uni icon-hazard', - close: true - }); - toast.dispatch(); - } - }); - }); - } - renderSignalDialog() { - return msgDialog('warningb', null, l.no_mic_title, l.chat_mic_off_tooltip, null, 1); - } - renderSignalWarning() { - return react1().createElement("div", { - className: ` - ${this.namespace} - meetings-signal-issue - simpletip - `, - "data-simpletip": l.show_info, - "data-simpletipposition": "top", - "data-simpletipoffset": "5", - "data-simpletip-class": "theme-dark-forced", - onClick: () => this.renderSignalDialog() - }, react1().createElement("i", { - className: "sprite-fm-mono icon-exclamation-filled" - })); - } - renderBlockedWarning() { - return react1().createElement("div", { - className: "stream-toast theme-dark-forced" - }, react1().createElement("div", { - className: "stream-toast-content" - }, react1().createElement("i", { - className: "stream-toast-icon sprite-fm-uni icon-warning" - }), react1().createElement("div", { - className: "stream-toast-message" - }, l.max_speakers_toast), react1().createElement(_button_jsx3__.A, { - className: "mega-button action stream-toast-close", - icon: "sprite-fm-mono icon-close-component", - onClick: () => this.setState({ - blocked: false - }) - }))); - } - componentWillUnmount() { - super.componentWillUnmount(); - this.props.chatRoom.unbind(this.inputObserver); - } - componentDidMount() { - super.componentDidMount(); - this.bindObservers(); - } - render() { - return react1().createElement(Component, (0,_extends0__.A)({}, this.props, { - signal: this.state.signal, - renderSignalWarning: this.renderSignalWarning, - blocked: this.state.blocked, - renderBlockedWarning: this.renderBlockedWarning - })); - } -}; - -}, - -542 -(_, EXP_, REQ_) { - -"use strict"; -REQ_.d(EXP_, { -$: () => withPermissionsObserver -}); -const _extends0__ = REQ_(168); -const react1__ = REQ_(594); -const react1 = REQ_.n(react1__); -const _mixins_js2__ = REQ_(137); -const _ui_modalDialogs_jsx3__ = REQ_(318); -const _ui_utils_jsx4__ = REQ_(314); - - - - - -const errors = { - browser: 'NotAllowedError: Permission denied', - system: 'NotAllowedError: Permission denied by system', - dismissed: 'NotAllowedError: Permission dismissed', - nil: 'NotFoundError: Requested device not found', - sharedCam: 'NotReadableError: Could not start video source', - sharedMic: 'NotReadableError: Could not start audio source', - sharedGeneric: 'NotReadableError: Device in use' -}; -const isUserActionError = error => { - return error && error === errors.browser; -}; -const withPermissionsObserver = Component => { - return class extends _mixins_js2__.w9 { - constructor(props) { - super(props); - this.namespace = `PO-${Component.NAMESPACE}`; - this.observer = `onLocalMediaError.${this.namespace}`; - this.childRef = undefined; - this.platform = ua.details.os; - this.helpURL = `${l.mega_help_host}/chats-meetings/meetings/enable-audio-video-call-permissions`; - this.macURI = 'x-apple.systempreferences:com.apple.preference.security'; - this.winURI = 'ms-settings'; - this.CONTENT = { - [Av.Audio]: { - system: { - title: l.no_mic_title, - info: this.platform === 'Windows' ? l.no_mic_system_windows.replace('[A]', ``).replace('[/A]', '') : l.no_mic_system_mac.replace('[A]', ``).replace('[/A]', ''), - buttons: [this.platform === 'Apple' || this.platform === 'Windows' ? { - key: 'open-settings', - label: l.open_system_settings, - className: 'positive', - onClick: () => { - window.open(this.platform === 'Apple' ? `${this.macURI}?Privacy_Microphone` : `${this.winURI}:privacy-microphone`, '_blank', 'noopener,noreferrer'); - this.closePermissionsDialog(Av.Audio); - } - } : { - key: 'ok', - label: l.ok_button, - className: 'positive', - onClick: () => this.closePermissionsDialog(Av.Audio) - }] - }, - browser: { - title: l.no_mic_title, - cover: 'permissions-mic', - info: l.allow_mic_access.replace('[X]', ''), - buttons: [{ - key: 'ok', - label: l.ok_button, - className: 'positive', - onClick: () => this.closePermissionsDialog(Av.Audio) - }] - }, - nil: { - title: l.no_mic_detected_title, - info: l.no_mic_detected_info, - buttons: [{ - key: 'ok', - label: l.ok_button, - className: 'positive', - onClick: () => this.closePermissionsDialog(Av.Audio) - }] - }, - shared: { - title: l.no_mic_title, - info: l.shared_mic_err_info.replace('[A]', ``).replace('[/A]', ''), - buttons: [{ - key: 'ok', - label: l.ok_button, - className: 'positive', - onClick: () => this.closePermissionsDialog(Av.Audio) - }] - } - }, - [Av.Camera]: { - system: { - title: l.no_camera_title, - info: this.platform === 'Windows' ? l.no_camera_system_windows.replace('[A]', ``).replace('[/A]', '') : l.no_camera_system_mac.replace('[A]', ``).replace('[/A]', ''), - buttons: [this.platform === 'Apple' || this.platform === 'Windows' ? { - key: 'open-settings', - label: l.open_system_settings, - className: 'positive', - onClick: () => { - window.open(this.platform === 'Apple' ? `${this.macURI}?Privacy_Camera` : `${this.winURI}:privacy-webcam`, '_blank', 'noopener,noreferrer'); - this.closePermissionsDialog(Av.Camera); - } - } : { - key: 'ok', - label: l.ok_button, - className: 'positive', - onClick: () => this.closePermissionsDialog(Av.Camera) - }] - }, - browser: { - title: l.no_camera_title, - cover: 'permissions-camera', - info: l.allow_camera_access.replace('[X]', ''), - buttons: [{ - key: 'ok', - label: l.ok_button, - className: 'positive', - onClick: () => this.closePermissionsDialog(Av.Camera) - }] - }, - nil: { - title: l.no_camera_detected_title, - info: l.no_camera_detected_info, - buttons: [{ - key: 'ok', - label: l.ok_button, - className: 'positive', - onClick: () => this.closePermissionsDialog(Av.Camera) - }] - }, - shared: { - title: l.no_camera_title, - info: l.shared_cam_err_info.replace('[A]', ``).replace('[/A]', ''), - buttons: [{ - key: 'ok', - label: l.ok_button, - className: 'positive', - onClick: () => this.closePermissionsDialog(Av.Camera) - }] + contacts.push(JSX_(ContactCard, { + withSelfNote, + disabled: isDisabled, + contact: v, + chatRoom: withSelfNote && megaChat.getNoteChat(), + className: `contacts-search short ${ selectedClass }${isDisabled ? " disabled" : ""}`, + noContextButton: "true", + selectable: selectableContacts, + onClick: self.props.readOnly ? () => {} : contact => { + if (isDisabled) { + return false; + } + const contactHash = contact.u; + if (contactHash === self.lastClicked && new Date() - self.clickTime < 500 && !self.props.disableDoubleClick || !self.props.multiple) { + if (self.props.onSelected) { + self.props.onSelected([contactHash]); } - }, - [Av.Screen]: { - title: l.no_screen_title, - info: l.no_screen_system.replace('[A]', ``).replace('[/A]', ''), - buttons: [{ - key: 'open-settings', - label: l.open_system_settings, - className: 'positive', - onClick: () => { - window.open(`${this.macURI}?Privacy_ScreenCapture`, '_blank', 'noopener,noreferrer'); - this.closePermissionsDialog(Av.Screen); + self.props.onSelectDone([contactHash]); + closeDropdowns(); + return; + } else { + const selected = clone(self.state.selected || []); + if (selected.indexOf(contactHash) === -1) { + selected.push(contactHash); + self.scrollToLastSelected = true; + if (self.props.onSelected) { + self.props.onSelected(selected); + } + } else { + if (selected.indexOf(contactHash) >= 0) { + array.remove(selected, contactHash); + } + if (self.props.onSelected) { + self.props.onSelected(selected); } - }] - } - }; - this.state = { - errMic: '', - errCamera: '', - errScreen: '', - [`dialog-${Av.Audio}`]: null, - [`dialog-${Av.Camera}`]: null, - [`dialog-${Av.Screen}`]: null - }; - this.getPermissionsDialogContent = () => { - const { - CONTENT, - state - } = this; - const { - errMic, - errCamera - } = state; - const { - browser, - system, - nil, - sharedCam, - sharedMic, - sharedGeneric - } = errors; - return { - [Av.Audio]: { - ...errMic === browser && CONTENT[Av.Audio].browser, - ...errMic === system && CONTENT[Av.Audio].system, - ...errMic === nil && CONTENT[Av.Audio].nil, - ...errMic === sharedMic && CONTENT[Av.Audio].shared, - ...errMic === sharedGeneric && CONTENT[Av.Audio].shared - }, - [Av.Camera]: { - ...errCamera === browser && CONTENT[Av.Camera].browser, - ...errCamera === system && CONTENT[Av.Camera].system, - ...errCamera === nil && CONTENT[Av.Camera].nil, - ...errCamera === sharedCam && CONTENT[Av.Camera].shared, - ...errCamera === sharedGeneric && CONTENT[Av.Camera].shared - }, - [Av.Screen]: CONTENT[Av.Screen] - }; - }; - this.resetError = av => { - this.setState({ - errMic: av === Av.Audio ? '' : this.state.errMic, - errCamera: av === Av.Camera ? '' : this.state.errCamera, - errScreen: av === Av.Screen ? '' : this.state.errScreen - }); - }; - this.hasToRenderPermissionsWarning = this.hasToRenderPermissionsWarning.bind(this); - this.renderPermissionsWarning = this.renderPermissionsWarning.bind(this); - } - hasToRenderPermissionsWarning(av) { - const CONFIG = { - [Av.Audio]: { - showOnUserActionError: true, - err: this.state.errMic - }, - [Av.Camera]: { - showOnUserActionError: true, - err: this.state.errCamera - }, - [Av.Screen]: { - showOnUserActionError: false, - err: this.state.errScreen - } - }; - const current = CONFIG[av]; - if (current) { - return isUserActionError(current.err) ? current.showOnUserActionError : current.err; - } - return false; - } - closePermissionsDialog(av) { - this.setState({ - [`dialog-${av}`]: false - }, () => { - let _this$childRef; - return (_this$childRef = this.childRef) == null ? void 0 : _this$childRef.safeForceUpdate(); - }); - } - renderPermissionsDialog(av, child) { - const content = this.getPermissionsDialogContent(); - const { - title, - info, - buttons, - cover - } = content[av] || {}; - return react1().createElement(_ui_modalDialogs_jsx3__.A.ModalDialog, { - dialogName: `${this.namespace}-permissions-${av}`, - className: ` - meetings-permissions-dialog - dialog-template-message - with-close-btn - warning - `, - buttons, - hideOverlay: Component.NAMESPACE === 'preview-meeting' && !document.body.classList.contains('not-logged'), - onClose: () => { - this.setState({ - [`dialog-${av}`]: false - }, () => child && child.safeForceUpdate()); - } - }, react1().createElement("header", null, cover ? null : react1().createElement("div", { - className: "graphic" - }, react1().createElement("i", { - className: "warning sprite-fm-uni icon-warning" - })), react1().createElement("div", { - className: "info-container" - }, react1().createElement("h3", { - id: "msgDialog-title" - }, title || l[47]), cover && react1().createElement("div", { - className: "permissions-warning-cover" - }, react1().createElement("span", { - className: cover - })), react1().createElement(_ui_utils_jsx4__.P9, { - tag: "p", - className: "permissions-warning-info", - content: info - })))); - } - renderPermissionsWarning(av, child) { - const { - errMic, - errCamera - } = this.state; - const dismissed = errMic === errors.dismissed || errCamera === errors.dismissed; - return react1().createElement("div", { - className: ` - ${this.namespace} - meetings-signal-issue - simpletip - ${dismissed ? 'with-small-area' : ''} - `, - "data-simpletip": l.show_info, - "data-simpletipposition": "top", - "data-simpletipoffset": "5", - "data-simpletip-class": "theme-dark-forced", - onClick: () => dismissed ? null : this.setState({ - [`dialog-${av}`]: true - }, () => { - if (child) { - this.childRef = child; } - }) - }, react1().createElement("span", { - className: "signal-issue-background" - }), react1().createElement("i", { - className: "sprite-fm-mono icon-exclamation-filled" - }), this.state[`dialog-${av}`] && this.renderPermissionsDialog(av, child)); - } - componentWillUnmount() { - super.componentWillUnmount(); - megaChat.unbind(this.observer); - } - componentDidMount() { - super.componentDidMount(); - megaChat.rebind(this.observer, (ev, errAv) => { - this.setState({ - errMic: errAv && errAv.mic ? String(errAv.mic) : this.state.errMic, - errCamera: errAv && errAv.camera ? String(errAv.camera) : this.state.errCamera, - errScreen: errAv && errAv.screen ? String(errAv.screen) : this.state.errScreen - }); - }); - megaChat.rebind(`onLocalMediaQueryError.${this.namespace}`, (ev, { - type, - err - }) => { - if (type === 'screen' && String(err) === errors.system) { - this.setState({ - [`dialog-${Av.Screen}`]: true - }, () => this.safeForceUpdate()); - } - }); - } - render() { - return react1().createElement(Component, (0,_extends0__.A)({}, this.props, this.state, { - errMic: this.state.errMic, - errCamera: this.state.errCamera, - errScreen: this.state.errScreen, - hasToRenderPermissionsWarning: this.hasToRenderPermissionsWarning, - resetError: this.resetError, - renderPermissionsWarning: this.renderPermissionsWarning - })); - } - }; -}; - -}, - -110 -(_, EXP_, REQ_) { - -"use strict"; -REQ_.d(EXP_, { -PS: () => addMonths, -We: () => stringToTime, -XH: () => stringToDate, -a4: () => getTimeIntervals, -cK: () => isToday, -dB: () => getUserTimezone, -ef: () => isTomorrow, -i_: () => getNearestHalfHour, -ro: () => isSameDay -}); - -const stringToDate = string => { - return moment(string, ['DD MMM YYYY', 'DD-MM-YYYY', 'DD.MM.YYYY', 'MMM DD YYYY', 'YYYY MMM DD', 'YYYY DD MMM']); -}; -const stringToTime = string => moment(string, ['HH:mm', 'hh:mm A']); -const isSameDay = (a, b) => { - return new Date(a).toDateString() === new Date(b).toDateString(); -}; -const isToday = timestamp => { - return new Date(timestamp).toDateString() === new Date().toDateString(); -}; -const isTomorrow = timestamp => { - const tomorrow = new Date(); - tomorrow.setDate(tomorrow.getDate() + 1); - return tomorrow.toDateString() === new Date(timestamp).toDateString(); -}; -const getDaysInMonth = (year, month) => { - return new Date(year, month, 0).getDate(); -}; -const addMonths = (timestamp, months) => { - const date = new Date(timestamp); - return new Date(date.setMonth(date.getMonth() + months)).getTime(); -}; -const getNearestHalfHour = (timestamp = Date.now()) => { - const { - SCHEDULED_MEETINGS_INTERVAL - } = ChatRoom; - return new Date(Math.ceil(timestamp / SCHEDULED_MEETINGS_INTERVAL) * SCHEDULED_MEETINGS_INTERVAL).getTime(); -}; -const getUserTimezone = () => { - return Intl.DateTimeFormat().resolvedOptions().timeZone; -}; -const getTimeIntervals = (timestamp, offsetFrom, interval = 30) => { - const increments = []; - if (timestamp) { - const [targetDate, initialDate] = [new Date(timestamp), new Date(timestamp)].map(date => { - date.setHours(0); - date.setMinutes(0); - return date; - }); - while (targetDate.getDate() === initialDate.getDate()) { - const timestamp = targetDate.getTime(); - const diff = offsetFrom && timestamp - offsetFrom; - increments.push({ - value: timestamp, - label: toLocaleTime(timestamp), - duration: diff && diff > 0 ? diff : undefined - }); - targetDate.setMinutes(targetDate.getMinutes() + interval); - } - } - return increments; -}; - -}, - -415 -(_, EXP_, REQ_) { - -"use strict"; - -// EXPORTS -REQ_.d(EXP_, { - $A: () => MAX_STREAMS, - Bq: () => PAGINATION, - gh: () => STREAMS_PER_PAGE, - hK: () => STREAM_ACTIONS, - ro: () => chunkNodes, - Ay: () => stream_Stream, - iv: () => filterAndSplitSources -}); - -// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/extends.js -const esm_extends = REQ_(168); -// EXTERNAL MODULE: external "React" -const React_ = REQ_(594); -const REaCt = REQ_.n(React_); -// EXTERNAL MODULE: ./js/chat/mixins.js -const mixins = REQ_(137); -// EXTERNAL MODULE: ./js/chat/ui/meetings/call.jsx + 11 modules -const meetings_call = REQ_(3); -// EXTERNAL MODULE: ./js/chat/ui/meetings/videoNode.jsx -const videoNode = REQ_(414); -// EXTERNAL MODULE: ./js/ui/utils.jsx -const utils = REQ_(314); -// EXTERNAL MODULE: ./js/chat/ui/meetings/button.jsx -const meetings_button = REQ_(959); -// EXTERNAL MODULE: ./js/ui/dropdowns.jsx -const dropdowns = REQ_(911); -// EXTERNAL MODULE: ./js/ui/buttons.jsx -const buttons = REQ_(994); -;// ./js/chat/ui/meetings/floatExtendedControls.jsx - - - -class FloatExtendedControls extends REaCt().Component { - constructor(...args) { - super(...args); - this.isActive = type => { - return !!(this.props.call.av & type); - }; - } - render() { - const { - hasToRenderPermissionsWarning, - renderPermissionsWarning, - resetError, - showScreenDialog, - onScreenSharingClick, - onHoldClick - } = this.props; - const { - onHold, - Screen - } = SfuClient.Av; - const isOnHold = this.isActive(onHold); - const callHoldLabel = isOnHold ? l[23459] : l[23460]; - const screenSharingLabel = this.isActive(Screen) ? l[22890] : l[22889]; - return REaCt().createElement(buttons.$, { - className: "mega-button theme-light-forced round large button-group", - icon: "sprite-fm-mono icon-options", - showScreenDialog - }, this.isActive(Screen) && REaCt().createElement("div", { - className: "info-indicator active" - }), REaCt().createElement(dropdowns.Dropdown, { - className: "button-group-menu theme-dark-forced", - noArrow: true, - positionAt: "center top", - collision: "none", - vertOffset: -90, - ref: r => { - this.dropdownRef = r; - }, - onBeforeActiveChange: e => { - if (e) { - $(document.body).trigger('closeAllDropdownsExcept', this.dropdownRef); + self.setState({ + selected + }); + if (self.props.selectCleanSearchRes) { + self.setState({ + 'searchValue': '' + }); + } + if (self.props.autoFocusSearchField) { + let _self$contactSearchFi; + (_self$contactSearchFi = self.contactSearchField) == null || _self$contactSearchFi.focus(); + } } + self.clickTime = new Date(); + self.lastClicked = contactHash; }, - showScreenDialog - }, REaCt().createElement(dropdowns.DropdownItem, { - key: "call-hold", - className: ` - theme-dark-forced - ${isOnHold ? 'active' : ''} - `, - label: callHoldLabel, - icon: ` - sprite-fm-mono - ${isOnHold ? 'icon-play-small-regular-outline' : 'icon-pause-small-regular-outline'} - `, - onClick: onHoldClick - }), REaCt().createElement(dropdowns.DropdownItem, { - key: "screen-sharing", - className: ` - theme-dark-forced - ${isOnHold ? 'disabled' : ''} - ${this.isActive(Screen) ? 'active' : ''} - `, - label: screenSharingLabel, - icon: ` - sprite-fm-mono - ${this.isActive(Screen) ? 'icon-monitor-off' : 'icon-monitor'} - `, - onClick: () => { - resetError(Av.Screen); - onScreenSharingClick(); - } - }), hasToRenderPermissionsWarning(Screen) ? renderPermissionsWarning(Screen, this) : null)); - } -} -FloatExtendedControls.NAMESPACE = 'stream-extended-controls'; -// EXTERNAL MODULE: ./js/chat/ui/meetings/micObserver.jsx -const micObserver = REQ_(772); -// EXTERNAL MODULE: ./js/chat/ui/meetings/permissionsObserver.jsx -const permissionsObserver = REQ_(542); -// EXTERNAL MODULE: ./js/chat/ui/meetings/hostsObserver.jsx -const hostsObserver = REQ_(972); -// EXTERNAL MODULE: ./js/chat/ui/meetings/streamControls.jsx -const streamControls = REQ_(489); -;// ./js/chat/ui/meetings/float.jsx - - - - - - - - - - - - -class FloatingVideo extends REaCt().Component { - constructor(...args) { - super(...args); - this.collapseListener = null; - this.state = { - collapsed: false - }; - this.toggleCollapsedMode = () => { - return this.setState(state => ({ - collapsed: !state.collapsed - })); - }; - } - componentWillUnmount() { - mBroadcaster.removeListener(this.collapseListener); - } - componentDidMount() { - this.collapseListener = mBroadcaster.addListener('meetings:collapse', () => this.setState({ - collapsed: true + noContextMenu: true, + searchValue: self.state.searchValue, + highlightSearchValue: self.props.highlightSearchValue, + emailTooltips: self.props.emailTooltips, + key: v.u })); - } - componentDidUpdate() { - if (typeof psa !== 'undefined') { - psa.repositionMeetingsCall(); + if (typeof this.props.onEventuallyUpdated === 'function') { + this.props.onEventuallyUpdated(); } + return true; } render() { - const { - peers, - minimized, - call, - floatDetached - } = this.props; - if (peers.length === 0 && !minimized && !call.isSharingScreen()) { - return null; - } - const STREAM_PROPS = { - ...this.props, - collapsed: this.state.collapsed, - toggleCollapsedMode: this.toggleCollapsedMode, - onLoadedData: this.onLoadedData - }; - if (minimized) { - return REaCt().createElement(utils.Ay.RenderTo, { - element: document.body - }, REaCt().createElement(Stream, STREAM_PROPS)); - } - return floatDetached ? REaCt().createElement(Stream, STREAM_PROPS) : null; - } -} -FloatingVideo.NAMESPACE = 'float-video'; -FloatingVideo.POSITION_MODIFIER = 'with-sidebar'; -class Stream extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - this.DRAGGABLE = { - POSITION: { - top: undefined, - left: undefined - }, - OPTIONS: { - scroll: 'false', - cursor: 'move', - opacity: 1, - start: () => { - if (this.state.options) { - this.handleOptionsToggle(); + const self = this; + let contacts = []; + const frequentContacts = []; + let extraClasses = ""; + const contactsSelected = []; + let multipleContacts = null; + let selectableContacts = false; + let selectFooter = null; + let selectedContacts = false; + const isSearching = !!self.state.searchValue; + megaChat.getNoteChat(); + const onAddContact = e => { + e.preventDefault(); + e.stopPropagation(); + contactAddDialog(); + if (this.props.onClose) { + this.props.onClose(); + } + }; + if (self.props.readOnly) { + const sel = self.state.selected || []; + for (let i = 0; i < sel.length; i++) { + const v = sel[i]; + contactsSelected.push(JSX_(ContactItem, { + contact: M.u[v], + key: v, + chatRoom: self.props.chatRoom + })); + } + } else if (self.props.multiple) { + selectableContacts = true; + const onSelectDoneCb = e => { + e.preventDefault(); + e.stopPropagation(); + closeDropdowns(); + if (self.props.onSelectDone) { + self.props.onSelectDone(self.state.selected); + } + }; + let onContactSelectDoneCb = contact => { + const contactHash = contact.u; + if (contactHash === self.lastClicked && new Date() - self.clickTime < 500) { + if (self.props.onSelected) { + self.props.onSelected([contactHash]); } - $(document.body).trigger('closeAllDropdownsExcept'); - }, - stop: (event, ui) => { - this.DRAGGABLE.POSITION = ui.position; - const { - clientWidth, - clientHeight - } = document.body; - const { - helper - } = ui; - const { - left, - top - } = this.DRAGGABLE.POSITION; - if (left < clientWidth / 2) { - helper.css('left', `${left / clientWidth * 100}%`).css('right', 'unset'); + self.props.onSelectDone([contactHash]); + return; + } else { + const selected = clone(self.state.selected || []); + if (selected.indexOf(contactHash) === -1) { + selected.push(contactHash); + self.scrollToLastSelected = true; + if (self.props.onSelected) { + self.props.onSelected(selected); + } } else { - helper.css('left', 'unset').css('right', `${clientWidth - left - helper.width()}px`); + if (selected.indexOf(contactHash) >= 0) { + array.remove(selected, contactHash); + } + if (self.props.onSelected) { + self.props.onSelected(selected); + } } - if (top < clientHeight / 2) { - helper.css('top', `${top / clientHeight * 100}%`).css('bottom', 'unset'); - } else { - helper.css('top', 'unset').css('bottom', `${clientHeight - top - helper.height()}px`); + self.setState({ + selected + }); + if (self.props.selectCleanSearchRes) { + self.setState({ + 'searchValue': '' + }); + } + if (self.props.autoFocusSearchField) { + let _self$contactSearchFi2; + (_self$contactSearchFi2 = self.contactSearchField) == null || _self$contactSearchFi2.focus(); } } - } - }; - this.EVENTS = { - MINIMIZE: ['slideshow:open', 'contact:open', 'textEditor:open', 'chat:open'], - EXPAND: ['slideshow:close', 'textEditor:close'] - }; - this.LISTENERS = []; - this.PREV_STATE = {}; - this.state = { - options: false - }; - this.getStreamSource = () => { - const { - call, - mode, - forcedLocal - } = this.props; - return mode === meetings_call.g.MINI && !forcedLocal ? call.getActiveStream() : call.getLocalStream(); - }; - this.unbindEvents = () => { - const events = [...this.EVENTS.MINIMIZE, ...this.EVENTS.EXPAND]; - for (let i = events.length; i--;) { - const event = events[i]; - mBroadcaster.removeListener(this.LISTENERS[event]); - } - document.removeEventListener('click', this.handleOptionsClose); - }; - this.bindEvents = () => { - for (let i = this.EVENTS.MINIMIZE.length; i--;) { - const event = this.EVENTS.MINIMIZE[i]; - this.LISTENERS[event] = mBroadcaster.addListener(event, () => { - this.PREV_STATE.minimised = this.props.minimized; - return this.props.onCallMinimize(); - }); - } - for (let i = this.EVENTS.EXPAND.length; i--;) { - const event = this.EVENTS.EXPAND[i]; - this.LISTENERS[event] = mBroadcaster.addListener(event, () => { - if (this.PREV_STATE.minimised) { - delete this.PREV_STATE.minimised; - return; + self.clickTime = new Date(); + self.lastClicked = contactHash; + }; + const selectedWidthSize = self.props.selectedWidthSize || 54; + const selectedWidth = self.state.selected.length * selectedWidthSize; + if (!self.state.selected || self.state.selected.length === 0) { + selectedContacts = false; + const emptySelectionMsg = self.props.emptySelectionMsg || l[8889]; + multipleContacts = JSX_("div", { + className: "horizontal-contacts-list" + }, JSX_("div", { + className: "contacts-list-empty-txt" + }, self.props.nothingSelectedButtonLabel ? self.props.nothingSelectedButtonLabel : emptySelectionMsg)); + } else { + selectedContacts = true; + onContactSelectDoneCb = onContactSelectDoneCb.bind(self); + const sel2 = self.state.selected || []; + for (let i2 = 0; i2 < sel2.length; i2++) { + const v2 = sel2[i2]; + contactsSelected.push(JSX_(ContactItem, { + key: v2, + chatRoom: self.props.chatRoom || false, + contact: M.u[v2], + noContextMenu: true, + onClick: onContactSelectDoneCb + })); + } + multipleContacts = JSX_("div", { + className: "horizontal-contacts-list" + }, JSX_(_ui_perfectScrollbar_jsx4__ .O, { + className: "perfectScrollbarContainer selected-contact-block horizontal-only", + selected: this.state.selected, + ref (psSelected) { + self.psSelected = psSelected; } - delete this.PREV_STATE.minimised; - return this.props.view === meetings_call.gR.CHAT && this.props.onCallExpand(); - }); - } - document.addEventListener('click', this.handleOptionsClose); - }; - this.initDraggable = () => { - let _this$domRef; - const { - minimized, - wrapperRef - } = this.props; - const containerEl = (_this$domRef = this.domRef) == null ? void 0 : _this$domRef.current; - if (containerEl) { - $(containerEl).draggable({ - ...this.DRAGGABLE.OPTIONS, - containment: minimized ? 'body' : wrapperRef == null ? void 0 : wrapperRef.current - }); + }, JSX_("div", { + className: "select-contact-centre", + style: { + width: selectedWidth + } + }, contactsSelected))); } - }; - this.repositionDraggable = () => { - let _this$props$wrapperRe, _this$domRef2; - const wrapperEl = (_this$props$wrapperRe = this.props.wrapperRef) == null ? void 0 : _this$props$wrapperRe.current; - const localEl = (_this$domRef2 = this.domRef) == null ? void 0 : _this$domRef2.current; - if (localEl.offsetLeft + localEl.offsetWidth > wrapperEl.offsetWidth) { - localEl.style.left = 'unset'; - localEl.style.removeProperty("right"); + if (self.props.selectFooter) { + selectFooter = JSX_("footer", null, JSX_("button", { + className: "mega-button", + onClick: onAddContact.bind(self) + }, JSX_("span", null, l[71])), JSX_("div", { + className: "footer-spacing" + }), JSX_("button", { + className: `mega-button ${selectedContacts ? '' : 'disabled'}`, + onClick (e) { + if (self.state.selected.length > 0) { + onSelectDoneCb(e); + } + } + }, JSX_("span", null, this.props.multipleSelectedButtonLabel ? this.props.multipleSelectedButtonLabel : l[8890]))); } - }; - this.handleOptionsClose = ({ - target - }) => { - if (this.state.options && !target.classList.contains('icon-options')) { - this.setState({ - options: false - }); + } + const alreadyAdded = {}; + let hideFrequents = !self.props.readOnly && !self.state.searchValue && frequentContacts.length > 0; + let frequentsLoading = false; + if (this.props.readOnly || this.props.disableFrequents) { + hideFrequents = true; + this._foundFrequents = []; + } else if (!self._foundFrequents) { + frequentsLoading = true; + this._foundFrequents = []; + megaChat.getFrequentContacts().then(res => { + this._foundFrequents = res.slice(Math.max(res.length - 30, 0), res.length).reverse(); + }).catch(dump).finally(() => { + if (this.isMounted()) { + this.safeForceUpdate(); + } + }); + } + for (let i = this._foundFrequents.length, total = 0; total < MAX_FREQUENTS && i--;) { + const v = this._foundFrequents[i]; + if (v.userId in M.u && this._eventuallyAddContact(M.u[v.userId], frequentContacts, selectableContacts)) { + alreadyAdded[v.userId] = 1; + total++; } - }; - this.handleOptionsToggle = () => this.setState({ - options: !this.state.options + } + self.props.contacts.forEach((v) => { + alreadyAdded[v.h] || self._eventuallyAddContact(v, contacts, selectableContacts); }); - this.renderOnHoldVideoNode = () => REaCt().createElement(videoNode.Cn, { - chatRoom: this.props.chatRoom + const sortFn = M.getSortByNameFn2(1); + contacts.sort((a, b) => { + return b.props.withSelfNote - a.props.withSelfNote || sortFn(a.props.contact, b.props.contact); }); - this.renderOptionsDialog = () => { - const { - call, - mode, - forcedLocal, - onScreenSharingClick, - onSpeakerChange, - onModeChange, - toggleCollapsedMode, - onMoveIntoGrid - } = this.props; - const IS_SPEAKER_VIEW = mode === meetings_call.g.MAIN && forcedLocal; + if (Object.keys(alreadyAdded).length === 0) { + hideFrequents = true; + } + const innerDivStyles = {}; + if (this.props.showMeAsSelected) { + self._eventuallyAddContact(M.u[u_handle], contacts, selectableContacts, true); + } + let noOtherContacts = false; + if (contacts.length === 0 || !(0,_contactsPanel_utils_jsx10__ .SN)() && this.props.step !== 1) { + noOtherContacts = true; + let noContactsMsg = ""; + if (M.u.length < 2) { + noContactsMsg = l[8877]; + } else { + noContactsMsg = l[8878]; + } + if (hideFrequents) { + contacts = JSX_("em", null, noContactsMsg); + } + } + const haveContacts = isSearching || frequentContacts.length !== 0 || !noOtherContacts; + let contactsList; + if (haveContacts) { + if (frequentContacts.length === 0 && noOtherContacts) { + if (self.props.newEmptySearchResult) { + contactsList = JSX_("div", { + className: "chat-contactspicker-no-contacts flex flex-column flex-center searching mt-2" + }, JSX_("div", { + className: "section-icon sprite-fm-mono icon-contacts" + }), JSX_("div", { + className: "fm-empty-cloud-txt small" + }, l[8674])); + } else { + contactsList = JSX_("div", { + className: "chat-contactspicker-no-contacts flex flex-column mt-2" + }, JSX_("div", { + className: "contacts-list-header" + }, l[165]), JSX_("div", { + className: "flex flex-1 flex-column flex-center" + }, JSX_("div", { + className: "section-icon sprite-fm-mono icon-contacts" + }), JSX_("div", { + className: "fm-empty-cloud-txt small" + }, l[784]), JSX_("div", { + className: "fm-empty-description small" + }, l[19115]))); + } + } else { + contactsList = JSX_(_ui_perfectScrollbar_jsx4__ .O, { + ref: ref => { + self.searchContactsScroll = ref; + }, + className: "contacts-search-scroll", + selected: this.state.selected, + changedHashProp: this.props.changedHashProp, + contacts, + frequentContacts, + searchValue: this.state.searchValue + }, JSX_(react1___default().Fragment, null, JSX_("div", { + className: "contacts-search-subsection", + style: { + display: hideFrequents ? 'none' : '' + } + }, JSX_("div", { + className: "contacts-list-header" + }, l[20141]), frequentsLoading ? JSX_("div", { + className: "loading-spinner" + }, "...") : JSX_("div", { + className: "contacts-search-list", + style: innerDivStyles + }, frequentContacts)), contacts.length > 0 ? JSX_("div", { + className: "contacts-search-subsection" + }, JSX_("div", { + className: "contacts-list-header" + }, frequentContacts && frequentContacts.length === 0 ? this.props.readOnly ? l[16217] : l[165] : l[165]), JSX_("div", { + className: "contacts-search-list", + style: innerDivStyles + }, contacts)) : null)); + } + } else if (self.props.newNoContact) { + multipleContacts = ""; + contactsList = JSX_("div", { + className: "chat-contactspicker-no-contacts flex flex-column flex-center mt-2" + }, JSX_("div", { + className: "section-icon sprite-fm-mono icon-contacts" + }), JSX_("div", { + className: "fm-empty-cloud-txt small" + }, l[784]), JSX_("div", { + className: "fm-empty-description small" + }, l[19115])); + extraClasses += " no-contacts"; + } else { + contactsList = JSX_("div", { + className: "chat-contactspicker-no-contacts flex flex-column flex-center mt-16" + }, JSX_("div", { + className: "section-icon sprite-fm-mono icon-contacts" + }), JSX_("div", { + className: "fm-empty-cloud-txt small" + }, l[784]), JSX_("div", { + className: "fm-empty-description small" + }, l[19115]), JSX_("button", { + className: "mega-button positive large fm-empty-button", + onClick: () => { + contactAddDialog(); + self.props.onClose == null || self.props.onClose(); + } + }, JSX_("span", null, l[101])), JSX_("div", { + className: ` + ${this.state.publicLink ? '' : 'loading'} + empty-share-public + ` + }, JSX_("i", { + className: "sprite-fm-mono icon-link-circle" + }), JSX_(_ui_utils_jsx3__ .P9, null, l[19111]))); + extraClasses += " no-contacts"; + } + const totalContactsNum = contacts.length + frequentContacts.length; + const searchPlaceholderMsg = mega.icu.format(l.search_contact_placeholder, totalContactsNum); + return JSX_("div", { + ref: this.domRef, + className: ` + ${this.props.className || ''} + ${extraClasses} + ` + }, this.props.topButtons && JSX_("div", { + className: "contacts-search-buttons" + }, this.props.topButtons.map(button => { const { - POSITION - } = this.DRAGGABLE; - return REaCt().createElement("div", { + key, + icon, + className, + title, + onClick + } = button || {}; + return JSX_("div", { + key, + className: "button-wrapper", + onClick: e => { + closeDropdowns(); + onClick(e); + } + }, JSX_(_ui_buttons_jsx5__ .$, { className: ` - ${FloatingVideo.NAMESPACE}-options - ${POSITION.left < 200 ? 'options-top' : ''} - ${POSITION.left < 200 && POSITION.top < 100 ? 'options-bottom' : ''} - theme-dark-forced - ` - }, REaCt().createElement("ul", null, REaCt().createElement("li", null, REaCt().createElement(meetings_button.A, { - icon: ` - sprite-fm-mono - ${IS_SPEAKER_VIEW ? 'grid-9' : 'grid-main'} - `, - onClick: () => this.setState({ - options: false + ${className || ''} + ${key === 'newChatLink' ? 'branded-blue' : ''} + mega-button + `, + icon, + label: title + })); + })), multipleContacts, !this.props.readOnly && haveContacts && !this.props.hideSearch && JSX_(react1___default().Fragment, null, JSX_("div", { + className: ` + contacts-search-header + ${this.props.headerClasses} + ` + }, JSX_("i", { + className: "sprite-fm-mono icon-preview-reveal" + }), JSX_("input", { + autoFocus: true, + type: "search", + placeholder: searchPlaceholderMsg, + ref: nodeRef => { + this.contactSearchField = nodeRef; + }, + onChange: this.onSearchChange, + value: this.state.searchValue + }), JSX_("div", { + className: ` + search-result-clear + ${this.state.searchValue && this.state.searchValue.length > 0 ? '' : 'hidden'} + `, + onClick: () => { + this.setState({ + searchValue: '' }, () => { - if (IS_SPEAKER_VIEW) { - return onModeChange(meetings_call.g.THUMBNAIL); - } - onSpeakerChange(call.getLocalStream()); - }) - }, REaCt().createElement("div", null, IS_SPEAKER_VIEW ? l.switch_to_thumb_view : l.display_in_main_view))), REaCt().createElement("li", null, REaCt().createElement(meetings_button.A, { - icon: "sprite-fm-mono icon-collapse-up", - onClick: onMoveIntoGrid - }, REaCt().createElement("div", null, l.move_into_grid_button))), REaCt().createElement("li", null, REaCt().createElement(meetings_button.A, { - icon: "sprite-fm-mono icon-download-standard", - onClick: () => this.setState({ - options: false - }, () => toggleCollapsedMode()) - }, REaCt().createElement("div", null, l.collapse_self_video)))), !!(call.av & SfuClient.Av.Screen) && REaCt().createElement("ul", { - className: "has-separator" - }, REaCt().createElement("li", null, REaCt().createElement(meetings_button.A, { - className: "end-screen-share", - icon: "icon-end-screenshare", - onClick: () => { - this.setState({ - options: false - }); - onScreenSharingClick(); - } - }, REaCt().createElement("div", null, l[22890]))))); - }; - this.renderMiniMode = source => { - const { - call, - chatRoom, - mode, - minimized, - isPresenterNode, - onLoadedData - } = this.props; - if (call.isOnHold) { - return this.renderOnHoldVideoNode(); + let _this$contactSearchFi; + return (_this$contactSearchFi = this.contactSearchField) == null ? void 0 : _this$contactSearchFi.focus(); + }); } - const VideoClass = source.isLocal ? isPresenterNode ? videoNode.Cn : videoNode.bJ : videoNode.zu; - return REaCt().createElement(VideoClass, { - key: source, - source, - chatRoom, - mode, - minimized, - isPresenterNode, - onLoadedData - }); - }; - this.renderSelfView = () => { - const { - isOnHold, - raisedHandPeers, - minimized, - chatRoom, - isPresenterNode, - call, - onLoadedData - } = this.props; - const { - options - } = this.state; - if (isOnHold) { - return this.renderOnHoldVideoNode(); - } - const VideoNode = call.isSharingScreen() ? videoNode.Vm : videoNode.bJ; - return REaCt().createElement(REaCt().Fragment, null, REaCt().createElement(VideoNode, { - isSelfOverlay: true, - raisedHandPeers, - minimized, - chatRoom, - isPresenterNode, - onLoadedData - }), REaCt().createElement("div", { - className: `${FloatingVideo.NAMESPACE}-self-overlay` - }, minimized ? null : REaCt().createElement(meetings_button.A, { - className: ` - mega-button - theme-light-forced - action - small - float-video-options-control - ${options ? 'active' : ''} - `, - icon: "sprite-fm-mono icon-options", - onClick: () => this.handleOptionsToggle() - }), options && this.renderOptionsDialog())); - }; - } - componentWillUnmount() { - super.componentWillUnmount(); - this.unbindEvents(); + }, JSX_("i", { + className: "sprite-fm-mono icon-close-component" + })))), this.props.inviteWarningLabel && this.props.chatRoom && this.renderInviteWarning(), !this.props.readOnly && haveContacts && !this.props.hideSearch && JSX_("div", { + className: "contacts-search-header-separator" + }), this.props.participantsList ? this.renderParticipantsList() : contactsList, selectFooter, this.props.showAddContact && (0,_contactsPanel_utils_jsx10__ .SN)() ? JSX_("div", { + className: "contacts-search-bottom" + }, JSX_(_ui_buttons_jsx5__ .$, { + className: "mega-button action positive", + icon: "sprite-fm-mono icon-add-circle", + label: l[71], + onClick: () => { + let _this$props$onAddCont, _this$props3; + contactAddDialog(); + closeDropdowns(); + (_this$props$onAddCont = (_this$props3 = this.props).onAddContact) == null || _this$props$onAddCont.call(_this$props3); + } + })) : null); } - componentDidUpdate(prevProps) { - super.componentDidUpdate(); - if (this.props.mode !== prevProps.mode) { - this.initDraggable(); - } - if (this.props.sidebar !== prevProps.sidebar && this.props.sidebar) { - this.repositionDraggable(); - } +} +ContactPickerWidget.defaultProps = { + multipleSelectedButtonLabel: false, + singleSelectedButtonLabel: false, + nothingSelectedButtonLabel: false, + allowEmpty: false, + disableFrequents: false, + skipMailSearch: false, + autoFocusSearchField: true, + selectCleanSearchRes: true, + disableDoubleClick: false, + newEmptySearchResult: false, + newNoContact: false, + emailTooltips: false +}; +class ContactPickerDialog extends _mixins2__ .w9 { + constructor(...args) { + super(...args); + this.dialogName = 'contact-picker-dialog'; } componentDidMount() { super.componentDidMount(); - this.bindEvents(); - this.initDraggable(); + M.safeShowDialog(this.dialogName, () => $(`.${this.dialogName}`)); + } + componentWillUnmount() { + super.componentWillUnmount(); + if ($.dialog === this.dialogName) { + closeDialog(); + } } render() { const { - NAMESPACE, - POSITION_MODIFIER - } = FloatingVideo; - const { - call, - mode, - minimized, - sidebar, - collapsed, - toggleCollapsedMode, - onCallExpand + active, + allowEmpty, + className, + exclude, + megaChat, + multiple, + multipleSelectedButtonLabel, + name, + nothingSelectedButtonLabel, + selectFooter, + singleSelectedButtonLabel, + inviteWarningLabel, + chatRoom, + onClose, + onSelectDone } = this.props; - const IS_MINI_MODE = mode === meetings_call.g.MINI; - if (collapsed) { - return REaCt().createElement("div", { - ref: this.domRef, - className: ` - ${NAMESPACE} - collapsed - theme-dark-forced - ${sidebar && !minimized ? POSITION_MODIFIER : ''} - `, - onClick: toggleCollapsedMode - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-arrow-up icon-collapse" - }), REaCt().createElement("div", { - className: "collapsed-audio-indicator" - }, REaCt().createElement(videoNode.Gz, { - source: call.getLocalStream() - }))); - } - const source = this.getStreamSource() || call.getLocalStream(); - return REaCt().createElement("div", { - ref: this.domRef, + return JSX_(_ui_modalDialogs7__ .A.ModalDialog, { + name, className: ` - ${NAMESPACE} - ${IS_MINI_MODE ? 'mini' : ''} - ${minimized ? 'minimized' : ''} - ${this.state.options ? 'active' : ''} - ${sidebar && !minimized ? POSITION_MODIFIER : ''} + ${className} + ${this.dialogName} + contacts-search `, - onClick: ({ - target - }) => minimized && target.classList.contains(`${NAMESPACE}-overlay`) && onCallExpand() - }, IS_MINI_MODE && this.renderMiniMode(source), !IS_MINI_MODE && this.renderSelfView(), minimized && REaCt().createElement(__Minimized, (0,esm_extends.A)({}, this.props, { - onOptionsToggle: this.handleOptionsToggle - }))); + onClose + }, JSX_(ContactPickerWidget, { + active, + allowEmpty, + className: "popup contacts-search small-footer", + contacts: M.u, + exclude, + megaChat, + multiple, + multipleSelectedButtonLabel, + nothingSelectedButtonLabel, + selectFooter, + singleSelectedButtonLabel, + inviteWarningLabel, + chatRoom, + onClose, + onSelectDone + })); } } -class Minimized extends mixins.w9 { - constructor(props) { - super(props); - this.domRef = REaCt().createRef(); - this.SIMPLETIP_PROPS = { - position: 'top', - offset: 5, - className: 'theme-dark-forced' - }; - this.waitingPeersListener = undefined; - this.raisedHandListener = undefined; - this.state = { - unread: 0, - waitingRoomPeers: [], - raisedHandPeers: [], - hideWrList: false, - hideHandsList: false - }; - this.isActive = type => { - return this.props.call.av & type; - }; - this.getUnread = () => { - const { - chatRoom - } = this.props; - chatRoom.rebind(Minimized.UNREAD_EVENT, () => this.setState({ - unread: chatRoom.getUnreadCount() - }, () => this.safeForceUpdate())); - }; - this.renderSignalWarning = () => this.props.signal ? null : this.props.renderSignalWarning(); - this.renderPermissionsWarning = type => { - const { - hasToRenderPermissionsWarning, - renderPermissionsWarning - } = this.props; - if (hasToRenderPermissionsWarning(type)) { - return renderPermissionsWarning(type, this); - } - return null; - }; - this.renderStreamControls = () => { - const { - call, - chatRoom, - recorderCid, - hasToRenderPermissionsWarning, - renderPermissionsWarning, - resetError, - onRecordingToggle, - onAudioClick, - onVideoClick, - onScreenSharingClick, - onHoldClick, - onCallEnd - } = this.props; - const audioLabel = this.isActive(SfuClient.Av.Audio) ? l[16214] : l[16708]; - const videoLabel = this.isActive(SfuClient.Av.Camera) ? l[22894] : l[22893]; - const LeaveButton = (0,hostsObserver.C)(({ - hasHost, - chatRoom, - confirmLeave, - onLeave - }) => { - return REaCt().createElement(meetings_button.A, { - simpletip: { - ...this.SIMPLETIP_PROPS, - label: l[5884] - }, - className: "mega-button theme-dark-forced round large end-call", - icon: "icon-phone-02", - onClick: ev => { - ev.stopPropagation(); - const callParticipants = chatRoom.getCallParticipants(); - const doLeave = () => !chatRoom.iAmOperator() || hasHost(chatRoom.call ? chatRoom.call.peers.map(a => a.userHandle) : []) || callParticipants.length === 1 ? onLeave() : confirmLeave({ - title: l.assign_host_leave_call, - body: l.assign_host_leave_call_details, - cta: l.assign_host_button, - altCta: l.leave_anyway - }); - return recorderCid && recorderCid === call.sfuClient.cid ? (0,streamControls.sX)(doLeave, onRecordingToggle) : doLeave(); - } - }, REaCt().createElement("span", null, l[5884])); - }); - return REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("div", { - className: `${FloatingVideo.NAMESPACE}-controls` - }, REaCt().createElement("div", { - className: "meetings-signal-container" - }, REaCt().createElement(meetings_button.A, { - simpletip: { - ...this.SIMPLETIP_PROPS, - label: audioLabel - }, - className: ` - mega-button - theme-light-forced - round - ${this.isActive(SfuClient.Av.onHold) ? 'disabled' : ''} - ${this.isActive(SfuClient.Av.Audio) ? '' : 'with-fill'} - `, - icon: this.isActive(SfuClient.Av.Audio) ? 'icon-mic-thin-outline' : 'icon-mic-off-thin-outline', - onClick: ev => { - ev.stopPropagation(); - resetError(Av.Audio); - onAudioClick(); - } - }, REaCt().createElement("span", null, audioLabel)), this.renderSignalWarning(), this.renderPermissionsWarning(Av.Audio)), REaCt().createElement("div", { - className: "meetings-signal-container" - }, REaCt().createElement(meetings_button.A, { - simpletip: { - ...this.SIMPLETIP_PROPS, - label: videoLabel - }, - className: ` - mega-button - theme-light-forced - round - ${this.isActive(SfuClient.Av.onHold) ? 'disabled' : ''} - ${this.isActive(SfuClient.Av.Camera) ? '' : 'with-fill'} - `, - icon: this.isActive(SfuClient.Av.Camera) ? 'icon-video-thin-outline' : 'icon-video-off-thin-outline', - onClick: ev => { - ev.stopPropagation(); - resetError(Av.Camera); - onVideoClick(); - } - }, REaCt().createElement("span", null, videoLabel)), this.renderPermissionsWarning(Av.Camera)), REaCt().createElement("div", { - className: "meetings-signal-container" - }, REaCt().createElement(FloatExtendedControls, { - call, - chatRoom, - onScreenSharingClick, - onHoldClick, - hasToRenderPermissionsWarning, - renderPermissionsWarning, - resetError, - showScreenDialog: !!this.props[`dialog-${Av.Screen}`] - }), this.renderPermissionsWarning(Av.Screen)), REaCt().createElement(LeaveButton, { - chatRoom, - participants: chatRoom.getCallParticipants(), - onLeave: onCallEnd, - onConfirmDenied: onCallEnd - })), REaCt().createElement("span", { - className: `${FloatingVideo.NAMESPACE}-fade` - })); - }; - this.renderPeersList = () => { - const { - onCallExpand, - onParticipantsToggle, - onWrListToggle - } = this.props; - const { - waitingRoomPeers, - raisedHandPeers, - hideHandsList, - hideWrList - } = this.state; - if (hideHandsList && hideWrList) { - return null; - } - const showRaised = hideHandsList || !hideWrList && waitingRoomPeers.length ? false : !!raisedHandPeers.length; - if (!showRaised && hideWrList) { - return null; - } - const showButton = !showRaised || showRaised && raisedHandPeers.length > 1; - return REaCt().createElement("div", { - className: ` - ${FloatingVideo.NAMESPACE}-alert - alert--waiting-peers - theme-dark-forced - `, - onClick: onCallExpand - }, REaCt().createElement(meetings_button.A, { - className: "close js-close", - icon: "sprite-fm-mono icon-dialog-close", - hideWrList, - hideHandsList, - onClick: ev => { - ev.stopPropagation(); - this.setState({ - hideHandsList: hideWrList || showRaised, - hideWrList: true - }); - } - }), REaCt().createElement("div", { - className: `alert-label ${showButton ? '' : 'label-only'}` - }, showRaised && REaCt().createElement("i", { - className: "sprite-fm-uni icon-raise-hand" - }), !hideWrList && !!waitingRoomPeers.length && mega.icu.format(l.wr_peers_waiting, waitingRoomPeers.length), showRaised && (raisedHandPeers.length > 1 ? raisedHandPeers.includes(u_handle) ? mega.icu.format(l.raise_self_peers_raised, raisedHandPeers.length - 1) : mega.icu.format(l.raise_peers_raised, raisedHandPeers.length) : REaCt().createElement(utils.P9, { - tag: "span", - content: raisedHandPeers[0] === u_handle ? l.raise_self_raised : l.raise_peer_raised.replace('%s', megaChat.html(M.getNameByHandle(raisedHandPeers[0]))) - }))), showButton && REaCt().createElement(meetings_button.A, { - className: "show-people", - label: showRaised ? l[16797] : l.wr_see_waiting, - onClick: ev => { - ev.stopPropagation(); - const promise = onCallExpand().catch(dump); - if (showRaised) { - promise.then(() => onParticipantsToggle(true)); - } else if (waitingRoomPeers.length > 1) { - promise.then(() => onWrListToggle(true)); - } - } - }, showRaised ? l[16797] : l.wr_see_waiting)); - }; - this.state.waitingRoomPeers = this.props.waitingRoomPeers || []; - this.state.raisedHandPeers = this.props.raisedHandPeers || []; + + }, + + 836 +(_, EXP_, REQ_) { + +"use strict"; + REQ_.d(EXP_, { + SN: () => hasContacts, + U_: () => resetCredentials, + X7: () => hasRelationship, + d_: () => LABEL, + gR: () => VIEW, + p4: () => isVerified, + qH: () => verifyCredentials, + qY: () => EVENTS, + ym: () => getUserFingerprint + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + +const EVENTS = { + KEYDOWN: 'keydown' +}; +const VIEW = { + CONTACTS: 0x00, + RECEIVED_REQUESTS: 0x01, + SENT_REQUESTS: 0x02, + PROFILE: 0x03 +}; +const LABEL = { + CONTACTS: l[165], + RECEIVED_REQUESTS: l[5863], + SENT_REQUESTS: l[5862] +}; +const hasContacts = () => M.u.some(contact => contact.c === 1); +const hasRelationship = contact => contact && contact.c === 1; +const isVerified = contact => { + if (contact && contact.u) { + const { + u: handle + } = contact; + const verificationState = u_authring.Ed25519[handle] || {}; + return verificationState.method >= authring.AUTHENTICATION_METHOD.FINGERPRINT_COMPARISON; + } + return null; +}; +const verifyCredentials = contact => { + if (contact.c === 1 && u_authring && u_authring.Ed25519) { + const verifyState = u_authring.Ed25519[contact.u] || {}; + if (typeof verifyState.method === "undefined" || verifyState.method < authring.AUTHENTICATION_METHOD.FINGERPRINT_COMPARISON) { + fingerprintDialog(contact.u); + } + } +}; +const resetCredentials = contact => { + if (M.isInvalidUserStatus()) { + return; } - componentDidMount() { - super.componentDidMount(); - this.getUnread(); - this.waitingPeersListener = mBroadcaster.addListener('meetings:peersWaiting', waitingRoomPeers => this.setState({ - waitingRoomPeers, - hideWrList: false, - hideHandsList: false - }, () => this.safeForceUpdate())); - this.raisedHandListener = mBroadcaster.addListener('meetings:raisedHand', raisedHandPeers => this.setState({ - raisedHandPeers, - hideWrList: false, - hideHandsList: false - }, () => this.safeForceUpdate())); - ['onCallPeerJoined', 'onCallPeerLeft'].map(event => this.props.chatRoom.rebind(`${event}.${Minimized.NAMESPACE}`, (ev, { - userHandle - }) => this.isMounted() && this.setState(state => ({ - raisedHandPeers: state.raisedHandPeers.includes(userHandle) ? state.raisedHandPeers.filter(h => h !== userHandle) : [...this.props.call.sfuClient.raisedHands] - }), this.safeForceUpdate))); + authring.resetFingerprintsForUser(contact.u).then(() => contact.trackDataChange()).catch(dump); +}; +const getUserFingerprint = handle => { + const $$FINGERPRINT = []; + userFingerprint(handle, fingerprints => { + for (let i = 0; i < fingerprints.length; i++) { + $$FINGERPRINT.push(JSX_("span", { + key: i + }, fingerprints[i])); + } + }); + return $$FINGERPRINT; +}; + + }, + + 4904 +(_, EXP_, REQ_) { + +"use strict"; + +// EXPORTS +REQ_.d(EXP_, { + qY: () => EVENTS, + Vw: () => VIEWS, + Ay: () => conversations +}); + +// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/extends.js +const esm_extends = REQ_(8168); +// EXTERNAL MODULE: external "React" +const external_React_ = REQ_(1594); +const REaCt = REQ_.n(external_React_); +// EXTERNAL MODULE: ./js/chat/mixins.js +const mixins = REQ_(8264); +// EXTERNAL MODULE: ./js/chat/ui/meetings/utils.jsx +const utils = REQ_(3901); +// EXTERNAL MODULE: ./js/ui/modalDialogs.jsx + 1 modules +const modalDialogs = REQ_(8120); +;// ./js/chat/ui/meetings/workflow/freeCallEnded.jsx + + +const NAMESPACE = 'free-call-ended-dlg'; +class FreeCallEnded extends REaCt().Component { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); } componentWillUnmount() { - super.componentWillUnmount(); - this.props.chatRoom.unbind(Minimized.UNREAD_EVENT); - [this.waitingPeersListener, this.raisedHandListener].map(listener => mBroadcaster.removeListener(listener)); - ['onCallPeerJoined', 'onCallPeerLeft'].map(event => this.props.chatRoom.off(`${event}.${Minimized.NAMESPACE}`)); + if ($.dialog === NAMESPACE) { + closeDialog(); + } + } + componentDidMount() { + M.safeShowDialog(NAMESPACE, () => { + if (!this.domRef.current) { + throw new Error(`${NAMESPACE} dialog: component ${NAMESPACE} not mounted.`); + } + eventlog(500295); + return $(`#${NAMESPACE}`); + }); } render() { const { - onCallExpand + onClose } = this.props; - const { - unread, - raisedHandPeers, - waitingRoomPeers - } = this.state; - return REaCt().createElement("div", { + return JSX_(modalDialogs.A.ModalDialog, { + id: NAMESPACE, ref: this.domRef, - className: `${FloatingVideo.NAMESPACE}-wrapper` - }, REaCt().createElement("div", { - className: `${FloatingVideo.NAMESPACE}-overlay` - }, REaCt().createElement(meetings_button.A, { - simpletip: { - ...this.SIMPLETIP_PROPS, - label: l.expand_mini_call - }, - className: "mega-button theme-light-forced action small expand", - icon: "sprite-fm-mono icon-fullscreen-enter", - onClick: ev => { - ev.stopPropagation(); - onCallExpand(); + className: "mega-dialog", + dialogType: "action", + dialogName: NAMESPACE, + onClose + }, JSX_("header", null, JSX_("div", { + className: "free-call-ended graphic" + }, JSX_("img", { + src: `${staticpath}images/mega/chat-upgrade-rocket.png` + }))), JSX_("section", { + className: "content" + }, JSX_("div", { + className: "content-block" + }, JSX_("div", { + className: "dialog-body-text" + }, JSX_("h3", null, l.free_call_ended_dlg_text), JSX_("span", null, l.free_call_ended_dlg_subtext)))), JSX_("footer", null, JSX_("div", { + className: "footer-container" + }, JSX_("button", { + className: "mega-button positive large", + onClick: () => { + loadSubPage('pro'); + eventlog(500261); + onClose(); } - }), this.renderStreamControls()), waitingRoomPeers && waitingRoomPeers.length || raisedHandPeers && raisedHandPeers.length ? this.renderPeersList() : null, unread ? REaCt().createElement("div", { - className: `${FloatingVideo.NAMESPACE}-notifications` - }, REaCt().createElement(meetings_button.A, { - className: "mega-button round large chat-control", - icon: "icon-chat-filled" - }, REaCt().createElement("span", null, l.chats)), REaCt().createElement("span", null, unread > 9 ? '9+' : unread)) : null); + }, JSX_("span", null, l.upgrade_now))))); } } -Minimized.NAMESPACE = 'float-video-minimized'; -Minimized.UNREAD_EVENT = 'onUnreadCountUpdate.localStreamNotifications'; -const __Minimized = (0,mixins.Zz)(micObserver.Q, permissionsObserver.$)(Minimized); -// EXTERNAL MODULE: ./js/chat/ui/inviteParticipantsPanel.jsx -const inviteParticipantsPanel = REQ_(815); -;// ./js/chat/ui/meetings/participantsNotice.jsx - - - - +// EXTERNAL MODULE: ./js/chat/ui/contactsPanel/utils.jsx +const contactsPanel_utils = REQ_(836); +// EXTERNAL MODULE: ./js/chat/ui/leftPanel/utils.jsx +const leftPanel_utils = REQ_(4664); +// EXTERNAL MODULE: ./js/chat/ui/link.jsx +const ui_link = REQ_(4649); +;// ./js/chat/ui/errorBoundary.jsx -class ParticipantsNotice extends mixins.w9 { - constructor(props) { - super(props); - this.domRef = REaCt().createRef(); - this.renderUserAlone = () => REaCt().createElement("div", { - className: ` - ${ParticipantsNotice.NAMESPACE} - theme-dark-forced - user-alone - ` - }, this.props.stayOnEnd ? REaCt().createElement("div", { - className: `${ParticipantsNotice.NAMESPACE}-heading` - }, REaCt().createElement("h1", null, this.props.everHadPeers ? l.only_one_here : l.waiting_for_others)) : REaCt().createElement("div", { - className: `${ParticipantsNotice.NAMESPACE}-content user-alone` - }, REaCt().createElement("h3", null, l.only_one_here), REaCt().createElement("p", { - className: "theme-dark-forced" - }, REaCt().createElement(utils.P9, null, l.empty_call_dlg_text.replace('%s', '2'))), REaCt().createElement("div", { - className: "notice-footer" - }, REaCt().createElement(meetings_button.A, { - className: "mega-button large stay-on-call", - onClick: this.props.onStayConfirm - }, REaCt().createElement("span", null, l.empty_call_stay_button)), REaCt().createElement(meetings_button.A, { - className: "mega-button positive large stay-on-call", - onClick: this.props.onCallEnd - }, REaCt().createElement("span", null, l.empty_call_dlg_end))))); - this.renderUserWaiting = () => { - const { - chatRoom, - onInviteToggle - } = this.props; - return REaCt().createElement("div", { - className: ` - ${ParticipantsNotice.NAMESPACE} - ${chatRoom.isMeeting ? '' : 'user-alone'} - theme-dark-forced - ` - }, REaCt().createElement("div", { - className: `${ParticipantsNotice.NAMESPACE}-heading` - }, chatRoom.type === 'private' ? REaCt().createElement("h1", null, REaCt().createElement(utils.zT, null, l.waiting_for_peer.replace('%NAME', chatRoom.getRoomTitle()))) : REaCt().createElement("h1", null, l.waiting_for_others)), chatRoom.isMeeting && chatRoom.publicLink && REaCt().createElement("div", { - className: `${ParticipantsNotice.NAMESPACE}-content-invite` - }, REaCt().createElement(inviteParticipantsPanel.Q, { - chatRoom, - disableLinkToggle: true, - onAddParticipants: () => { - this.setState({ - inviteDialog: false - }, () => onInviteToggle()); - } - }))); +class ErrorBoundary extends REaCt().Component { + constructor(...args) { + super(...args); + this.state = { + hasError: false, + error: null }; - this.av = this.props.call.sfuClient.availAv; + this.handleRetry = () => this.setState({ + hasError: false, + error: null + }); } - specShouldComponentUpdate(newProps) { - const { - stayOnEnd, - hasLeft, - isOnHold, - call - } = this.props; - const currAv = this.av; - this.av = call.sfuClient.availAv; - return newProps.stayOnEnd !== stayOnEnd || newProps.hasLeft !== hasLeft || newProps.isOnHold !== isOnHold || this.av !== currAv; + static getDerivedStateFromError(error) { + return { + hasError: true, + error + }; + } + componentDidCatch(error, errorInfo) { + console.error(error, errorInfo); } render() { const { - call, - hasLeft, - streamContainer, - chatRoom - } = this.props; - if (call.isDestroyed) { - return null; + hasError, + error + } = this.state; + if (hasError) { + return JSX_("div", { + className: "meetings-error" + }, JSX_("div", { + className: "meetings-error--content" + }, JSX_("i", { + className: ` + sprite-fm-illustration-wide + ${mega.ui.isDarkTheme() ? 'mega-logo-dark' : 'img-mega-logo-light'} + ` + }), JSX_("h1", null, l[200]), JSX_("span", null, "Please ", JSX_(ui_link.A, { + onClick: this.handleRetry + }, "try again"), " or\xA0", JSX_(ui_link.A, { + onClick: () => location.reload() + }, "reload the page"), "."), d && JSX_("div", { + className: "meetings-error--details" + }, error.toString()))); } - return REaCt().createElement("div", { - ref: this.domRef, - className: `${ParticipantsNotice.NAMESPACE}-container` - }, call.isSharingScreen() ? null : REaCt().createElement(videoNode.Cn, { - className: "local-stream-mirrored", - chatRoom, - source: call.getLocalStream() - }), streamContainer(hasLeft ? this.renderUserAlone() : this.renderUserWaiting())); + return this.props.children; } } -ParticipantsNotice.NAMESPACE = 'participants-notice'; -// EXTERNAL MODULE: ./js/chat/ui/chatToaster.jsx -const chatToaster = REQ_(424); -;// ./js/chat/ui/meetings/participantsBlock.jsx +// EXTERNAL MODULE: ./js/chat/ui/fallback.jsx +const fallback = REQ_(3439); +;// ./js/chat/ui/conversations.jsx + + -const MAX_STREAMS_PER_PAGE = 10; -const SIMPLE_TIP = { - position: 'top', - offset: 5, - className: 'theme-dark-forced' + +const LeftPanel = (0,external_React_.lazy)(() => REQ_.e( 493).then(REQ_.bind(REQ_, 4907))); +const EmptyConversationsPanel = (0,external_React_.lazy)(() => REQ_.e( 493).then(REQ_.bind(REQ_, 8596))); +const ChatToaster = (0,external_React_.lazy)(() => REQ_.e( 493).then(REQ_.bind(REQ_, 8491))); +const ConversationPanels = (0,external_React_.lazy)(() => REQ_.e( 493).then(REQ_.bind(REQ_, 5677)).then(m => ({ + default: m.ConversationPanels +}))); +const ContactsPanel = (0,external_React_.lazy)(() => REQ_.e( 253).then(REQ_.bind(REQ_, 5392))); +const ScheduleMeetingDialog = (0,external_React_.lazy)(() => REQ_.e( 716).then(REQ_.bind(REQ_, 8389))); +const ScheduleOccurrenceDialog = (0,external_React_.lazy)(() => REQ_.e( 716).then(REQ_.bind(REQ_, 4156))); +const ContactSelectorDialog = (0,external_React_.lazy)(() => REQ_.e( 543).then(REQ_.bind(REQ_, 2678))); +const StartGroupChatWizard = (0,external_React_.lazy)(() => REQ_.e( 543).then(REQ_.bind(REQ_, 5199))); +const StartMeetingDialog = (0,external_React_.lazy)(() => REQ_.e( 543).then(REQ_.bind(REQ_, 7190))); +const VIEWS = { + CHATS: 0x00, + MEETINGS: 0x01, + LOADING: 0x02 }; -class ParticipantsBlock extends mixins.w9 { - constructor(...args) { - super(...args); +const EVENTS = { + NAV_RENDER_VIEW: 'navRenderView' +}; +window.convAppConstants = { + VIEWS, + EVENTS +}; +class ConversationsApp extends mixins.w9 { + constructor(props) { + super(props); this.domRef = REaCt().createRef(); - this.nodeMenuRef = REaCt().createRef(); - this.dupNodeMenuRef = REaCt().createRef(); + this.chatRoomRef = null; + this.occurrenceRef = null; this.state = { - page: 0 + startGroupChatDialog: false, + startMeetingDialog: false, + scheduleMeetingDialog: false, + scheduleOccurrenceDialog: false, + freeCallEndedDialog: false, + contactSelectorDialog: false, + view: VIEWS.LOADING, + callExpanded: false, + ipcData: null }; - this.movePage = direction => this.setState(state => ({ - page: direction === PAGINATION.NEXT ? state.page + 1 : state.page - 1 - })); - this.renderLocalNode = isPresenterNode => { + this._cacheRouting(); + megaChat.rebind('onStartNewMeeting.convApp', () => this.startMeeting()); + } + startMeeting() { + if (megaChat.hasSupportForCalls) { + return (0,utils.dQ)().then(() => this.setState({ + startMeetingDialog: true + })).catch(() => d && console.warn('Already in a call.')); + } + return showToast('warning', l[7211]); + } + _cacheRouting() { + this.routingSection = this.props.megaChat.routingSection; + this.routingSubSection = this.props.megaChat.routingSubSection; + this.routingParams = this.props.megaChat.routingParams; + } + hasOpenDialog() { + return [...document.querySelectorAll('.mega-dialog')].some(dialog => !!(dialog.offsetParent || dialog.offsetWidth || dialog.offsetHeight)); + } + specShouldComponentUpdate() { + if (this.routingSection !== this.props.megaChat.routingSection || this.routingSubSection !== this.props.megaChat.routingSubSection || this.routingParams !== this.props.megaChat.routingParams) { + this._cacheRouting(); + return true; + } + } + componentDidMount() { + super.componentDidMount(); + $(document).rebind('keydown.megaChatTextAreaFocus', e => { + if (!M.chat || e.megaChatHandled) { + return; + } const { - call, - peers, - mode, - raisedHandPeers, - chatRoom, - forcedLocal, - presenterThumbSelected, - onSeparate, - onSpeakerChange, - onModeChange - } = this.props; - const localStream = call.getLocalStream(); - if (localStream) { - const IS_SPEAKER_VIEW = mode === meetings_call.g.MAIN && forcedLocal; - const VideoClass = isPresenterNode ? videoNode.Vm : videoNode.bJ; - let isActive = false; - if (isPresenterNode) { - isActive = forcedLocal && !presenterThumbSelected; - } else if (call.pinnedCid === 0 || forcedLocal) { - if (presenterThumbSelected) { - isActive = !isPresenterNode; - } else if (localStream.hasScreen) { - isActive = isPresenterNode; - } else { - isActive = true; - } + currentlyOpenedChat + } = megaChat; + const currentRoom = megaChat.getCurrentRoom(); + if (currentlyOpenedChat) { + if (currentRoom && currentRoom.isReadOnly() || $(e.target).is(".messages-textarea, input, textarea") || (e.ctrlKey || e.metaKey || e.which === 19) && e.keyCode === 67 || e.keyCode === 91 || e.keyCode === 17 || e.keyCode === 27 || e.altKey || e.metaKey || e.ctrlKey || e.shiftKey || this.hasOpenDialog() || document.querySelector('textarea:focus,select:focus,input:focus')) { + return; } - return REaCt().createElement(VideoClass, { - key: `${u_handle}${isPresenterNode ? '_block' : ''}`, - className: ` - local-stream-node - ${call.isSharingScreen() ? '' : 'local-stream-mirrored'} - ${isActive ? 'active' : ''} - ${call.speakerCid === 0 ? 'active-speaker' : ''} - `, - simpletip: { - ...SIMPLE_TIP, - label: l[8885] - }, - mode, - raisedHandPeers, - chatRoom, - source: localStream, - localAudioMuted: !(call.av & SfuClient.Av.Audio), - isPresenterNode, - onClick: (source, ev) => { - const nodeMenuRef = isPresenterNode ? this.nodeMenuRef && this.nodeMenuRef.current : this.dupNodeMenuRef && this.dupNodeMenuRef.current; - if (nodeMenuRef && nodeMenuRef.contains(ev.target)) { - ev.preventDefault(); - ev.stopPropagation(); - return; - } - return onSpeakerChange(localStream, !isPresenterNode); - } - }, (peers == null ? void 0 : peers.length) && REaCt().createElement("div", { - ref: isPresenterNode ? this.nodeMenuRef : this.dupNodeMenuRef, - className: "node-menu theme-dark-forced" - }, REaCt().createElement("div", { - className: "node-menu-toggle" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-more-horizontal-thin-outline" - })), REaCt().createElement("div", { - className: "node-menu-content" - }, REaCt().createElement("ul", null, REaCt().createElement("li", null, REaCt().createElement(meetings_button.A, { - icon: ` - sprite-fm-mono - ${IS_SPEAKER_VIEW ? 'grid-9' : 'grid-main'} - `, - onClick: () => { - if (IS_SPEAKER_VIEW) { - return onModeChange(meetings_call.g.THUMBNAIL); - } - return onSpeakerChange(localStream); - } - }, REaCt().createElement("span", null, IS_SPEAKER_VIEW ? l.switch_to_thumb_view : l.display_in_main_view))), REaCt().createElement("li", null, REaCt().createElement(meetings_button.A, { - icon: "sprite-fm-mono grid-separate", - onClick: onSeparate - }, REaCt().createElement("span", null, l.separate_from_grid_button))))))); + const $typeArea = $('.messages-textarea:visible:first'); + moveCursortoToEnd($typeArea); + e.megaChatHandled = true; + $typeArea.triggerHandler(e); + e.preventDefault(); + e.stopPropagation(); + return false; } - return null; - }; - this.onScroll = (chunks, evt) => { - const { - page - } = this.state; - if (evt.deltaY < 0) { - if (page > 0) { - this.movePage(PAGINATION.PREV); + }); + $(document).rebind('mouseup.megaChatTextAreaFocus', e => { + if (!M.chat || e.megaChatHandled || slideshowid) { + return; + } + const $target = $(e.target); + if (megaChat.currentlyOpenedChat) { + if ($target.is(".messages-textarea,a,input,textarea,select,button") || $target.is('i') && $target.parent().is('a,input,select,button') || $target.closest('.messages.scroll-area').length > 0 || $target.closest('.mega-dialog').length > 0 || this.hasOpenDialog() || document.querySelector('textarea:focus,select:focus,input:focus') || window.getSelection().toString()) { + return; } - } else if (evt.deltaY > 0) { - if (page < Object.values(chunks).length - 1) { - this.movePage(PAGINATION.NEXT); + const $typeArea = $('.messages-textarea:visible:first'); + if ($typeArea.length === 1 && !$typeArea.is(":focus")) { + $typeArea.trigger("focus"); + e.megaChatHandled = true; } } - }; + }); + megaChat.rebind(megaChat.plugins.meetingsManager.EVENTS.EDIT, (ev, chatOrOccurrence) => { + if (chatOrOccurrence instanceof ChatRoom || !chatOrOccurrence) { + this.chatRoomRef = chatOrOccurrence; + this.setState({ + scheduleMeetingDialog: true + }); + } else { + this.occurrenceRef = chatOrOccurrence; + this.setState({ + scheduleOccurrenceDialog: true + }); + } + }); + megaChat.rebind(EVENTS.NAV_RENDER_VIEW, ({ + data + }) => { + if (Object.values(VIEWS).includes(data)) { + this.renderView(data); + } + }); + megaChat.rebind('onCallTimeLimitExceeded', () => { + this.setState({ + freeCallEndedDialog: true + }); + }); + if (megaChat.WITH_SELF_NOTE && !megaChat.getNoteChat() && !is_chatlink) { + api.req({ + a: 'mcc', + u: [], + m: 0, + g: 0, + v: Chatd.VERSION + }).catch(dump); + } + this.requestReceivedListener = mBroadcaster.addListener('fmViewUpdate:ipc', () => { + this.setState({ + ipcData: this.makeIpcData() + }); + }); + this.setState({ + ipcData: this.makeIpcData() + }); + } + componentWillUnmount() { + super.componentWillUnmount(); + $(document).off('keydown.megaChatTextAreaFocus'); + mBroadcaster.removeListener('fmViewUpdate:ipc', this.requestReceivedListener); } - shouldComponentUpdate() { + componentDidUpdate(prevProps, prevState) { + this.handleOnboardingStep(); const { - peers - } = this.props; - return peers && peers.length; - } - render() { + names: prevNames + } = prevState.ipcData; + const newIpcData = this.makeIpcData(); const { - call, - mode, - peers, - floatDetached, - chatRoom, - raisedHandPeers, - presenterThumbSelected, - onSpeakerChange - } = this.props; - if (peers && peers.length) { - const { - screen, - video, - rest - } = filterAndSplitSources(peers, call); - const sources = [...screen, ...video, ...rest]; - const $$PEER = (peer, i) => { - const { - clientId, - userHandle, - hasScreenAndCam, - hasScreen, - isLocal - } = peer; - if (screen.length && (screen[0].clientId === clientId || screen[0].isLocal && isLocal)) { - screen.shift(); - } - if (!(peer instanceof CallManager2.Peer)) { - const isPresenterNode = screen.length && screen[0].isLocal; - if (floatDetached && !isPresenterNode) { - return; - } - return this.renderLocalNode(!floatDetached && isPresenterNode); - } - const presenterCid = screen.length && screen[0].clientId === clientId; - let PeerClass; - if (hasScreenAndCam) { - PeerClass = presenterCid ? videoNode.ob : videoNode.Qs; - } else { - PeerClass = videoNode.au; - } - assert(!presenterCid || hasScreen); - const isActiveSpeaker = !peer.audioMuted && call.speakerCid === peer.clientId; - let isActive = false; - if (call.pinnedCid === clientId) { - if (presenterThumbSelected) { - isActive = !presenterCid; - } else if (hasScreen) { - isActive = presenterCid; - } else { - isActive = true; - } - } - const name = M.getNameByHandle(userHandle); - let label = name; - if (presenterCid) { - label = name ? l.presenter_nail.replace('%s', name) : megaChat.plugins.userHelper.SIMPLETIP_USER_LOADER; - } else { - label = name || megaChat.plugins.userHelper.SIMPLETIP_USER_LOADER; - } - return REaCt().createElement(PeerClass, { - key: `${userHandle}-${i}-${clientId}`, - className: ` - video-crop - ${isActive ? 'active' : ''} - ${isActiveSpeaker ? 'active-speaker' : ''} - `, - simpletip: { - ...SIMPLE_TIP, - label - }, - raisedHandPeers, - mode, - chatRoom, - source: peer, - isPresenterNode: !!presenterCid, - onSpeakerChange: node => onSpeakerChange(node, !presenterCid), - onClick: node => onSpeakerChange(node, !presenterCid) - }); - }; - if (sources.length <= (floatDetached ? MAX_STREAMS_PER_PAGE : 9)) { - return REaCt().createElement("div", { - ref: this.domRef, - className: "stream-participants-block theme-dark-forced" - }, REaCt().createElement("div", { - className: "participants-container" - }, REaCt().createElement("div", { - className: ` - participants-grid - ${floatDetached && sources.length === 1 || sources.length === 0 ? 'single-column' : ''} - ` - }, sources.map((p, i) => $$PEER(p, i))))); + names: newNames + } = newIpcData; + if (newNames.size !== prevNames.size) { + this.setState({ + ipcData: newIpcData + }); + return; + } + let different = false; + for (const [email, name] of newNames) { + if (!prevNames.has(email) || prevNames.get(email) !== name) { + different = true; + break; } - const { - page - } = this.state; - const chunks = chunkNodes(sources, MAX_STREAMS_PER_PAGE); - return REaCt().createElement("div", { - ref: this.domRef, - className: "carousel" - }, REaCt().createElement("div", { - className: "carousel-container", - onWheel: evt => this.onScroll(chunks, evt) - }, REaCt().createElement("div", { - className: "stream-participants-block theme-dark-forced" - }, REaCt().createElement("div", { - className: "participants-container" - }, Object.values(chunks).map((chunk, i) => { - const { - id, - nodes - } = chunk; - return REaCt().createElement("div", { - key: id, - className: ` - carousel-page - ${i === page ? 'active' : ''} - ` - }, page === 0 ? null : REaCt().createElement("button", { - className: "carousel-control carousel-button-prev theme-dark-forced", - onClick: () => this.movePage(PAGINATION.PREV) - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-arrow-up" - })), REaCt().createElement("div", { - className: ` - participants-grid - ${nodes.length === 1 ? 'single-column' : ''} - ` - }, nodes.map((peer, j) => $$PEER(peer, j + i * MAX_STREAMS_PER_PAGE))), page >= Object.values(chunks).length - 1 ? null : REaCt().createElement("button", { - className: "carousel-control carousel-button-next theme-dark-forced", - onClick: () => this.movePage(PAGINATION.NEXT) - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-arrow-down" - }))); - }))))); } - return null; + if (different) { + this.setState({ + ipcData: newIpcData + }); + } } -} -// EXTERNAL MODULE: ./js/chat/ui/meetings/videoNodeMenu.jsx -const videoNodeMenu = REQ_(539); -// EXTERNAL MODULE: ./js/ui/modalDialogs.jsx + 1 modules -const modalDialogs = REQ_(318); -;// ./js/chat/ui/meetings/modeSwitch.jsx - - - - - -class ModeSwitch extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - this.state = { - expanded: false, - settings: false - }; - this.handleMousedown = ({ - target - }) => { - if (this.state.expanded || this.state.settings) { - let _this$domRef; - return (_this$domRef = this.domRef) != null && (_this$domRef = _this$domRef.current) != null && _this$domRef.contains(target) ? null : this.doClose(); + handleOnboardingStep() { + if (this.state.view === VIEWS.LOADING) { + return; + } + megaChat.plugins.chatOnboarding.checkAndShowStep(); + } + renderView(view) { + this.setState({ + view + }, () => { + const { + $chatTreePanePs, + routingSection, + currentlyOpenedChat + } = megaChat; + Object.values($chatTreePanePs).forEach(ref => ref.reinitialise == null ? void 0 : ref.reinitialise()); + if (routingSection !== 'chat') { + loadSubPage('fm/chat'); } - }; - this.handleKeydown = ({ - keyCode - }) => keyCode && keyCode === 27 && this.doClose(); - this.doClose = () => this.isMounted() && this.setState({ - expanded: false, - settings: false - }, () => this.props.setActiveElement(this.state.expanded)); - this.doToggle = () => this.isMounted() && this.setState(state => ({ - expanded: !state.expanded - }), () => this.props.setActiveElement(this.state.expanded || this.state.settings)); - this.setStreamsPerPage = streamsPerPage => { - if (streamsPerPage) { - let _this$props$onStreams, _this$props; - (_this$props$onStreams = (_this$props = this.props).onStreamsPerPageChange) == null || _this$props$onStreams.call(_this$props, streamsPerPage); - this.doClose(); + megaChat.currentlyOpenedView = view; + if (!currentlyOpenedChat) { + megaChat.renderListing(null, false).catch(dump); } - }; - this.getModeIcon = mode => { - switch (mode) { - case meetings_call.g.THUMBNAIL: - return 'grid-9'; - case meetings_call.g.MAIN: - return 'grid-main'; - default: - return null; + }); + } + makeIpcData() { + let mixed = false; + const names = new Map(); + const data = Object.values(M.ipc).reduce((acc, curr) => { + const name = M.getNameByEmail(curr.m); + if (name !== curr.m) { + names.set(curr.m, name); + mixed = true; } - }; - this.Toggle = () => { - const { - mode - } = this.props; - return REaCt().createElement("div", { - className: `${ModeSwitch.BASE_CLASS}-toggle`, - onClick: this.doToggle - }, REaCt().createElement(meetings_button.A, null, REaCt().createElement("i", { - className: `sprite-fm-mono ${this.getModeIcon(mode)}` - }), mode === meetings_call.g.THUMBNAIL && REaCt().createElement("div", null, l.thumbnail_view), mode === meetings_call.g.MAIN && REaCt().createElement("div", null, l.main_view)), REaCt().createElement("i", { - className: "sprite-fm-mono icon-arrow-down" - })); - }; - this.Option = ({ - label, - mode - }) => { - return REaCt().createElement("div", { - className: ` - ${ModeSwitch.BASE_CLASS}-option - ${mode === this.props.mode ? 'active' : ''} - `, - onClick: () => { - this.doToggle(); - this.props.onModeChange(mode); - } - }, REaCt().createElement(meetings_button.A, null, REaCt().createElement("i", { - className: `sprite-fm-mono ${this.getModeIcon(mode)}` - }), REaCt().createElement("div", null, label))); - }; - this.Settings = () => { - const { - streamsPerPage - } = this.props; - return REaCt().createElement("div", { - className: `${ModeSwitch.BASE_CLASS}-settings` - }, REaCt().createElement("div", { - className: "settings-wrapper" - }, REaCt().createElement("strong", null, l.layout_settings_heading), REaCt().createElement("span", null, l.layout_settings_info), REaCt().createElement("div", { - className: "recurring-radio-buttons" - }, REaCt().createElement("div", { - className: "recurring-label-wrap" - }, REaCt().createElement("div", { - className: ` - uiTheme - ${streamsPerPage === STREAMS_PER_PAGE.MIN ? 'radioOn' : 'radioOff'} - ` - }, REaCt().createElement("input", { - type: "radio", - name: "9", - onClick: () => this.setStreamsPerPage(STREAMS_PER_PAGE.MIN) - })), REaCt().createElement("div", { - className: "radio-txt" - }, REaCt().createElement("span", { - className: "recurring-radio-label", - onClick: () => this.setStreamsPerPage(STREAMS_PER_PAGE.MIN) - }, "9"))), REaCt().createElement("div", { - className: "recurring-label-wrap" - }, REaCt().createElement("div", { - className: ` - uiTheme - ${streamsPerPage === STREAMS_PER_PAGE.MED ? 'radioOn' : 'radioOff'} - ` - }, REaCt().createElement("input", { - type: "radio", - name: "21", - onClick: () => { - this.setStreamsPerPage(STREAMS_PER_PAGE.MED); - } - })), REaCt().createElement("div", { - className: "radio-txt" - }, REaCt().createElement("span", { - className: "recurring-radio-label", - onClick: () => this.setStreamsPerPage(STREAMS_PER_PAGE.MED) - }, "21"))), REaCt().createElement("div", { - className: "recurring-label-wrap" - }, REaCt().createElement("div", { - className: ` - uiTheme - ${streamsPerPage === STREAMS_PER_PAGE.MAX ? 'radioOn' : 'radioOff'} - ` - }, REaCt().createElement("input", { - type: "radio", - name: "49", - onClick: () => { - this.setStreamsPerPage(STREAMS_PER_PAGE.MAX); + return { + ...acc, + [curr.p]: { + ...curr, + name } - })), REaCt().createElement("div", { - className: "radio-txt" - }, REaCt().createElement("span", { - className: "recurring-radio-label", - onClick: () => this.setStreamsPerPage(STREAMS_PER_PAGE.MAX) - }, "49")))), REaCt().createElement("small", null, l.layout_settings_warning)), REaCt().createElement("div", { - className: "settings-close" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-dialog-close", - onClick: this.doClose - }))); + }; + }, Object.create(null)); + return { + mixed, + data, + names }; } - componentWillUnmount() { - super.componentWillUnmount(); - document.removeEventListener('mousedown', this.handleMousedown); - document.removeEventListener('keydown', this.handleKeydown); - } - componentDidMount() { - super.componentDidMount(); - document.addEventListener('mousedown', this.handleMousedown); - document.addEventListener('keydown', this.handleKeydown); - } render() { const { - Toggle, - Option, - Settings, - domRef, - state, - doToggle - } = this; - return REaCt().createElement("div", { - ref: domRef, - className: ModeSwitch.BASE_CLASS - }, REaCt().createElement(Toggle, null), REaCt().createElement("div", { + CHATS, + MEETINGS + } = VIEWS; + const { + routingSection, + chatUIFlags, + currentlyOpenedChat, + chats + } = megaChat; + const { + view, + startGroupChatDialog, + startMeetingDialog, + scheduleMeetingDialog, + scheduleOccurrenceDialog, + callExpanded, + freeCallEndedDialog, + contactSelectorDialog + } = this.state; + const isEmpty = chats && routingSection === 'chat' && !currentlyOpenedChat && !is_chatlink; + const isLoading = !currentlyOpenedChat && megaChat.allChatsHadInitialLoadedHistory() === false && routingSection !== 'contacts'; + const rightPane = JSX_("div", { className: ` - ${ModeSwitch.BASE_CLASS}-menu - ${state.expanded ? 'expanded' : ''} - ` - }, REaCt().createElement(Option, { - label: l.main_view, - mode: meetings_call.g.MAIN - }), REaCt().createElement(Option, { - label: l.thumbnail_view, - mode: meetings_call.g.THUMBNAIL - }), REaCt().createElement("div", { - className: `${ModeSwitch.BASE_CLASS}-option`, - onClick: () => this.setState({ - settings: true - }, doToggle) - }, REaCt().createElement(meetings_button.A, null, REaCt().createElement("i", { - className: "sprite-fm-mono icon-settings" - }), REaCt().createElement("div", null, l.layout_settings_button)))), state.settings && REaCt().createElement(Settings, null)); + fm-right-files-block + in-chat + ${is_chatlink ? 'chatlink' : ''} + ` + }, JSX_(external_React_.Suspense, { + fallback: JSX_(fallback.A, null) + }, !isLoading && JSX_(ChatToaster, { + isRootToaster: true + }), !isLoading && routingSection === 'contacts' && JSX_(ContactsPanel, { + megaChat, + contacts: M.u, + received: this.state.ipcData, + sent: M.opc + }), !isLoading && JSX_(ConversationPanels, (0,esm_extends.A)({}, this.props, { + className: routingSection === 'chat' ? '' : 'hidden', + routingSection, + currentlyOpenedChat, + isEmpty, + chatUIFlags, + onToggleExpandedFlag: () => this.setState(() => ({ + callExpanded: (0,utils.Av)() + })), + onMount: () => { + const chatRoom = megaChat.getCurrentRoom(); + const view = chatRoom && chatRoom.isMeeting ? MEETINGS : CHATS; + this.setState({ + view + }, () => { + megaChat.currentlyOpenedView = view; + }); + } + })), !isLoading && isEmpty && JSX_(EmptyConversationsPanel, { + isMeeting: view === MEETINGS, + onNewChat: () => this.setState({ + contactSelectorDialog: true + }), + onStartMeeting: () => this.startMeeting(), + onScheduleMeeting: () => this.setState({ + scheduleMeetingDialog: true + }) + })), !isLoading && routingSection === 'notFound' && JSX_("span", null, JSX_("center", null, "Section not found"))); + const noteChat = megaChat.getNoteChat(); + return JSX_(ErrorBoundary, null, JSX_("div", { + ref: this.domRef, + className: "conversationsApp" + }, JSX_(external_React_.Suspense, { + fallback: JSX_(fallback.A, null) + }, startMeetingDialog && JSX_(StartMeetingDialog, { + onStart: (topic, audio, video) => { + megaChat.createAndStartMeeting(topic, audio, video); + this.setState({ + startMeetingDialog: false + }); + }, + onClose: () => this.setState({ + startMeetingDialog: false + }) + }), startGroupChatDialog && JSX_(StartGroupChatWizard, { + name: "start-group-chat", + flowType: 1, + onClose: () => this.setState({ + startGroupChatDialog: false + }), + onConfirmClicked: () => this.setState({ + startGroupChatDialog: false + }) + }), scheduleMeetingDialog && JSX_(ScheduleMeetingDialog, { + chatRoom: this.chatRoomRef, + callExpanded, + onClose: () => { + this.setState({ + scheduleMeetingDialog: false + }, () => { + this.chatRoomRef = null; + }); + } + }), scheduleOccurrenceDialog && JSX_(ScheduleOccurrenceDialog, { + chatRoom: this.occurrenceRef.scheduledMeeting.chatRoom, + scheduledMeeting: this.occurrenceRef.scheduledMeeting, + occurrenceId: this.occurrenceRef.uid, + callExpanded, + onClose: () => { + this.setState({ + scheduleOccurrenceDialog: false + }, () => { + this.occurrenceRef = null; + }); + } + }), contactSelectorDialog && JSX_(ContactSelectorDialog, { + className: `main-start-chat-dropdown ${leftPanel_utils.C}-contact-selector`, + multiple: false, + topButtons: [{ + key: 'newGroupChat', + title: l[19483], + className: 'positive', + onClick: () => this.setState({ + startGroupChatDialog: true, + contactSelectorDialog: false + }) + }, ...megaChat.WITH_SELF_NOTE ? (0,contactsPanel_utils.SN)() || noteChat && noteChat.hasMessages() ? [] : [{ + key: 'noteChat', + title: l.note_label, + icon: 'sprite-fm-mono icon-file-text-thin-outline note-chat-icon', + onClick: () => { + closeDialog(); + loadSubPage(`fm/chat/p/${u_handle}`); + } + }] : []], + showAddContact: (0,contactsPanel_utils.SN)(), + onClose: () => this.setState({ + contactSelectorDialog: false + }), + onSelectDone: selected => { + if (selected.length === 1) { + return megaChat.createAndShowPrivateRoom(selected[0]).then(room => room.setActive()); + } + megaChat.createAndShowGroupRoomFor(selected); + } + })), JSX_(external_React_.Suspense, { + fallback: JSX_(fallback.A, null) + }, routingSection && JSX_(LeftPanel, { + view, + views: VIEWS, + routingSection, + conversations: chats, + renderView: view => this.renderView(view), + startMeeting: () => { + this.startMeeting(); + eventlog(500293); + }, + scheduleMeeting: () => { + this.setState({ + scheduleMeetingDialog: true + }); + delay('chat-event-sm-button-main', () => eventlog(99918)); + }, + createNewChat: () => this.setState({ + contactSelectorDialog: true + }) + })), freeCallEndedDialog && JSX_(FreeCallEnded, { + onClose: () => { + this.setState({ + freeCallEndedDialog: false + }); + } + }), rightPane)); } } -ModeSwitch.NAMESPACE = 'modeSwitch'; -ModeSwitch.BASE_CLASS = 'mode'; -;// ./js/chat/ui/meetings/streamHead.jsx + const conversations = ConversationsApp; + }, + 3439 +(_, EXP_, REQ_) { +"use strict"; + REQ_.d(EXP_, { + A: () => Fallback + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); +class Fallback extends react0___default().Component { + render() { + return JSX_("div", { + className: "loading-spinner light" + }, JSX_("div", { + className: "main-loader" + })); + } +} + }, + 4664 +(_, EXP_, REQ_) { +"use strict"; + REQ_.d(EXP_, { + C: () => NAMESPACE, + x: () => FILTER + }); +const NAMESPACE = 'lhp'; +const FILTER = { + MUTED: 'muted', + UNREAD: 'unread' +}; + }, -class StreamHead extends mixins.w9 { - constructor(...args) { - super(...args); - this.delayProcID = null; - this.domRef = REaCt().createRef(); - this.durationRef = REaCt().createRef(); - this.dialogRef = REaCt().createRef(); - this.topicRef = REaCt().createRef(); - this.interval = undefined; - this.state = { - dialog: false, - duration: undefined, - banner: false, - modeSwitch: false - }; - this.updateDurationDOM = () => { - if (this.durationRef) { - this.durationRef.current.innerText = this.durationString; - } - }; - this.closeTooltips = () => { - for (const node of this.domRef.current.querySelectorAll('.simpletip')) { - node.dispatchEvent(StreamHead.EVENTS.SIMPLETIP); - } - }; - this.toggleFullscreen = () => this.fullscreen ? document.exitFullscreen() : document.documentElement.requestFullscreen(); - this.toggleBanner = callback => this.setState(state => ({ - banner: !state.banner - }), () => callback && callback()); - this.handleDialogClose = ({ - target - }) => { - if (this.state.dialog) { - let _targetDialog$domRef; - const { - topicRef, - dialogRef, - delayProcID - } = this; - const topicElement = topicRef && topicRef.current; - const targetDialog = dialogRef && dialogRef.current && dialogRef.current; - const dialogElement = (_targetDialog$domRef = targetDialog.domRef) == null ? void 0 : _targetDialog$domRef.current; - if (topicElement.contains(target)) { - return; - } - return (target.classList.contains('icon-dialog-close') || !dialogElement.contains(target)) && this.setState({ - dialog: false - }, () => delayProcID && delay.cancel(delayProcID)); - } - }; - this.getModerators = () => { - let _this$props$chatRoom; - const members = (_this$props$chatRoom = this.props.chatRoom) == null ? void 0 : _this$props$chatRoom.members; - if (members) { - const moderators = []; - for (const [handle, role] of Object.entries(members)) { - if (role === ChatRoom.MembersSet.PRIVILEGE_STATE.OPERATOR) { - moderators.push(M.getNameByHandle(handle)); - } - } - return mega.utils.trans.listToString(moderators, mega.icu.format(l.meeting_moderators, moderators.length)); - } - }; - this.Dialog = () => { - const link = `${getBaseUrl()}/${this.props.chatRoom.publicLink}`; - const mods = this.getModerators(); - return REaCt().createElement(modalDialogs.A.ModalDialog, (0,esm_extends.A)({ - ref: this.dialogRef - }, this.state, { - mods, - name: "meeting-info-dialog", - title: l[18132], - className: "group-chat-link dialog-template-main theme-dark-forced in-call-info", - hideOverlay: true - }), REaCt().createElement("section", { - className: "content" - }, REaCt().createElement("div", { - className: "content-block" - }, REaCt().createElement(utils.zT, { - className: "info" - }, mods), REaCt().createElement("div", { - className: "info" - }, l.copy_and_share), REaCt().createElement("div", { - className: "link-input-container" - }, REaCt().createElement("div", { - className: "mega-input with-icon box-style" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-link" - }), REaCt().createElement("input", { - type: "text", - className: "megaInputs", - readOnly: true, - value: link - })), REaCt().createElement(meetings_button.A, { - className: "mega-button positive copy-to-clipboard", - onClick: () => { - if (copyToClipboard(link)) { - this.toggleBanner(() => { - this.delayProcID = delay(`${StreamHead.NAMESPACE}-banner`, this.toggleBanner, 10000); - }); - } - } - }, REaCt().createElement("span", null, l[63]))), this.state.banner && REaCt().createElement("div", { - className: "banner-copy-success" - }, l[7654]))), REaCt().createElement("footer", null, REaCt().createElement("div", { - className: "footer-container" - }))); - }; - this.Pagination = () => { - const { - mode, - peers, - page, - streamsPerPage, - floatDetached, - chunksLength, - call, - onMovePage - } = this.props; - if (mode !== meetings_call.g.THUMBNAIL || !peers) { - return null; - } - const { - screen, - video, - rest - } = filterAndSplitSources(peers, call); - if (screen.length + video.length + rest.length > (floatDetached ? streamsPerPage + 1 : streamsPerPage)) { - return REaCt().createElement("div", { - className: `${StreamHead.NAMESPACE}-pagination` - }, REaCt().createElement(meetings_button.A, { - className: ` - carousel-button-prev - theme-dark-forced - ${page !== 0 ? '' : 'disabled'} - `, - icon: "sprite-fm-mono icon-arrow-left", - onClick: () => page !== 0 && onMovePage(PAGINATION.PREV) - }), REaCt().createElement("div", null, page + 1, "/", chunksLength), REaCt().createElement(meetings_button.A, { - className: ` - carousel-button-next - theme-dark-forced - ${page < chunksLength - 1 ? '' : 'disabled'} - `, - icon: "sprite-fm-mono icon-arrow-right", - onClick: () => page < chunksLength - 1 && onMovePage(PAGINATION.NEXT) - })); - } - return null; - }; - } - get fullscreen() { - return document.fullscreenElement; - } - get duration() { - return (Date.now() - this.props.call.ts) / 1000; - } - get durationString() { - return this.duration ? secondsToTimeShort(this.duration) : '--:--:--'; - } - componentWillUnmount() { - super.componentWillUnmount(); - clearInterval(this.durationInterval); - document.removeEventListener(StreamHead.EVENTS.FULLSCREEN, this.closeTooltips); - document.removeEventListener(StreamHead.EVENTS.CLICK_DIALOG, this.handleDialogClose); + 4649 +(_, EXP_, REQ_) { + +"use strict"; + REQ_.d(EXP_, { + A: () => Link + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + +class Link extends react0___default().Component { + constructor(props) { + super(props); + this.IS_CLICK_URL = undefined; + this.IS_CLICK_URL = this.props.to && (this.props.to.startsWith('/') || this.props.to.includes('mega.io')); } componentDidMount() { - super.componentDidMount(); - this.durationInterval = setInterval(this.updateDurationDOM, 1000); - document.addEventListener(StreamHead.EVENTS.FULLSCREEN, this.closeTooltips); - document.addEventListener(StreamHead.EVENTS.CLICK_DIALOG, this.handleDialogClose); + if (this.IS_CLICK_URL) { + clickURLs(); + } } render() { const { - NAMESPACE - } = StreamHead; - const { - mode, - streamsPerPage, - chatRoom, - onStreamsPerPageChange, - onCallMinimize, - onModeChange, - setActiveElement + className, + to, + target, + children, + onClick } = this.props; - const { - dialog - } = this.state; - const SIMPLETIP = { - position: 'bottom', - offset: 5, - className: 'theme-dark-forced' - }; - return REaCt().createElement("div", { - ref: this.domRef, - className: `${NAMESPACE}` - }, dialog && REaCt().createElement(this.Dialog, null), REaCt().createElement("div", { - className: `${NAMESPACE}-content theme-dark-forced` - }, REaCt().createElement("div", { - className: `${NAMESPACE}-info` - }, REaCt().createElement("div", { - ref: this.durationRef, - className: "stream-duration" - }, this.durationString), REaCt().createElement("div", { - ref: this.topicRef, - className: ` - stream-topic - ${chatRoom.isMeeting && chatRoom.publicLink ? 'has-meeting-link' : ''} - `, - onClick: () => chatRoom.isMeeting && chatRoom.publicLink && this.setState({ - dialog: !dialog, - banner: false - }, () => setActiveElement(this.state.dialog)) - }, REaCt().createElement(utils.zT, null, chatRoom.getRoomTitle()), chatRoom.isMeeting && chatRoom.publicLink && REaCt().createElement("i", { - className: ` - sprite-fm-mono - ${dialog ? 'icon-arrow-up' : 'icon-arrow-down'} - ` - }))), REaCt().createElement(this.Pagination, null), REaCt().createElement("div", { - className: `${NAMESPACE}-controls` - }, REaCt().createElement(ModeSwitch, { - mode, - streamsPerPage, - onStreamsPerPageChange, - onModeChange, - setActiveElement - }), REaCt().createElement(meetings_button.A, { - className: "head-control", - simpletip: { - ...SIMPLETIP, - label: this.fullscreen ? l.exit_fullscreen : l[17803] - }, - icon: this.fullscreen ? 'icon-fullscreen-leave' : 'icon-fullscreen-enter', - onClick: this.toggleFullscreen - }, REaCt().createElement("span", null, this.fullscreen ? l.exit_fullscreen : l[17803])), REaCt().createElement(meetings_button.A, { - className: "head-control", - simpletip: { - ...SIMPLETIP, - label: l.minimize - }, - icon: "icon-call-min-mode", - onClick: () => { - onCallMinimize(); - eventlog(500305); + if (this.IS_CLICK_URL) { + return JSX_("a", { + className: ` + clickurl + ${className || ''} + `, + href: to, + target + }, children); + } + return JSX_("a", { + className, + href: "#", + onClick: ev => { + if (onClick) { + ev.preventDefault(); + return onClick(ev); + } + return null; } - }, REaCt().createElement("div", null, l.minimize))))); + }, children); } } -StreamHead.NAMESPACE = 'stream-head'; -StreamHead.EVENTS = { - FULLSCREEN: 'fullscreenchange', - SIMPLETIP: new Event('simpletipClose'), - CLICK_DIALOG: 'click' -}; -// EXTERNAL MODULE: ./js/chat/ui/contacts.jsx -const contacts = REQ_(251); -// EXTERNAL MODULE: ./js/ui/perfectScrollbar.jsx -const perfectScrollbar = REQ_(486); -// EXTERNAL MODULE: ./js/chat/ui/link.jsx -const ui_link = REQ_(280); -;// ./js/chat/ui/meetings/waitingRoom/admit.jsx - - - + }, + 6740 +(_, EXP_, REQ_) { +"use strict"; + REQ_.d(EXP_, { + A: () => __WEBPACK_DEFAULT_EXPORT__ + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); -const NAMESPACE = 'admit'; -class Admit extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - this.peersWaitingRef = REaCt().createRef(); +class Group extends react0___default().Component { + constructor(props) { + super(props); + this.containerRef = react0___default().createRef(); this.state = { expanded: false }; - this.doAdmit = peers => { - let _this$props$call; - return (_this$props$call = this.props.call) == null || (_this$props$call = _this$props$call.sfuClient) == null ? void 0 : _this$props$call.wrAllowJoin([peers]); - }; - this.doDeny = peers => { - let _this$props$call2; - return (_this$props$call2 = this.props.call) == null || (_this$props$call2 = _this$props$call2.sfuClient) == null ? void 0 : _this$props$call2.wrKickOut([peers]); - }; - this.Icon = ({ - icon, - label, - onClick - }) => REaCt().createElement("i", { - className: ` - sprite-fm-mono - simpletip - ${icon} - `, - "data-simpletip": label, - "data-simpletipposition": "top", - "data-simpletipoffset": "5", - "data-simpletip-class": "theme-dark-forced", - onClick - }); - this.CallLimitBanner = ({ - call - }) => REaCt().createElement("div", { - className: `${NAMESPACE}-user-limit-banner` - }, call.organiser === u_handle ? (0,utils.lI)(l.admit_limit_banner_organiser, '[A]', ui_link.A, { - onClick() { - window.open(`${getBaseUrl()}/pro`, '_blank', 'noopener,noreferrer'); - eventlog(500259); - } - }) : l.admit_limit_banner_host); - this.renderPeersList = () => { - const { - peers, - call, - chatRoom - } = this.props; - const disableAdding = call.sfuClient.callLimits && call.sfuClient.callLimits.usr && chatRoom.getCallParticipants().length >= call.sfuClient.callLimits.usr; - return REaCt().createElement(perfectScrollbar.O, { - ref: this.peersWaitingRef, - options: { - 'suppressScrollX': true - } - }, REaCt().createElement("div", { - className: "peers-waiting" - }, this.isUserLimited && REaCt().createElement(this.CallLimitBanner, { - call - }), peers.map(handle => { - return REaCt().createElement("div", { - key: handle, - className: "peers-waiting-card" - }, REaCt().createElement("div", { - className: "peer-avatar" - }, REaCt().createElement(contacts.Avatar, { - contact: M.u[handle] - })), REaCt().createElement("div", { - className: "peer-name" - }, REaCt().createElement(contacts.ContactAwareName, { - contact: M.u[handle], - emoji: true - })), REaCt().createElement("div", { - className: "peer-controls" - }, REaCt().createElement(this.Icon, { - icon: "icon-close-component", - label: l.wr_deny, - onClick: () => this.doDeny(handle) - }), REaCt().createElement(this.Icon, { - icon: `icon-check ${disableAdding ? 'disabled' : ''}`, - label: l.wr_admit, - onClick: () => !disableAdding && this.doAdmit(handle) - }))); - }))); - }; - this.renderMultiplePeersWaiting = () => { - const { - call, - peers, - expanded, - onWrListToggle - } = this.props; - if (peers && peers.length) { - const disableAddAll = this.isUserLimited; - return REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("div", { - className: `${NAMESPACE}-head` - }, REaCt().createElement("h3", null, mega.icu.format(l.wr_peers_waiting, peers.length)), expanded ? REaCt().createElement(this.Icon, { - icon: "icon-arrow-up", - onClick: () => onWrListToggle(false) - }) : null), !expanded && disableAddAll && REaCt().createElement(this.CallLimitBanner, { - call - }), expanded && REaCt().createElement("div", { - className: `${NAMESPACE}-content` - }, this.renderPeersList()), REaCt().createElement("div", { - className: `${NAMESPACE}-controls` - }, expanded ? null : REaCt().createElement(meetings_button.A, { - className: "mega-button theme-dark-forced", - onClick: () => onWrListToggle(true) - }, REaCt().createElement("span", null, l.wr_see_waiting)), REaCt().createElement(meetings_button.A, { - peers, - className: `mega-button positive theme-dark-forced ${disableAddAll ? 'disabled' : ''}`, - onClick: () => !disableAddAll && call.sfuClient.wrAllowJoin(peers) - }, REaCt().createElement("span", null, l.wr_admit_all)))); - } - return null; - }; - this.renderSinglePeerWaiting = () => { - const { - peers, - call - } = this.props; - const peer = peers[0]; - const disableAdding = this.isUserLimited; - if (peer) { - return REaCt().createElement(REaCt().Fragment, null, REaCt().createElement(utils.P9, { - tag: "h3", - content: l.wr_peer_waiting.replace('%s', megaChat.html(M.getNameByHandle(peer))) - }), disableAdding && REaCt().createElement(this.CallLimitBanner, { - call - }), REaCt().createElement("div", { - className: `${NAMESPACE}-controls` - }, REaCt().createElement(meetings_button.A, { - className: "mega-button theme-dark-forced", - onClick: () => this.doDeny(peer) - }, REaCt().createElement("span", null, l.wr_deny)), REaCt().createElement(meetings_button.A, { - className: `mega-button positive theme-dark-forced ${disableAdding ? 'disabled' : ''}`, - onClick: () => !disableAdding && this.doAdmit(peer) - }, REaCt().createElement("span", null, l.wr_admit)))); - } - return null; - }; + this.doToggle = this.doToggle.bind(this); } - get isUserLimited() { - const { - call, - chatRoom, - peers - } = this.props; - return call.sfuClient.callLimits && call.sfuClient.callLimits.usr && chatRoom.getCallParticipants().length + (peers ? peers.length : 0) > call.sfuClient.callLimits.usr; + toggleEvents() { + return this.state.expanded ? $(document).rebind(`mousedown.${Group.NAMESPACE}`, ev => !this.containerRef.current.contains(ev.target) && this.doToggle()).rebind(`keydown.${Group.NAMESPACE}`, ({ + keyCode + }) => keyCode && keyCode === 27 && this.doToggle()) : $(document).unbind(`.${Group.NAMESPACE}`); + } + doToggle() { + this.setState(state => ({ + expanded: !state.expanded + }), () => this.toggleEvents()); } render() { const { - chatRoom, - peers + active, + warn, + onHold, + screenSharing, + children } = this.props; - if (chatRoom.iAmOperator()) { - return REaCt().createElement("div", { - ref: this.domRef, + if (children && children.length) { + return JSX_("div", { + ref: this.containerRef, + className: Group.BASE_CLASS + }, JSX_("div", { className: ` - ${NAMESPACE} - theme-dark-forced - ` - }, REaCt().createElement("div", { - className: `${NAMESPACE}-wrapper` - }, peers && peers.length > 1 ? this.renderMultiplePeersWaiting() : this.renderSinglePeerWaiting())); + ${Group.BASE_CLASS}-menu + ${this.state.expanded ? 'expanded' : ''} + `, + onClick: this.doToggle + }, children.map(item => { + return item && JSX_("div", { + key: item.key, + className: `${Group.BASE_CLASS}-item` + }, item); + })), JSX_("button", { + className: "mega-button theme-light-forced round large", + onClick: this.doToggle + }, active && JSX_("div", { + className: "info-indicator active" + }), warn && JSX_("div", { + className: "info-indicator warn simpletip", + "data-simpletip": l.screen_share_crop_tip, + "data-simpletipposition": "top", + "data-simpletipoffset": "5", + "data-simpletip-class": "theme-dark-forced" + }, JSX_("i", { + className: "sprite-fm-mono icon-exclamation-filled" + })), JSX_("i", { + className: ` + sprite-fm-mono + ${screenSharing ? 'icon-end-screenshare' : ''} + ${!onHold && !screenSharing && 'icon-options'} + ` + }))); } return null; } } -;// ./js/chat/ui/meetings/stream.jsx - - - - - - +Group.NAMESPACE = 'buttonGroup'; +Group.BASE_CLASS = 'button-group'; +class Button extends react0___default().Component { + constructor(...args) { + super(...args); + this.buttonRef = react0___default().createRef(); + } + componentDidUpdate() { + if (this.props.simpletip) { + $(this.buttonRef.current).trigger('simpletipUpdated'); + } + } + componentDidMount() { + if (this.props.didMount) { + this.props.didMount(this); + } + } + render() { + const { + children, + className, + style, + simpletip, + icon, + onClick + } = this.props; + return JSX_("button", { + ref: this.buttonRef, + className: ` + ${className ? className : ''} + ${simpletip ? 'simpletip' : ''} + `, + style, + "data-simpletip": simpletip == null ? void 0 : simpletip.label, + "data-simpletipposition": simpletip == null ? void 0 : simpletip.position, + "data-simpletipoffset": simpletip == null ? void 0 : simpletip.offset, + "data-simpletip-class": simpletip == null ? void 0 : simpletip.className, + onClick + }, icon && JSX_("i", { + className: `sprite-fm-mono ${icon}` + }), children); + } +} +Button.Group = Group; + const __WEBPACK_DEFAULT_EXPORT__ = Button; + }, + 6521 +(_, EXP_, REQ_) { +"use strict"; + REQ_.d(EXP_, { + PS: () => addMonths, + We: () => stringToTime, + XH: () => stringToDate, + a4: () => getTimeIntervals, + cK: () => isToday, + dB: () => getUserTimezone, + ef: () => isTomorrow, + i_: () => getNearestHalfHour, + ro: () => isSameDay + }); +const stringToDate = string => { + return moment(string, ['DD MMM YYYY', 'DD-MM-YYYY', 'DD.MM.YYYY', 'MMM DD YYYY', 'YYYY MMM DD', 'YYYY DD MMM']); +}; +const stringToTime = string => moment(string, ['HH:mm', 'hh:mm A']); +const isSameDay = (a, b) => { + return new Date(a).toDateString() === new Date(b).toDateString(); +}; +const isToday = timestamp => { + return new Date(timestamp).toDateString() === new Date().toDateString(); +}; +const isTomorrow = timestamp => { + const tomorrow = new Date(); + tomorrow.setDate(tomorrow.getDate() + 1); + return tomorrow.toDateString() === new Date(timestamp).toDateString(); +}; +const getDaysInMonth = (year, month) => { + return new Date(year, month, 0).getDate(); +}; +const addMonths = (timestamp, months) => { + const date = new Date(timestamp); + return new Date(date.setMonth(date.getMonth() + months)).getTime(); +}; +const getNearestHalfHour = (timestamp = Date.now()) => { + const { + SCHEDULED_MEETINGS_INTERVAL + } = ChatRoom; + return new Date(Math.ceil(timestamp / SCHEDULED_MEETINGS_INTERVAL) * SCHEDULED_MEETINGS_INTERVAL).getTime(); +}; +const getUserTimezone = () => { + return Intl.DateTimeFormat().resolvedOptions().timeZone; +}; +const getTimeIntervals = (timestamp, offsetFrom, interval = 30) => { + const increments = []; + if (timestamp) { + const [targetDate, initialDate] = [new Date(timestamp), new Date(timestamp)].map(date => { + date.setHours(0); + date.setMinutes(0); + return date; + }); + while (targetDate.getDate() === initialDate.getDate()) { + const timestamp = targetDate.getTime(); + const diff = offsetFrom && timestamp - offsetFrom; + increments.push({ + value: timestamp, + label: toLocaleTime(timestamp), + duration: diff && diff > 0 ? diff : undefined + }); + targetDate.setMinutes(targetDate.getMinutes() + interval); + } + } + return increments; +}; + }, + 3901 +(_, EXP_, REQ_) { -const stream_NAMESPACE = 'stream'; +"use strict"; + REQ_.d(EXP_, { + $A: () => MAX_STREAMS, + Av: () => isExpanded, + Bq: () => PAGINATION, + Cy: () => isModerator, + Fj: () => EXPANDED_FLAG, + HV: () => getUnsupportedBrowserMessage, + P: () => isGuest, + ZE: () => TYPE, + _F: () => renderEndConfirm, + dQ: () => inProgressAlert, + g: () => MODE, + gR: () => VIEW, + gh: () => STREAMS_PER_PAGE, + hK: () => STREAM_ACTIONS, + sX: () => renderLeaveConfirm + }); +const EXPANDED_FLAG = 'in-call'; +const MODE = { + THUMBNAIL: 1, + MAIN: 2, + MINI: 3 +}; +const VIEW = { + DEFAULT: 0, + CHAT: 1, + PARTICIPANTS: 2 +}; +const TYPE = { + AUDIO: 1, + VIDEO: 2 +}; const STREAM_ACTIONS = { ADD: 1, REMOVE: 2 @@ -26881,757 +5217,44 @@ const STREAMS_PER_PAGE = { MED: 21, MAX: 49 }; -const chunkNodes = (nodes, size) => { - if (nodes && nodes.length && size) { - const chunked = []; - let index = 0; - while (index < nodes.length) { - chunked.push({ - id: index, - nodes: nodes.slice(index, index + size) - }); - index += size; - } - return chunked; - } - return null; -}; -const filterAndSplitSources = (sources, call) => { - const screen = []; - const video = []; - const rest = []; - for (const peer of Object.values(sources.toJS())) { - if (peer instanceof CallManager2.Peer) { - if (peer.hasScreen) { - screen.push(peer, peer); - } else if (peer.videoMuted) { - rest.push(peer); - } else { - video.push(peer); - } - } - } - const local = call.getLocalStream(); - if (local.hasScreen) { - const presenters = [...call.presenterStreams]; - if (presenters.pop() === u_handle) { - screen.unshift(local, local); - } else { - screen.push(local, local); - } - } else if (local.av & Av.Camera) { - video.unshift(local); - } else { - rest.push(local); - } - return { - screen, - video, - rest - }; -}; -class stream_Stream extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - this.containerRef = REaCt().createRef(); - this.nodeRefs = []; - this.chunks = []; - this.chunksLength = 0; - this.lastRescaledCache = undefined; - this.state = { - page: 0, - overlayed: false, - streamsPerPage: STREAMS_PER_PAGE.MED, - floatDetached: false, - wrToggled: false - }; - this.toggleFloatDetachment = () => { - this.setState(state => ({ - floatDetached: !state.floatDetached - })); - }; - this.toggleWaitingRoomList = state => { - this.setState({ - wrToggled: state - }); - }; - } - movePage(direction) { - return this.setState(state => ({ - page: direction === PAGINATION.NEXT ? state.page + 1 : state.page - 1 - })); - } - getColumns(streamsCount) { - switch (true) { - case streamsCount >= 43: - return 7; - case streamsCount >= 26: - return 6; - case streamsCount >= 17: - return 5; - case streamsCount >= 13: - return 4; - case streamsCount === 1: - return 1; - case streamsCount >= 7: - return 3; - default: - return 2; - } - } - scaleNodes(columns, forced = false) { - let _Object$values$page; - const { - peers, - minimized, - mode, - call - } = this.props; - const { - screen, - video, - rest - } = filterAndSplitSources(peers, call); - let presenter = false; - const sources = [...screen, ...video, ...rest].filter(source => { - if (!source.isLocal) { - return true; - } - if (source.hasScreen && !presenter) { - presenter = true; - return true; - } - return false; - }); - presenter = false; - const container = this.containerRef.current; - this.lastRescaledCache = forced ? null : this.lastRescaledCache; - if (minimized || !container) { - return; - } - const { - floatDetached, - streamsPerPage, - page - } = this.state; - const parentRef = container.parentNode; - const parentStyle = getComputedStyle(parentRef); - const extraVerticalMargin = parseInt(parentStyle.paddingTop) + parseInt(parentStyle.paddingBottom); - let containerWidth = parentRef.offsetWidth; - let containerHeight = parentRef.offsetHeight - extraVerticalMargin; - const nodesPerPage = floatDetached ? streamsPerPage : streamsPerPage - 1; - const streamsInUI = sources.length > nodesPerPage ? (_Object$values$page = Object.values(this.chunks)[page]) == null ? void 0 : _Object$values$page.nodes : sources; - if (streamsInUI) { - const streamCountInUI = sources.length > nodesPerPage || floatDetached ? streamsInUI.length : streamsInUI.length + 1; - let rows; - if (mode === meetings_call.g.THUMBNAIL) { - columns = typeof columns === 'number' ? columns : this.getColumns(streamCountInUI); - rows = Math.ceil(streamCountInUI / columns); - } else { - rows = 1; - columns = 1; - } - containerWidth -= columns * 6 * 2; - containerHeight -= rows * 6 * 2; - let targetWidth = Math.floor(containerWidth / columns); - let targetHeight = targetWidth / 16 * 9; - if (targetHeight * rows > containerHeight) { - targetHeight = Math.floor(containerHeight / rows); - targetWidth = targetHeight / 9 * 16; - } - const nodeRefs = this.nodeRefs.flat(); - const nodeRefsLength = nodeRefs.length; - const viewMode = mode || meetings_call.g.MAIN; - let cache = `${viewMode}:${targetWidth}:${targetHeight}:${nodeRefsLength}:${rows}:${streamCountInUI}:${columns}`; - for (let i = 0; i < nodeRefsLength; i++) { - cache += `${nodeRefs[i].cacheKey}:`; - } - if (this.lastRescaledCache === cache) { - return; - } - this.lastRescaledCache = cache; - for (let i = 0; i < nodeRefsLength; i++) { - const node = nodeRefs[i]; - if (node && node.ref) { - node.ref.style.width = `${targetWidth}px`; - node.ref.style.height = `${targetHeight}px`; - } - } - container.style.width = `${(targetWidth + 12) * columns}px`; - } - } - renderNodes() { - const { - mode, - peers, - call, - raisedHandPeers, - chatRoom, - onVideoDoubleClick, - onModeChange - } = this.props; - const { - page, - streamsPerPage, - floatDetached - } = this.state; - const { - screen, - video, - rest - } = filterAndSplitSources(peers, call); - const sources = [...screen, ...video, ...rest]; - if (mode === meetings_call.g.THUMBNAIL) { - const nodesPerPage = floatDetached ? streamsPerPage : streamsPerPage - 1; - if (sources.length <= nodesPerPage) { - const $$PEER = (peer, i) => { - const { - clientId, - hasScreenAndCam, - hasScreen, - isLocal - } = peer; - if (screen.length && (screen[0].clientId === clientId || screen[0].isLocal && isLocal)) { - screen.shift(); - } - if (!(peer instanceof CallManager2.Peer)) { - const isPresenterNode = screen.length && screen[0].isLocal; - if (floatDetached && !isPresenterNode) { - return; - } - if (floatDetached || !isPresenterNode) { - return REaCt().createElement(videoNode.bJ, { - key: `${mode}_thumb_${u_handle}`, - chatRoom, - isPresenterNode: false, - raisedHandPeers, - source: peer, - didMount: ref => { - this.nodeRefs.push({ - clientId: u_handle, - cacheKey: `${mode}_${u_handle}_thumb`, - ref - }); - this.scaleNodes(undefined, true); - }, - willUnmount: () => { - this.nodeRefs = this.nodeRefs.filter(nodeRef => nodeRef.cacheKey !== `${mode}_${u_handle}_thumb`); - } - }, this.renderSelfViewMenu()); - } - return REaCt().createElement(videoNode.Cn, { - key: `${mode}_${u_handle}`, - chatRoom, - isPresenterNode, - source: isPresenterNode && peer, - raisedHandPeers, - didMount: ref => { - this.nodeRefs.push({ - clientId: u_handle, - cacheKey: `${mode}_${u_handle}`, - ref - }); - this.scaleNodes(undefined, true); - }, - willUnmount: () => { - this.nodeRefs = this.nodeRefs.filter(nodeRef => nodeRef.cacheKey !== `${mode}_${u_handle}`); - } - }, hasScreen ? this.renderNodeMenu(peer, { - isPresenterNode - }) : this.renderSelfViewMenu()); - } - const presenterCid = screen.length && screen[0].clientId === clientId; - let PeerClass = videoNode.au; - if (hasScreenAndCam) { - PeerClass = presenterCid ? videoNode.zu : videoNode.Qs; - } - const cacheKey = `${mode}_${clientId}_${i}_${hasScreenAndCam ? 1 : 0}`; - return REaCt().createElement(PeerClass, { - key: cacheKey, - mode, - chatRoom, - menu: true, - source: peer, - raisedHandPeers, - isPresenterNode: !!presenterCid, - onDoubleClick: (peer, e) => { - e.preventDefault(); - e.stopPropagation(); - onVideoDoubleClick(peer, !presenterCid); - }, - didMount: ref => { - this.nodeRefs.push({ - clientId: presenterCid || clientId, - cacheKey, - ref - }); - }, - willUnmount: () => { - this.nodeRefs = this.nodeRefs.filter(nodeRef => nodeRef.cacheKey !== cacheKey); - } - }, this.renderNodeMenu(peer, { - isPresenterNode: !!presenterCid - })); - }; - return sources.map((p, i) => $$PEER(p, i)); - } - if (floatDetached) { - for (let i = 0; i < sources.length; i++) { - if (sources[i].isLocal) { - sources.splice(i, 1); - break; - } - } - } - this.chunks = chunkNodes(sources, streamsPerPage); - this.chunksLength = Object.values(this.chunks).length; - return REaCt().createElement("div", { - className: "carousel" - }, REaCt().createElement("div", { - className: "carousel-container" - }, Object.values(this.chunks).map((chunk, i) => { +const isGuest = () => !u_type; +const inProgressAlert = (isJoin, chatRoom) => { + return new Promise((resolve, reject) => { + if (megaChat.haveAnyActiveCall()) { + if (window.sfuClient) { const { - id, - nodes - } = chunk; - return REaCt().createElement("div", { - key: id, - className: ` - carousel-page - ${i === page ? 'active' : ''} - ` - }, nodes.map((peer, j) => { - const { - clientId, - hasScreenAndCam, - hasScreen, - isLocal - } = peer; - if (screen.length && (screen[0].clientId === clientId || screen[0].isLocal && isLocal)) { - screen.shift(); - } - if (peer instanceof CallManager2.Peer) { - const presenterCid = screen.length && screen[0].clientId === clientId; - const cacheKey = `${mode}_${clientId}_${j + i * streamsPerPage}_${hasScreenAndCam ? 1 : 0}`; - let PeerClass = videoNode.au; - if (hasScreenAndCam) { - PeerClass = presenterCid ? videoNode.zu : videoNode.Qs; - } - return REaCt().createElement(PeerClass, { - key: cacheKey, - mode, - source: peer, - chatRoom, - isPresenterNode: !!presenterCid, - raisedHandPeers, - onDoubleClick: (peer, e) => { - e.preventDefault(); - e.stopPropagation(); - onVideoDoubleClick(peer); - }, - didMount: ref => { - if (!this.nodeRefs[id]) { - this.nodeRefs[id] = []; - } - this.nodeRefs[id].push({ - clientId: presenterCid || clientId, - ref, - cacheKey - }); - this.scaleNodes(undefined, true); - }, - willUnmount: () => { - this.nodeRefs = this.nodeRefs.map(chunk => chunk.filter(nodeRef => nodeRef.cacheKey !== cacheKey)); - } - }, this.renderNodeMenu(peer, { - isPresenterNode: !!presenterCid - })); - } - const isPresenterNode = screen.length && screen[0].isLocal; - if (floatDetached && !isPresenterNode) { - return null; - } - if (floatDetached || !isPresenterNode) { - return REaCt().createElement(videoNode.bJ, { - key: `${mode}_thumb_${u_handle}`, - chatRoom, - source: peer, - isPresenterNode: false, - didMount: ref => { - if (!this.nodeRefs[id]) { - this.nodeRefs[id] = []; - } - this.nodeRefs[id].push({ - clientId: u_handle, - cacheKey: `${mode}_${u_handle}_thumb`, - ref - }); - this.scaleNodes(undefined, true); - }, - willUnmount: () => { - this.nodeRefs = this.nodeRefs.map(chunk => chunk.filter(nodeRef => nodeRef.cacheKey !== `${mode}_${u_handle}_thumb`)); - } - }, this.renderSelfViewMenu()); - } - return REaCt().createElement(videoNode.Cn, { - key: `${mode}_${u_handle}`, - chatRoom, - raisedHandPeers, - isPresenterNode, - source: isPresenterNode && peer, - didMount: ref => { - if (!this.nodeRefs[id]) { - this.nodeRefs[id] = []; - } - this.nodeRefs[id].push({ - clientId: u_handle, - ref, - cacheKey: `${mode}_${u_handle}` - }); - this.scaleNodes(undefined, true); - }, - willUnmount: () => { - this.nodeRefs = this.nodeRefs.map(chunk => chunk.filter(nodeRef => nodeRef.cacheKey !== `${mode}_${u_handle}`)); - } - }, hasScreen ? this.renderNodeMenu(peer, { - isPresenterNode - }) : this.renderSelfViewMenu()); - })); - }))); - } - const source = call.getActiveStream(); - if (!source) { - return null; - } - const VideoType = source.isLocal ? videoNode.Cn : videoNode.zu; - const videoNodeRef = REaCt().createRef(); - return REaCt().createElement(VideoType, { - key: source.clientId, - chatRoom, - raisedHandPeers, - source, - isPresenterNode: source.hasScreen, - toggleFullScreen: () => { - call.setPinnedCid(source.clientId); - }, - onSpeakerChange: () => { - onModeChange(meetings_call.g.THUMBNAIL); - }, - ref: node => { - videoNodeRef.current = node; - } - }, this.renderNodeMenu(source, { - key: `${source.clientId}-main`, - isMain: true, - videoNodeRef, - isPresenterNode: source.hasScreen - })); - } - renderNodeMenu(peer, props) { - const { - mode, - chatRoom, - ephemeralAccounts, - onCallMinimize, - onSpeakerChange, - onModeChange - } = this.props; - return REaCt().createElement(videoNodeMenu.Ay, (0,esm_extends.A)({ - mode, - privilege: chatRoom.members[peer.userHandle], - chatRoom, - stream: peer, - ephemeralAccounts, - onCallMinimize, - onSpeakerChange, - onModeChange - }, props)); - } - renderSelfViewMenu() { - const { - call, - onSpeakerChange - } = this.props; - return REaCt().createElement("div", { - className: "node-menu theme-dark-forced" - }, REaCt().createElement("div", { - className: "node-menu-toggle" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-more-horizontal-thin-outline" - })), REaCt().createElement("div", { - className: "node-menu-content" - }, REaCt().createElement("ul", null, REaCt().createElement("li", null, REaCt().createElement(meetings_button.A, { - icon: "sprite-fm-mono grid-main", - onClick: () => onSpeakerChange(call.getLocalStream()) - }, REaCt().createElement("span", null, l.display_in_main_view))), REaCt().createElement("li", null, REaCt().createElement(meetings_button.A, { - icon: "sprite-fm-mono grid-separate", - onClick: this.toggleFloatDetachment - }, REaCt().createElement("span", null, l.separate_from_grid_button)))))); - } - renderOnHold() { - return REaCt().createElement("div", { - className: "on-hold-overlay" - }, REaCt().createElement("div", { - className: "stream-on-hold theme-light-forced", - onClick: this.props.onHoldClick - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-play" - }), REaCt().createElement("span", null, l[23459]))); - } - renderStreamContainer() { - const { - call, - chatRoom, - peers, - stayOnEnd, - everHadPeers, - isOnHold, - mode, - hasOtherParticipants, - onInviteToggle, - onStayConfirm, - onCallEnd - } = this.props; - const { - screen, - video, - rest - } = filterAndSplitSources(peers, call); - const sources = [...screen, ...video, ...rest]; - const showNotice = sources.length === 0 || !hasOtherParticipants && !call.presenterStreams.has(u_handle); - const streamContainer = content => REaCt().createElement("div", { - ref: this.containerRef, - className: ` - ${stream_NAMESPACE}-container - ${showNotice ? 'with-notice' : ''} - ${sources.length === 1 && mode === meetings_call.g.THUMBNAIL ? `${this.state.floatDetached ? 'single' : 'dual'}-stream` : ''} - ` - }, content); - if (showNotice) { - return REaCt().createElement(ParticipantsNotice, { - call, - hasLeft: call.left, - chatRoom, - everHadPeers, - streamContainer, - stayOnEnd, - isOnHold, - onInviteToggle, - onStayConfirm, - onCallEnd: () => onCallEnd(1) - }); - } - return streamContainer(this.renderNodes()); - } - renderToaster() { - return REaCt().createElement(chatToaster.A, { - showDualNotifications: true, - hidden: this.props.minimized, - onShownToast: toast => { - if (toast.options && toast.options.persistent) { - this.setState({ - overlayed: true - }); - } - }, - onHideToast: toast => { - if (this.state.overlayed && toast.options && toast.options.persistent) { - this.setState({ - overlayed: false - }); + chatRoom: activeCallRoom + } = megaChat.activeCall; + const peers = activeCallRoom ? activeCallRoom.getParticipantsExceptMe(activeCallRoom.getCallParticipants()).map(h => M.getNameByHandle(h)) : []; + let body = isJoin ? l.cancel_to_join : l.cancel_to_start; + if (peers.length) { + body = mega.utils.trans.listToString(peers, isJoin ? l.cancel_with_to_join : l.cancel_with_to_start); } + msgDialog('warningb', null, l.call_in_progress, body, null, 1); + return reject(); } - }); - } - specShouldComponentUpdate(nextProps) { - if (nextProps.minimized !== this.props.minimized || nextProps.mode !== this.props.mode || nextProps.isFloatingPresenter !== this.props.isFloatingPresenter) { - return true; - } - return null; - } - componentWillUnmount() { - super.componentWillUnmount(); - chatGlobalEventManager.removeEventListener('resize', this.getUniqueId()); - mBroadcaster.removeListener(this.callHoldListener); - } - componentDidMount() { - super.componentDidMount(); - this.scaleNodes(); - chatGlobalEventManager.addEventListener('resize', this.getUniqueId(), () => this.scaleNodes()); - this.callHoldListener = mBroadcaster.addListener('meetings:toggleHold', () => this.scaleNodes(undefined, true)); - } - componentDidUpdate() { - super.componentDidMount(); - const { - call, - mode, - forcedLocal, - onSpeakerChange, - onModeChange - } = this.props; - this.scaleNodes(); - if (this.chunksLength > 0 && this.state.page + 1 > this.chunksLength) { - this.movePage(PAGINATION.PREV); - } - if (mode === meetings_call.g.THUMBNAIL && call.pinnedCid !== null) { - this.hasPresenter = true; - onSpeakerChange(call.getActiveStream()); - } else if (mode === meetings_call.g.MAIN && call.pinnedCid === null && !call.presenterStreams.size && this.hasPresenter) { - this.hasPresenter = false; - onModeChange(meetings_call.g.THUMBNAIL); - } else if (mode === meetings_call.g.MAIN && forcedLocal && call.pinnedCid !== 0) { - onSpeakerChange(call.getActiveStream()); - } else if (!call.presenterStreams.size) { - this.hasPresenter = false; + if (chatRoom.getCallParticipants().includes(u_handle)) { + return resolve(); + } + return msgDialog(`warningb:!^${l[2005]}!${isJoin ? l.join_call_anyway : l.start_call_anyway}`, null, isJoin ? l.join_multiple_calls_title : l.start_multiple_calls_title, isJoin ? l.join_multiple_calls_text : l.start_multiple_calls_text, join => { + if (join) { + return resolve(); + } + return reject(); + }, 1); } + resolve(); + }); +}; +window.inProgressAlert = inProgressAlert; +const isModerator = (chatRoom, handle) => { + if (chatRoom && handle) { + return chatRoom.members[handle] === ChatRoom.MembersSet.PRIVILEGE_STATE.OPERATOR; } - render() { - const { - overlayed, - page, - streamsPerPage, - floatDetached, - wrToggled - } = this.state; - const { - mode, - call, - chatRoom, - minimized, - peers, - sidebar, - hovered, - forcedLocal, - view, - isOnHold, - waitingRoomPeers, - recorderCid, - raisedHandPeers, - isFloatingPresenter, - onRecordingToggle, - onCallMinimize, - onCallExpand, - onModeChange, - onAudioClick, - onVideoClick, - onCallEnd, - onScreenSharingClick, - onHoldClick, - onSpeakerChange, - onParticipantsToggle, - setActiveElement - } = this.props; - return REaCt().createElement("div", { - ref: this.domRef, - className: ` - ${stream_NAMESPACE} - ${sidebar ? '' : 'full'} - ${hovered ? 'hovered' : ''} - ` - }, waitingRoomPeers && waitingRoomPeers.length ? REaCt().createElement(Admit, { - chatRoom, - call, - peers: waitingRoomPeers, - expanded: wrToggled, - onWrListToggle: this.toggleWaitingRoomList - }) : null, this.renderToaster(), minimized ? null : REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("div", { - className: ` - ${stream_NAMESPACE}-wrapper - ${mode === meetings_call.g.MAIN ? 'with-participants-block' : ''} - ` - }, isOnHold ? this.renderOnHold() : overlayed && REaCt().createElement("div", { - className: "call-overlay" - }), this.renderStreamContainer()), mode === meetings_call.g.MAIN && REaCt().createElement(ParticipantsBlock, (0,esm_extends.A)({}, this.props, { - floatDetached, - onSeparate: this.toggleFloatDetachment - })), REaCt().createElement(StreamHead, { - disableCheckingVisibility: true, - mode, - peers, - page, - streamsPerPage, - floatDetached, - chunksLength: this.chunksLength, - call, - chatRoom, - onCallMinimize, - onModeChange, - onStreamsPerPageChange: streamsPerPage => this.setState({ - streamsPerPage - }), - onMovePage: direction => this.movePage(direction), - setActiveElement - })), REaCt().createElement(FloatingVideo, { - call, - peers, - mode, - view, - floatDetached, - isOnHold, - chatRoom, - minimized, - sidebar, - forcedLocal, - isPresenterNode: isFloatingPresenter, - wrapperRef: this.domRef, - waitingRoomPeers, - recorderCid, - raisedHandPeers, - onRecordingToggle, - onAudioClick, - onVideoClick, - onCallEnd, - onScreenSharingClick, - onCallMinimize, - onMoveIntoGrid: this.toggleFloatDetachment, - onCallExpand: async () => { - await onCallExpand(); - this.scaleNodes(undefined, true); - }, - onSpeakerChange, - onModeChange, - onHoldClick, - onParticipantsToggle, - onWrListToggle: this.toggleWaitingRoomList - })); - } -} - -}, - -489 -(_, EXP_, REQ_) { - -"use strict"; -REQ_.d(EXP_, { -Ay: () => __WEBPACK_DEFAULT_EXPORT__, -_F: () => renderEndConfirm, -sX: () => renderLeaveConfirm -}); -const _extends0__ = REQ_(168); -const react1__ = REQ_(594); -const react1 = REQ_.n(react1__); -const _mixins2__ = REQ_(137); -const _button_jsx3__ = REQ_(959); -const _stream_jsx4__ = REQ_(415); -const _micObserver_jsx5__ = REQ_(772); -const _permissionsObserver_jsx6__ = REQ_(542); -const _call_jsx7__ = REQ_(3); -const _hostsObserver_jsx8__ = REQ_(972); -const _ui_dropdowns_jsx9__ = REQ_(911); -const _ui_utils_jsx10__ = REQ_(314); - - - - - - - - - - - + return false; +}; +const isExpanded = () => document.body.classList.contains(EXPANDED_FLAG); +const getUnsupportedBrowserMessage = () => navigator.userAgent.match(/Chrom(e|ium)\/(\d+)\./) ? l.alert_unsupported_browser_version : l.alert_unsupported_browser; const renderLeaveConfirm = (onConfirm, onRecordingToggle) => msgDialog(`confirmation:!^${l.leave_call_recording_dialog_cta}!${l.leave_call_recording_dialog_nop_cta}`, undefined, l.leave_call_recording_dialog_heading, l.leave_call_recording_dialog_body, cb => { if (cb) { onRecordingToggle(); @@ -27644,9898 +5267,7046 @@ const renderEndConfirm = (onConfirm, onRecordingToggle) => msgDialog(`confirmati onConfirm(); } }, 1); -class StreamControls extends _mixins2__.w9 { - constructor(...args) { - super(...args); - this.domRef = react1().createRef(); - this.endContainerRef = react1().createRef(); - this.endButtonRef = react1().createRef(); - this.SIMPLETIP = { - position: 'top', - offset: 8, - className: 'theme-dark-forced' - }; - this.state = { - endCallOptions: false, - endCallPending: false, - devices: {}, - audioSelectDropdown: false, - videoSelectDropdown: false, - loading: false, - muteSpeak: false - }; - this.LeaveButton = (0,_hostsObserver_jsx8__.C)(({ - hasHost, - chatRoom, - confirmLeave, - onLeave - }) => { - const doLeave = () => hasHost(chatRoom.call ? chatRoom.call.peers.map(a => a.userHandle) : []) ? onLeave() : confirmLeave({ - title: l.assign_host_leave_call, - body: l.assign_host_leave_call_details, - cta: l.assign_host_button, - altCta: l.leave_anyway - }); - return react1().createElement(_button_jsx3__.A, { - className: "mega-button", - onClick: () => { - const { - recorderCid, - call, - onRecordingToggle - } = this.props; - return recorderCid && recorderCid === call.sfuClient.cid ? renderLeaveConfirm(doLeave, onRecordingToggle) : doLeave(); - } - }, react1().createElement("span", null, l.leave)); + + }, + + 855 +(_, EXP_, REQ_) { + +"use strict"; + REQ_.d(EXP_, { + M: () => ConversationMessageMixin + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _mixins_js1__ = REQ_(8264); + const _ui_buttons_jsx2__ = REQ_(5155); + const _ui_emojiDropdown_jsx3__ = REQ_(1165); + + + + +class ConversationMessageMixin extends _mixins_js1__ .u9 { + constructor(props) { + super(props); + this.attachRerenderCallbacks = false; + this._reactionContactHandles = []; + this.__cmmUpdateTickCount = 0; + this._contactChangeListeners = false; + this.onAfterRenderWasTriggered = false; + lazy(this, '__cmmId', () => { + return `${this.getUniqueId() }--${ String(Math.random()).slice(-7)}`; }); - this.setActiveElement = forced => this.props.setActiveElement(forced || this.state.audioSelectDropdown || this.state.videoSelectDropdown || this.state.endCallOptions); - this.handleMousedown = ({ - target - }) => { - if (this.isMounted()) { - const { - audioSelectDropdown, - videoSelectDropdown, - endCallOptions - } = this.state; - return (audioSelectDropdown || videoSelectDropdown || endCallOptions) && ['audio-sources', 'video-sources', 'meetings-end-options'].some(selector => { - let _document$querySelect; - return (_document$querySelect = document.querySelector(`.${selector}`)) == null ? void 0 : _document$querySelect.contains(target); - }) ? 0x4B1D : this.setState({ - audioSelectDropdown: false, - videoSelectDropdown: false, - endCallOptions: false - }, this.setActiveElement); - } - }; - this.renderDebug = () => { - return react1().createElement("div", { - className: "stream-debug", - style: { - position: 'absolute', - left: 25, - bottom: 36, - display: 'flex', - alignItems: 'center', - color: 'tomato' - } - }, react1().createElement(_button_jsx3__.A, { - className: "mega-button round small theme-dark-forced positive", - simpletip: { - ...this.SIMPLETIP, - label: 'Add Stream' - }, - onClick: () => this.props.onStreamToggle(_stream_jsx4__.hK.ADD) - }, react1().createElement("span", null, l.add)), react1().createElement(_button_jsx3__.A, { - className: "mega-button round small theme-dark-forced negative", - simpletip: { - ...this.SIMPLETIP, - label: 'Remove Stream' - }, - onClick: () => this.props.peers.length > 1 && this.props.onStreamToggle(_stream_jsx4__.hK.REMOVE) - }, react1().createElement("span", null, l[83])), react1().createElement("span", null, this.props.peers.length + 1)); - }; - this.renderEndCallOptions = () => { - let _this$endContainerRef; - const { - call, - chatRoom, - recorderCid, - onRecordingToggle, - onCallEnd - } = this.props; - const { - endCallOptions, - endCallPending - } = this.state; - const doEnd = () => this.setState({ - endCallPending: true - }, () => chatRoom.endCallForAll()); - const endContainerRef = (_this$endContainerRef = this.endContainerRef) == null ? void 0 : _this$endContainerRef.current; - return react1().createElement("div", (0,_extends0__.A)({}, endCallOptions && { - style: (({ - left, - top - }) => ({ - left, - top - }))(endContainerRef.getBoundingClientRect()) - }, { - className: ` - meetings-end-options - theme-dark-forced - ${endCallOptions ? '' : 'hidden'} - ` - }), react1().createElement("div", { - className: "meetings-end-options-content" - }, react1().createElement(this.LeaveButton, { - chatRoom, - recorderCid, - participants: chatRoom.getCallParticipants(), - onLeave: onCallEnd, - onConfirmDenied: onCallEnd - }), react1().createElement(_button_jsx3__.A, { - className: ` - mega-button - positive - ${endCallPending ? 'disabled' : ''} - `, - onClick: () => { - if (recorderCid && recorderCid === call.sfuClient.cid) { - return renderEndConfirm(doEnd, onRecordingToggle); - } - return doEnd(); - } - }, react1().createElement("span", null, l.end_for_all)))); - }; - this.renderEndCall = () => { - const { - call, - chatRoom, - peers, - recorderCid, - onRecordingToggle, - onCallEnd - } = this.props; - return react1().createElement("div", { - ref: this.endContainerRef, - className: "end-call-container", - onClick: () => { - if (chatRoom.type !== 'private' && peers.length && _call_jsx7__.Ay.isModerator(chatRoom, u_handle)) { - return this.setState(state => ({ - endCallOptions: !state.endCallOptions - }), () => { - if (this.endButtonRef) { - $(this.endButtonRef.current).trigger('simpletipClose'); - } - this.setActiveElement(); - }); - } - if (recorderCid && recorderCid === call.sfuClient.cid) { - return chatRoom.type === 'private' ? renderEndConfirm(onCallEnd, onRecordingToggle) : renderLeaveConfirm(onCallEnd, onRecordingToggle); - } - return onCallEnd(); - } - }, react1().createElement(_ui_utils_jsx10__.Ay.RenderTo, { - element: document.body - }, this.renderEndCallOptions()), react1().createElement(_button_jsx3__.A, { - simpletip: { - ...this.SIMPLETIP, - label: l[5884] - }, - className: "mega-button theme-dark-forced round negative end-call call-action", - icon: "icon-phone-02", - didMount: button => { - this.endButtonRef = button.buttonRef; - } - }), react1().createElement("span", null, l.end_button)); - }; - this.renderSourceOpener = ({ - type, - eventId - }) => { - return react1().createElement("div", { - className: ` - input-source-opener - button - ${this.state[type] ? 'active-dropdown' : ''} - `, - onClick: async ev => { - ev.stopPropagation(); - this.setState(() => ({ - loading: true - }), async () => { - const devices = await this.updateMediaDevices(); - const updated = JSON.stringify(devices) !== JSON.stringify(this.state.devices); - this.setState(state => ({ - loading: false, - audioSelectDropdown: false, - videoSelectDropdown: false, - devices: updated ? devices : this.state.devices, - [type]: !state[type] - }), () => { - const { - audioSelectDropdown, - videoSelectDropdown - } = this.state; - this.props.setActiveElement(audioSelectDropdown || videoSelectDropdown); - eventlog(eventId); - }); - }); - } - }, react1().createElement("i", { - className: "sprite-fm-mono icon-arrow-up" - })); - }; - this.handleDeviceChange = () => { - this.micDefaultRenamed = false; - this.updateMediaDevices().always(devices => { - let _sfuClient$localAudio, _oldDevices$audioIn; - if (!this.isMounted()) { - return; + this._emojiOnActiveStateChange = this._emojiOnActiveStateChange.bind(this); + this.emojiSelected = this.emojiSelected.bind(this); + const { + message: msg + } = this.props; + if (msg instanceof Message && msg._reactions && msg.messageId.length === 11 && msg.isSentOrReceived() && !Object.hasOwnProperty.call(msg, 'reacts')) { + msg.reacts.forceLoad().then(() => { + this.addContactListenerIfMissing(this._reactionContacts()); + }).catch(dump.bind(null, `reactions.load.${msg.messageId}`)); + } + } + UNSAFE_componentWillMount() { + if (super.UNSAFE_componentWillMount) { + super.UNSAFE_componentWillMount(); + } + const {chatRoom} = this.props; + if (chatRoom) { + chatRoom.rebind(`onChatShown.${ this.__cmmId}`, () => { + if (!this._contactChangeListeners) { + this.addContactListeners(); } - const { - devices: oldDevices - } = this.state; - const { - sfuClient, - av - } = this.props.call; - if (av & Av.Audio && !SfuClient.micDeviceId && ((_sfuClient$localAudio = sfuClient.localAudioTrack()) == null ? void 0 : _sfuClient$localAudio.getCapabilities().deviceId) === 'default' && oldDevices != null && (_oldDevices$audioIn = oldDevices.audioIn) != null && _oldDevices$audioIn.default && devices.audioIn.default && oldDevices.audioIn.default !== devices.audioIn.default) { - for (const [key, value] of Object.entries(devices.audioIn)) { - if (key !== 'default' && devices.audioIn.default.indexOf(value) > -1) { - sfuClient.setMicDevice(key).then(() => SfuClient.persistMicDevice(null)); - break; - } - } + }).rebind(`onChatHidden.${ this.__cmmId}`, () => { + if (this._contactChangeListeners) { + this.removeContactListeners(); } - this.setState({ - devices, - audioSelectDropdown: false, - videoSelectDropdown: false - }, this.setActiveElement); }); + } + this.addContactListeners(); + } + haveMeetingsCall() { + return document.querySelector('.meetings-call') && document.querySelector('.chat-opened'); + } + removeContactListeners() { + const users = this._contactChangeListeners; + if (d > 3) { + console.warn('%s.removeContactListeners', this.getReactId(), [this], users); + } + for (let i = users.length; i--;) { + users[i].removeEventHandler(this); + } + this._contactChangeListeners = false; + } + _reactionContacts() { + const { + message + } = this.props; + const { + reacts + } = message; + const handles = []; + const reactions = Object.values(reacts.reactions); + for (let i = 0; i < reactions.length; i++) { + handles.push(...Object.keys(reactions[i])); + } + this._reactionContactHandles = array.unique(handles); + return this._reactionContactHandles; + } + addContactListeners() { + const users = this._contactChangeListeners || []; + const addUser = user => { + if (user instanceof MegaDataMap && users.indexOf(user) < 0) { + users.push(user); + } }; - this.renderOnboardingRaise = () => { - const { - chatRoom, - onOnboardingRaiseDismiss - } = this.props; - return react1().createElement("div", { - className: "meetings-call-onboarding" - }, react1().createElement("div", { - className: "mega-dialog mega-onboarding-dialog dialog-template-message onboarding-raise", - id: "ob-dialog", - role: "dialog", - "aria-labelledby": "ob-dialog-title", - "aria-modal": "true" - }, react1().createElement("i", { - className: "sprite-fm-mono icon-tooltip-arrow tooltip-arrow bottom", - id: "ob-dialog-arrow" - }), react1().createElement("header", null, react1().createElement("div", null, react1().createElement("h2", { - id: "ob-dialog-title" - }, l.raise_onboarding_title), react1().createElement("p", { - id: "ob-dialog-text" - }, chatRoom.isMeeting ? l.raise_onboarding_body : l.raise_onboarding_group_body))), react1().createElement("footer", null, react1().createElement("div", { - className: "footer-container" - }, react1().createElement("button", { - className: "mega-button js-next small theme-light-forced", - onClick: onOnboardingRaiseDismiss - }, react1().createElement("span", null, l.ok_button)))))); - }; - this.renderRaiseButton = () => { - const { - call, - raisedHandPeers, - onboardingRaise - } = this.props; - const isOnHold = call.av & Av.onHold; - const hasRaisedHand = raisedHandPeers.includes(u_handle); - return react1().createElement("li", { - className: isOnHold ? 'disabled' : '' - }, onboardingRaise && this.renderOnboardingRaise(), react1().createElement(_button_jsx3__.A, { - className: ` - mega-button - theme-light-forced - call-action - round - ${isOnHold ? 'disabled' : ''} - ${hasRaisedHand ? 'with-fill' : ''} - `, - icon: "icon-raise-hand", - onClick: isOnHold ? null : () => { - if (hasRaisedHand) { - call.sfuClient.lowerHand(); - eventlog(500311); - return; - } - call.sfuClient.raiseHand(); - eventlog(500249); + addUser(this.getContact()); + if (this.haveMoreContactListeners) { + const moreIds = this.haveMoreContactListeners(); + if (moreIds) { + for (let i = moreIds.length; i--;) { + const handle = moreIds[i]; + addUser(handle in M.u && M.u[handle]); } - }), react1().createElement("span", null, l.raise_button)); - }; + } + } + for (let i = this._reactionContactHandles.length; i--;) { + addUser(this._reactionContactHandles[i] in M.u && M.u[this._reactionContactHandles[i]]); + } + if (d > 3) { + console.warn('%s.addContactListeners', this.getReactId(), [this], users); + } + for (let i = users.length; i--;) { + users[i].addChangeListener(this); + } + this._contactChangeListeners = users; + } + addContactListenerIfMissing(contacts) { + if (!this._contactChangeListeners) { + return false; + } + if (!Array.isArray(contacts)) { + contacts = [contacts]; + } + const added = []; + for (let i = 0; i < contacts.length; i++) { + const user = M.u[contacts[i]]; + if (user && !this._contactChangeListeners.includes(user)) { + this._contactChangeListeners.push(user); + user.addChangeListener(this); + added.push(user.h); + } + } + if (d > 1) { + console.warn('%s.addContactListenerIfMissing', this.getReactId(), [this], added); + } + } + eventuallyUpdate() { + super.eventuallyUpdate(); + this.__cmmUpdateTickCount = -2; } - renderSoundDropdown() { - const { - call - } = this.props; - const { - micDeviceId, - audioOutDeviceId - } = SfuClient; - const { - audioIn = {}, - audioOut = {} - } = this.state.devices; - let selectedIn; - const inTrack = call.sfuClient.localAudioTrack(); - if (inTrack) { - const { - deviceId - } = inTrack.getCapabilities(); - selectedIn = deviceId in audioIn ? deviceId : 'default'; - if (deviceId === 'default' && inTrack.label !== audioIn.default) { - this.micDefaultRenamed = inTrack.label; - } - } else if (micDeviceId) { - selectedIn = micDeviceId in audioIn ? micDeviceId : 'default'; + handleChangeEvent(x, z, k) { + if (k === 'ts' || k === 'ats') { + return; + } + if (this.isComponentEventuallyVisible()) { + if (++this.__cmmUpdateTickCount > 5) { + this.eventuallyUpdate(); + delay.cancel(this.__cmmId); + } else { + delay(this.__cmmId, () => this.eventuallyUpdate(), 90); + } } else { - selectedIn = 'default'; + this._requiresUpdateOnResize = true; } - if (this.micDefaultRenamed) { - audioIn.default = this.micDefaultRenamed; + } + componentWillUnmount() { + super.componentWillUnmount(); + const {chatRoom} = this.props; + if (chatRoom) { + chatRoom.off(`onChatShown.${ this.__cmmId}`).off(`onChatHidden.${ this.__cmmId}`); } - let selectedOut; - let peerPlayer; - if (call.sfuClient.peers.size) { - peerPlayer = call.sfuClient.peers.values().next().audioPlayer; + if (this._contactChangeListeners) { + this.removeContactListeners(); } - if (peerPlayer && peerPlayer.playerElem && peerPlayer.playerElem.sinkId) { - const { - sinkId - } = peerPlayer.playerElem; - selectedOut = sinkId in audioOut ? sinkId : 'default'; - } else if (audioOutDeviceId) { - selectedOut = audioOutDeviceId in audioOut ? audioOutDeviceId : 'default'; - } else { - selectedOut = 'default'; + } + getContact() { + if (this.props.contact) { + return this.props.contact; } - const mics = Object.entries(audioIn).map(([id, name]) => { - return react1().createElement(_ui_dropdowns_jsx9__.DropdownItem, { - key: id, - onClick: () => { - call.sfuClient.setMicDevice(id === 'default' ? null : id); - this.setState({ - audioSelectDropdown: false - }, this.setActiveElement); - } - }, react1().createElement(react1().Fragment, null, react1().createElement("div", { - className: "av-device-name" - }, name), selectedIn === id && react1().createElement("i", { - className: "sprite-fm-mono icon-check-small-regular-outline" - }))); - }); - const speakers = Object.entries(audioOut).map(([id, name]) => { - return react1().createElement(_ui_dropdowns_jsx9__.DropdownItem, { - key: id, - onClick: () => { - Promise.resolve(call.sfuClient.setAudioOutDevice(id === 'default' ? null : id)).catch(dump); - this.setState({ - audioSelectDropdown: false - }, this.setActiveElement); - } - }, react1().createElement(react1().Fragment, null, react1().createElement("div", { - className: "av-device-name" - }, name), selectedOut === id && react1().createElement("i", { - className: "sprite-fm-mono icon-check-small-regular-outline" - }))); - }); - return react1().createElement(_ui_dropdowns_jsx9__.Dropdown, { - className: "input-sources audio-sources theme-dark-forced", - active: true, - noArrow: true, - positionMy: "center top", - positionAt: "center bottom", - horizOffset: -50, - vertOffset: 16, - closeDropdown: () => this.setState({ - audioSelectDropdown: false - }, this.setActiveElement) - }, react1().createElement("div", { - className: "source-label" - }, l.microphone), mics.length ? mics : react1().createElement(_ui_dropdowns_jsx9__.DropdownItem, { - label: l.no_mics - }), react1().createElement("hr", null), react1().createElement("div", { - className: "source-label" - }, l.speaker), speakers.length ? speakers : react1().createElement(_ui_dropdowns_jsx9__.DropdownItem, { - label: l.no_speakers - }), react1().createElement("hr", null), react1().createElement(_ui_dropdowns_jsx9__.DropdownItem, { - icon: "sprite-fm-mono icon-volume-max-small-regular-outline", - label: l.test_speaker, - disabled: speakers.length === 0, - onClick: () => { - delay('call-test-speaker', () => { - this.testAudioOut().catch(ex => { - console.error('Failed to test audio on the selected device', ex, audioOutDeviceId); - }); - }); - } - })); + const {message} = this.props; + return Message.getContactForMessage(message); + } + getTimestampAsString() { + return toLocaleTime(this.getTimestamp()); } - renderVideoDropdown() { + getTimestamp(forUpdated) { const { - call + message } = this.props; - const { - videoIn = {} - } = this.state.devices; - const { - camDeviceId - } = SfuClient; - let selectedCam; - if (call.sfuClient.localCameraTrack()) { - const { - deviceId - } = call.sfuClient.localCameraTrack().getCapabilities(); - selectedCam = deviceId in videoIn ? deviceId : 'default'; - } else if (camDeviceId) { - selectedCam = camDeviceId in videoIn ? camDeviceId : 'default'; - } else { - selectedCam = 'default'; + const timestamp = (message.getDelay == null ? void 0 : message.getDelay()) || message.delay || unixtime(); + return forUpdated && message.updated > 0 ? timestamp + message.updated : timestamp; + } + componentDidUpdate() { + const self = this; + const {chatRoom} = self.props.message; + if (!self.onAfterRenderWasTriggered) { + const msg = self.props.message; + let shouldRender = true; + if (msg.isManagement && msg.isManagement() === true && msg.isRenderableManagement() === false) { + shouldRender = false; + } + if (shouldRender) { + chatRoom.trigger("onAfterRenderMessage", self.props.message); + self.onAfterRenderWasTriggered = true; + } } - const cameras = Object.entries(videoIn).map(([id, name]) => { - return react1().createElement(_ui_dropdowns_jsx9__.DropdownItem, { - key: id, - onClick: () => { - call.sfuClient.setCameraDevice(id === 'default' ? null : id); - this.setState({ - videoSelectDropdown: false - }, this.setActiveElement); - } - }, react1().createElement(react1().Fragment, null, react1().createElement("div", { - className: "av-device-name" - }, name), selectedCam === id && react1().createElement("i", { - className: "sprite-fm-mono icon-check-small-regular-outline" - }))); + } + getCurrentUserReactions() { + const { + reactions + } = this.props.message.reacts; + return Object.keys(reactions).filter(utf => { + let _reactions$utf; + return (_reactions$utf = reactions[utf]) == null ? void 0 : _reactions$utf[u_handle]; }); - return react1().createElement(_ui_dropdowns_jsx9__.Dropdown, { - className: "input-sources video-sources theme-dark-forced", - active: true, - noArrow: true, - positionMy: "center top", - positionAt: "center bottom", - horizOffset: -50, - vertOffset: 16, - closeDropdown: () => this.setState({ - videoSelectDropdown: false - }, this.setActiveElement) - }, react1().createElement("div", { - className: "source-label" - }, l.camera_button), cameras.length ? cameras : react1().createElement(_ui_dropdowns_jsx9__.DropdownItem, { - label: l.no_cameras - })); } - async updateMediaDevices() { - let devices = await SfuClient.enumMediaDevices().catch(dump); - devices = devices || { - audioIn: {}, - audioOut: {}, - videoIn: {} + emojiSelected(e, slug, meta) { + const { + chatRoom, + message, + onEmojiBarChange + } = this.props; + if (chatRoom.isReadOnly()) { + return false; + } + const { + reactions + } = this.props.message.reacts; + const CURRENT_USER_REACTIONS = this.getCurrentUserReactions().length; + const REACTIONS_LIMIT = { + TOTAL: 50, + PER_PERSON: 24 }; - const removeEmptyDevices = devices => { - for (const key of Object.keys(devices)) { - if (!key || !devices[key]) { - delete devices[key]; - } + const addReaction = () => chatRoom.messagesBuff.userAddReaction(message.messageId, slug, meta); + const emoji = megaChat._emojiData.emojisSlug[slug] || meta; + if (emoji && message.reacts.getReaction(u_handle, emoji.u)) { + if (onEmojiBarChange && Object.keys(reactions).length === 1 && Object.keys(reactions[emoji.u]).length === 1) { + onEmojiBarChange(false); } - }; - removeEmptyDevices(devices.audioIn); - removeEmptyDevices(devices.audioOut); - removeEmptyDevices(devices.videoIn); - if (devices.audioIn.communications) { - delete devices.audioIn.communications; + return chatRoom.messagesBuff.userDelReaction(message.messageId, slug, meta); } - return devices; - } - async testAudioOut() { - if (!SfuClient.audioOutDeviceId) { - return megaChat.playSound(megaChat.SOUNDS.SPEAKER_TEST); + if (emoji && reactions[emoji.u] && CURRENT_USER_REACTIONS < REACTIONS_LIMIT.PER_PERSON) { + return addReaction(); } - const currentDevices = await this.updateMediaDevices(); - if (currentDevices.audioOut && !(SfuClient.audioOutDeviceId in currentDevices.audioOut)) { - return megaChat.playSound(megaChat.SOUNDS.SPEAKER_TEST); + if (CURRENT_USER_REACTIONS >= REACTIONS_LIMIT.PER_PERSON) { + return msgDialog('info', '', l[24205].replace('%1', REACTIONS_LIMIT.PER_PERSON)); } - const ctx = new AudioContext({ - sinkId: SfuClient.audioOutDeviceId - }); - if (ctx.state !== 'running') { - throw new Error('The audio context failed to start'); - } - const soundBuffer = await megaChat.fetchSoundBuffer(megaChat.SOUNDS.SPEAKER_TEST); - const buffer = await ctx.decodeAudioData(soundBuffer); - const gain = ctx.createGain(); - const source = ctx.createBufferSource(); - source.buffer = buffer; - source.connect(gain); - gain.connect(ctx.destination); - gain.gain.value = 0.07; - source.start(); - } - componentWillUnmount() { - super.componentWillUnmount(); - document.removeEventListener('mousedown', this.handleMousedown); - navigator.mediaDevices.removeEventListener('devicechange', this.handleDeviceChange); - this.props.chatRoom.off(`onLocalSpeechDetected.${StreamControls.NAMESPACE}`); + if (Object.keys(reactions).length >= REACTIONS_LIMIT.TOTAL) { + return msgDialog('info', '', l[24206].replace('%1', REACTIONS_LIMIT.TOTAL)); + } else if (onEmojiBarChange && Object.keys(reactions).length === 0) { + onEmojiBarChange(true); + } + return addReaction(); } - componentDidMount() { - super.componentDidMount(); - document.addEventListener('mousedown', this.handleMousedown); - navigator.mediaDevices.addEventListener('devicechange', this.handleDeviceChange); - this.props.chatRoom.rebind(`onLocalSpeechDetected.${StreamControls.NAMESPACE}`, () => this.setState({ - muteSpeak: true - }, () => this.setActiveElement(true))); + _emojiOnActiveStateChange(newVal) { + this.setState(() => { + return { + reactionsDropdownActive: newVal + }; + }); } - render() { + getEmojisImages() { const { - call, - signal, chatRoom, - renderSignalWarning, - hasToRenderPermissionsWarning, - renderPermissionsWarning, - resetError, - blocked, - renderBlockedWarning, - onAudioClick, - onVideoClick, - onScreenSharingClick, - onHoldClick + message } = this.props; - const { - audioSelectDropdown, - videoSelectDropdown, - muteSpeak - } = this.state; - const avFlags = call.av; - const isOnHold = avFlags & Av.onHold; - return react1().createElement(react1().Fragment, null, blocked && renderBlockedWarning(), react1().createElement("div", { - ref: this.domRef, - className: StreamControls.NAMESPACE - }, d && localStorage.callDebug ? this.renderDebug() : '', react1().createElement("ul", null, react1().createElement("li", { - className: ` - ${isOnHold ? 'disabled' : ''} - with-input-selector - `, - onClick: () => isOnHold ? null : this.setState({ - muteSpeak: false - }, () => { - resetError(Av.Audio); - onAudioClick(); - }) - }, muteSpeak && react1().createElement("div", { - className: "mic-muted-tip theme-light-forced", - onClick: ev => ev.stopPropagation() - }, react1().createElement("span", null, l.mic_still_muted), react1().createElement(_button_jsx3__.A, { - className: "mic-muted-tip-btn", - onClick: () => { - this.setState({ - muteSpeak: false - }, () => { - this.setActiveElement(); - eventlog(500509); - }); + const isReadOnlyClass = chatRoom.isReadOnly() ? " disabled" : ""; + let emojisImages = message._reactions && message.reacts.reactions && Object.keys(message.reacts.reactions).map(utf => { + const reaction = message.reacts.reactions[utf]; + const count = Object.keys(reaction).length; + if (!count) { + return null; } - }, l[148]), react1().createElement("i", { - className: "sprite-fm-mono icon-tooltip-arrow tooltip-arrow bottom" - })), react1().createElement(_button_jsx3__.A, { - className: ` - mega-button - theme-light-forced - call-action - round - ${isOnHold ? 'disabled' : ''} - ${avFlags & Av.Audio || isOnHold ? '' : 'with-fill'} - `, - icon: avFlags & Av.Audio ? 'icon-mic-thin-outline' : 'icon-mic-off-thin-outline' - }), react1().createElement("span", null, l.mic_button), signal ? null : renderSignalWarning(), hasToRenderPermissionsWarning(Av.Audio) ? renderPermissionsWarning(Av.Audio) : null, this.renderSourceOpener({ - type: 'audioSelectDropdown', - eventId: chatRoom.isMeeting ? 500299 : 500300 - })), audioSelectDropdown && react1().createElement("div", { - ref: this.audioDropdownRef - }, this.renderSoundDropdown()), react1().createElement("li", { - className: ` - ${isOnHold ? 'disabled' : ''} - with-input-selector - `, - onClick: () => { - if (isOnHold) { - return; + const filename = twemoji.convert.toCodePoint(utf); + const currentUserReacted = !!reaction[u_handle]; + const names = []; + if (reaction) { + ChatdIntegration._ensureContactExists(Object.keys(reaction)); + const rKeys = Object.keys(reaction); + for (let i = 0; i < rKeys.length; i++) { + const uid = rKeys[i]; + if (reaction[uid]) { + if (uid === u_handle) { + names.push(l[24071] || 'You'); + } else if (uid in M.u) { + names.push(M.getNameByHandle(uid) || megaChat.plugins.userHelper.SIMPLETIP_USER_LOADER); + } + } } - resetError(Av.Camera); - onVideoClick(); } - }, react1().createElement(_button_jsx3__.A, { - className: ` - mega-button - theme-light-forced - call-action - round - ${isOnHold ? 'disabled' : ''} - ${avFlags & Av.Camera || isOnHold ? '' : 'with-fill'} - `, - icon: avFlags & Av.Camera ? 'icon-video-thin-outline' : 'icon-video-off-thin-outline' - }), react1().createElement("span", null, l.camera_button), hasToRenderPermissionsWarning(Av.Camera) ? renderPermissionsWarning(Av.Camera) : null, this.renderSourceOpener({ - type: 'videoSelectDropdown', - eventId: chatRoom.isMeeting ? 500301 : 500302 - })), videoSelectDropdown && react1().createElement("div", { - ref: this.videoDropdownRef - }, this.renderVideoDropdown()), react1().createElement("li", { - className: isOnHold ? 'disabled' : '', - onClick: () => { - if (isOnHold) { - return; + let emojiData = megaChat._emojiData.emojisUtf[utf]; + if (!emojiData) { + emojiData = Object.create(null); + emojiData.u = utf; + } + let slug = emojiData && emojiData.n || ""; + let tipText; + slug = slug ? `:${slug}:` : utf; + if (Object.keys(reaction).length === 1 && reaction[u_handle]) { + tipText = (l[24068] || "You (click to remove) [G]reacted with %s[/G]").replace("%s", slug); + } else { + tipText = mega.utils.trans.listToString(names, (l[24069] || "%s [G]reacted with %s2[/G]").replace("%s2", slug)); + } + const notFoundEmoji = slug && slug[0] !== ":"; + return JSX_("div", { + key: slug, + onClick: ((e, slug, meta) => () => this.emojiSelected(e, slug, meta))(null, slug, emojiData), + className: ` + reactions-bar__reaction + simpletip + ${currentUserReacted ? 'user-reacted' : ''} + ${notFoundEmoji ? 'emoji-loading-error' : ''} + ${isReadOnlyClass} + `, + "data-simpletip": tipText, + "data-simpletipoffset": "3", + "data-simpletipposition": "top" + }, JSX_("img", { + width: "10", + height: "10", + className: "emoji emoji-loading", + draggable: "false", + onError: e => { + const textNode = document.createElement("em"); + textNode.classList.remove('emoji-loading'); + textNode.append(document.createTextNode(utf)); + e.target.replaceWith(textNode); + textNode.parentNode.classList.add('emoji-loading-error'); + }, + onLoad: e => { + e.target.classList.remove('emoji-loading'); + }, + src: `${staticpath }images/mega/twemojis/2_v2/72x72/${ filename }.png` + }), JSX_("span", { + className: "message text-block" + }, count)); + }); + emojisImages = emojisImages && emojisImages.filter((v) => { + return !!v; + }); + if (emojisImages && emojisImages.length > 0) { + const reactionBtn = !chatRoom.isReadOnly() ? JSX_(_ui_buttons_jsx2__ .$, { + className: "popup-button reactions-button hover-colorized simpletip", + icon: "sprite-fm-theme icon-emoji-reactions reactions-icon", + disabled: false, + key: "add-reaction-button", + attrs: { + 'data-simpletip': l[24070] || "Add reaction...", + 'data-simpletipoffset': "3", + 'data-simpletipposition': "top" } - resetError(Av.Screen); - onScreenSharingClick(); - if (chatRoom.isMeeting) { - eventlog(500303); - } else { - eventlog(500304); + }, JSX_(_ui_emojiDropdown_jsx3__ .A, { + horizOffset: this.haveMeetingsCall() ? -150 : 0, + onActiveChange: this._emojiOnActiveStateChange, + className: "popup emoji reactions-dropdown", + onClick: this.emojiSelected + })) : null; + emojisImages.push(reactionBtn); + } + return emojisImages ? JSX_("div", { + className: "reactions-bar", + id: "reactions-bar", + onMouseEnter: () => { + if (this._loadedReacts) { + return false; } + this._loadedReacts = megaChat.plugins.userHelper.fetchAllNames(this._reactionContacts(), chatRoom).catch(dump).finally(() => { + this._loadedReacts = true; + this.safeForceUpdate(); + }); } - }, react1().createElement(_button_jsx3__.A, { - className: ` - mega-button - theme-light-forced - call-action - round - ${isOnHold ? 'disabled' : ''} - ${avFlags & Av.Screen ? 'with-fill' : ''} - `, - icon: avFlags & Av.Screen ? 'icon-monitor-off' : 'icon-monitor' - }), react1().createElement("span", null, avFlags & Av.Screen ? l.screenshare_stop_button : l.screenshare_button), hasToRenderPermissionsWarning(Av.Screen) ? renderPermissionsWarning(Av.Screen, this) : null), chatRoom.type === 'private' ? null : this.renderRaiseButton(), react1().createElement("li", { - onClick: onHoldClick - }, react1().createElement(_button_jsx3__.A, { - className: ` - mega-button - theme-light-forced - call-action - round - ${isOnHold ? 'with-fill' : ''} - `, - icon: isOnHold ? 'icon-play-small-regular-outline' : 'icon-pause-small-regular-outline' - }), react1().createElement("span", null, isOnHold ? l.resume_call_button : l.hold_button)), react1().createElement("li", null, this.renderEndCall())))); + }, emojisImages) : null; + } + getMessageActionButtons() { + const { + chatRoom, + message + } = this.props; + return message instanceof Message && message.isSentOrReceived() && !chatRoom.isReadOnly() ? JSX_(_ui_buttons_jsx2__ .$, { + className: "popup-button reactions-button tiny-button simpletip", + icon: `${"sprite-fm-theme reactions-icon"} icon-emoji-reactions`, + iconHovered: `${"sprite-fm-theme reactions-icon"} icon-emoji-reactions-active`, + disabled: false, + key: "add-reaction-button", + attrs: { + 'data-simpletip': l[24070] || "Add reaction...", + 'data-simpletipoffset': "3", + 'data-simpletipposition': "top" + } + }, JSX_(_ui_emojiDropdown_jsx3__ .A, { + horizOffset: this.haveMeetingsCall() ? -110 : 0, + noArrow: true, + onActiveChange: this._emojiOnActiveStateChange, + className: "popup emoji reactions-dropdown", + onClick: this.emojiSelected + })) : null; } } -StreamControls.NAMESPACE = 'stream-controls'; -const __WEBPACK_DEFAULT_EXPORT__ = (0,_mixins2__.Zz)(_micObserver_jsx5__.Q, _permissionsObserver_jsx6__.$)(StreamControls); -}, -414 + }, + + 5470 (_, EXP_, REQ_) { "use strict"; -REQ_.d(EXP_, { -Cn: () => LocalVideoHiRes, -Gz: () => AudioLevelIndicator, -Qs: () => PeerVideoThumbFixed, -Vm: () => LocalVideoHiResCloned, -au: () => PeerVideoThumb, -bJ: () => LocalVideoThumb, -ob: () => PeerVideoHiResCloned, -zu: () => PeerVideoHiRes -}); -const react0__ = REQ_(594); -const react0 = REQ_.n(react0__); -const _mixins1__ = REQ_(137); -const _contacts_jsx2__ = REQ_(251); -const _call_jsx3__ = REQ_(3); -const _ui_utils4__ = REQ_(314); + REQ_.d(EXP_, { + A: () => ScheduleMetaChange + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _mixin_jsx1__ = REQ_(855); + const _contacts_jsx2__ = REQ_(8022); + const _ui_utils_jsx3__ = REQ_(6411); + const _ui_buttons_jsx4__ = REQ_(5155); -class VideoNode extends _mixins1__.w9 { - constructor(props, source) { - super(props); - this.domRef = react0().createRef(); - this.contRef = react0().createRef(); - this.audioLevelRef = react0().createRef(); - this.statsHudRef = react0().createRef(); - this.raisedHandListener = undefined; +class ScheduleMetaChange extends _mixin_jsx1__ .M { + constructor(...args) { + super(...args); this.state = { - raisedHandPeers: [] + link: '' }; - this.source = source; - this.state.raisedHandPeers = this.props.raisedHandPeers || []; } componentDidMount() { - let _this$props$didMount, _this$props, _this$domRef; super.componentDidMount(); - this.source.registerConsumer(this); - (_this$props$didMount = (_this$props = this.props).didMount) == null || _this$props$didMount.call(_this$props, (_this$domRef = this.domRef) == null ? void 0 : _this$domRef.current); - this.requestVideo(true); - this.raisedHandListener = mBroadcaster.addListener('meetings:raisedHand', raisedHandPeers => this.setState({ - raisedHandPeers - }, () => this.safeForceUpdate())); - } - onVisibilityChange(isVisible) { - this.requestVideo(isVisible); - } - componentDidUpdate() { - super.componentDidUpdate(); - if (this.props.didUpdate) { - let _this$domRef2; - this.props.didUpdate((_this$domRef2 = this.domRef) == null ? void 0 : _this$domRef2.current); - } - this.requestVideo(); - } - onAvChange() { - this.safeForceUpdate(); - } - displayVideoElement(video, container) { - this.attachVideoElemHandlers(video); - this.video = video; - container.replaceChildren(video); - } - attachVideoElemHandlers(video) { - if (video._snSetup) { - return; + if (this.props.mode === ScheduleMetaChange.MODE.CREATED) { + if (is_chatlink) { + this.setState({ + link: `${getBaseUrl()}/chat/${is_chatlink.ph}#${is_chatlink.key}` + }); + } else { + const { + chatRoom + } = this.props; + chatRoom.updatePublicHandle().then(() => { + if (this.isMounted() && !this.state.link && chatRoom.publicLink) { + this.setState({ + link: `${getBaseUrl()}/${chatRoom.publicLink}` + }); + } + }).catch(dump); + } } - video.autoplay = true; - video.controls = false; - video.muted = true; - video.ondblclick = e => { + if (this.props.message.meta.ap) { const { - onDoubleClick, - toggleFullScreen - } = this.props; - onDoubleClick == null || onDoubleClick(this.source, e); - if (toggleFullScreen && !document.fullscreenElement && this.domRef.current) { - if (typeof toggleFullScreen === 'function') { - toggleFullScreen(this); - } - this.domRef.current.requestFullscreen({ - navigationUI: 'hide' + meetingsManager + } = megaChat.plugins; + this.redrawListener = `${meetingsManager.EVENTS.OCCURRENCES_UPDATE}.redraw${this.getUniqueId()}`; + megaChat.rebind(this.redrawListener, () => { + onIdle(() => { + const { + meta + } = this.props.message; + if (!meta.ap) { + return; + } + this.props.message.meta = meetingsManager.noCsMeta(meta.handle, meta.ap, megaChat.chats[meta.cid]); + this.safeForceUpdate(); }); - } - }; - video.onloadeddata = ev => { - if (this.props.onLoadedData) { - this.props.onLoadedData(ev); - } - }; - video._snSetup = true; + megaChat.off(this.redrawListener); + delete this.redrawListener; + }); + } } componentWillUnmount() { super.componentWillUnmount(); - delete this.video; - this.detachVideoElemHandlers(); - this.source.deregisterConsumer(this); - mBroadcaster.removeListener(this.raisedHandListener); - if (this.props.willUnmount) { - this.props.willUnmount(); - } - } - detachVideoElemHandlers() { - let _this$contRef$current; - const video = (_this$contRef$current = this.contRef.current) == null ? void 0 : _this$contRef$current.firstChild; - if (!video || !video._snSetup) { - return; - } - video.onloadeddata = null; - video.ondblclick = null; - delete video._snSetup; - } - isVideoCropped() { - let _this$video; - return (_this$video = this.video) == null ? void 0 : _this$video.classList.contains("video-crop"); - } - cropVideo() { - let _this$video2; - (_this$video2 = this.video) == null || _this$video2.classList.add("video-crop"); - } - uncropVideo() { - let _this$video3; - (_this$video3 = this.video) == null || _this$video3.classList.remove("video-crop"); - } - displayStats(stats) { - const elem = this.statsHudRef.current; - if (!elem) { - return; + if (this.redrawListener) { + megaChat.off(this.redrawListener); } - elem.textContent = stats ? `${stats} (${this.ownVideo ? "cloned" : "ref"})` : ""; } - renderVideoDebugMode() { - if (this.source.isFake) { - return null; - } - let className = "video-rtc-stats"; - let title; - if (this.isLocal) { - if (window.sfuClient) { - title = new URL(window.sfuClient.url).host; - } - if (this.props.isSelfOverlay) { - className += " video-rtc-stats-ralign"; - } - } - if (!title) { - title = ""; + specShouldComponentUpdate(nextProps) { + if (this.props.mode === ScheduleMetaChange.MODE.CREATED && this.props.link !== nextProps.link) { + return true; } - return react0().createElement("div", { - ref: this.statsHudRef, - className, - title - }); + return null; } - renderContent() { - const { - source, - contRef - } = this; - if (this.props.isPresenterNode || source.av & Av.Camera) { - return react0().createElement("div", { - ref: contRef, - className: "video-node-holder video-node-loading" + componentDidUpdate(prevProps) { + if (this.props.mode === ScheduleMetaChange.MODE.CREATED && prevProps.link !== this.props.link) { + this.setState({ + link: this.props.link ? `${getBaseUrl()}/${this.props.link}` : '' }); } - delete this._lastResizeHeight; - return react0().createElement(_contacts_jsx2__.Avatar, { - contact: M.u[source.userHandle] - }); - } - getStatusIcon(icon, label) { - return react0().createElement("span", { - className: "simpletip", - "data-simpletip-class": "theme-dark-forced", - "data-simpletipposition": "top", - "data-simpletipoffset": "5", - "data-simpletip": label - }, react0().createElement("i", { - className: icon - })); - } - renderStatus() { - const { - chatRoom, - isPresenterNode, - minimized - } = this.props; - const { - raisedHandPeers - } = this.state; - const { - source - } = this; - const { - sfuClient - } = chatRoom.call; - const { - userHandle, - isOnHold - } = source; - const $$CONTAINER = ({ - children - }) => react0().createElement("div", { - className: "video-node-status theme-dark-forced" - }, children); - const name = react0().createElement("div", { - className: "video-status-name" - }, isPresenterNode ? react0().createElement(_ui_utils4__.zT, null, l.presenter_nail.replace('%s', M.getNameByHandle(userHandle))) : react0().createElement(_contacts_jsx2__.ContactAwareName, { - contact: M.u[userHandle], - emoji: true - })); - if (isOnHold) { - return react0().createElement($$CONTAINER, null, name, this.getStatusIcon('sprite-fm-mono icon-pause', l[23542].replace('%s', M.getNameByHandle(userHandle) || megaChat.plugins.userHelper.SIMPLETIP_USER_LOADER))); - } - return react0().createElement(react0().Fragment, null, !minimized && react0().createElement("div", { - className: "stream-signifiers" - }, raisedHandPeers && raisedHandPeers.length && raisedHandPeers.includes(userHandle) ? this.getStatusIcon('sprite-fm-uni stream-signifier-icon icon-raise-hand') : null), react0().createElement($$CONTAINER, null, name, react0().createElement(AudioLevelIndicator, { - source - }), sfuClient.haveBadNetwork ? this.getStatusIcon('sprite-fm-mono icon-call-offline', l.poor_connection) : null)); } - render() { + onAddToCalendar() { const { - mode, - chatRoom, - simpletip, - className, - children, - onClick + chatRoom } = this.props; const { - domRef, - source, - isLocal, - isLocalScreen - } = this; - if (!chatRoom.call) { - return null; - } - const { - call - } = chatRoom; - const isActiveSpeaker = !source.audioMuted && call.speakerCid === source.clientId; - return react0().createElement("div", { - ref: domRef, - className: ` - video-node - ${onClick ? 'clickable' : ''} - ${className || ''} - ${isLocal && !isLocalScreen ? ' local-stream-mirrored' : ''} - ${simpletip ? 'simpletip' : ''} - ${isActiveSpeaker && mode === _call_jsx3__.g.THUMBNAIL ? 'active-speaker' : ''} - `, - "data-simpletip": simpletip == null ? void 0 : simpletip.label, - "data-simpletipposition": simpletip == null ? void 0 : simpletip.position, - "data-simpletipoffset": simpletip == null ? void 0 : simpletip.offset, - "data-simpletip-class": simpletip == null ? void 0 : simpletip.className, - onClick: evt => onClick == null ? void 0 : onClick(source, evt) - }, source && react0().createElement(react0().Fragment, null, children || null, react0().createElement("div", { - className: "video-node-content" - }, CallManager2.Call.VIDEO_DEBUG_MODE ? this.renderVideoDebugMode() : null, this.renderContent(), this.renderStatus()))); - } -} -class DynVideo extends VideoNode { - onAvChange() { - this._lastResizeHeight = null; - super.onAvChange(); - } - dynRequestVideo() { - const { - source, - domRef - } = this; - if (source.isFake || source.isDestroyed) { - return; - } - if (source.isStreaming() && this.isMounted()) { - const node = domRef == null ? void 0 : domRef.current; - this.dynRequestVideoBySize(node.offsetHeight); - } else { - this.dynRequestVideoBySize(0); - this.displayStats(null); - } - } - dynRequestVideoQuality(quality) { - this.requestedQ = quality && CallManager2.FORCE_LOWQ ? 1 : quality; - if (!this.source.dynUpdateVideoQuality()) { - this.dynUpdateVideoElem(); - } - } - dynRequestVideoBySize(h) { - if (h === 0) { - this._lastResizeHeight = 0; - this.dynRequestVideoQuality(CallManager2.VIDEO_QUALITY.NO_VIDEO); - return; - } - if (this.contRef.current) { - if (this._lastResizeHeight === h) { - return; - } - this._lastResizeHeight = h; - } else { - this._lastResizeHeight = null; - } - let newQ; - if (h > 360) { - newQ = CallManager2.VIDEO_QUALITY.HIGH; - } else if (h > 180) { - newQ = CallManager2.VIDEO_QUALITY.MEDIUM; - } else if (h > 90 || this.noThumb) { - newQ = CallManager2.VIDEO_QUALITY.LOW; - } else { - newQ = CallManager2.VIDEO_QUALITY.THUMB; - } - this.dynRequestVideoQuality(newQ); - } - dynUpdateVideoElem() { - let _this$source$hiResPla; - const vidCont = this.contRef.current; - if (!this.isMounted() || !vidCont) { - return; - } - const player = this.noThumb ? (_this$source$hiResPla = this.source.hiResPlayer) == null || (_this$source$hiResPla = _this$source$hiResPla.gui) == null ? void 0 : _this$source$hiResPla.video : this.source.player; - if (!player) { - vidCont.replaceChildren(); - return; - } - this.dynSetVideoSource(player, vidCont); - } -} -class DynVideoDirect extends DynVideo { - constructor(props, source) { - super(props, source); - this.isDirect = true; - this.requestVideo = this.dynRequestVideo; - } - dynSetVideoSource(srcPlayer, vidCont) { - if (vidCont.firstChild !== srcPlayer) { - this.displayVideoElement(srcPlayer, vidCont); - } - if (srcPlayer.paused) { - srcPlayer.play().catch(nop); - } - } -} -class PeerVideoHiRes extends DynVideoDirect { - constructor(props) { - super(props, props.source); - } -} -class DynVideoCloned extends DynVideo { - constructor(props, source) { - super(props, source); - this.ownVideo = CallManager2.createVideoElement(); - } - dynSetVideoSource(srcPlayer, vidCont) { - const cloned = this.ownVideo; - const currVideo = vidCont.firstChild; - if (!currVideo) { - this.displayVideoElement(cloned, vidCont); - } else { - assert(currVideo === cloned); - } - if (cloned.paused || cloned.srcObject !== srcPlayer.srcObject) { - cloned.srcObject = srcPlayer.srcObject; - Promise.resolve(cloned.play()).catch(nop); - } - } -} -class PeerVideoThumb extends DynVideoCloned { - constructor(props) { - super(props, props.source); - this.requestVideo = this.dynRequestVideo; - } -} -class PeerVideoThumbFixed extends VideoNode { - constructor(props) { - super(props, props.source); - assert(props.source.hasScreenAndCam); - this.ownVideo = CallManager2.createVideoElement(); - if (CallManager2.Call.VIDEO_DEBUG_MODE) { - this.onRxStats = this._onRxStats; - } - } - addVideo() { - assert(this.source.hasScreenAndCam); - const vidCont = this.contRef.current; - assert(vidCont); - if (vidCont.firstChild !== this.ownVideo) { - this.displayVideoElement(this.ownVideo, vidCont); - } - } - delVideo() { - SfuClient.playerStop(this.ownVideo); - const vidCont = this.contRef.current; - if (!vidCont) { - return; - } - vidCont.replaceChildren(); - } - requestVideo(forceVisible) { - if (!this.isComponentVisible() && !forceVisible) { - return; - } - if (this.player) { - this.playVideo(); - } else { - this.addVideo(); - this.player = this.source.sfuPeer.getThumbVideo(() => { - return this; - }); - } - } - playVideo() { - let _this$player$slot; - const track = (_this$player$slot = this.player.slot) == null ? void 0 : _this$player$slot.inTrack; - if (!track) { - return; - } - SfuClient.playerPlay(this.ownVideo, track, true); - } - attachToTrack(track) { - if (!this.source.hasScreenAndCam) { - return; - } - SfuClient.playerPlay(this.ownVideo, track); - } - detachFromTrack() { - this.delVideo(); - } - onPlayerDestroy() { - delete this.player; - } - componentWillUnmount() { - if (this.player) { - this.player.destroy(); - } - super.componentWillUnmount(); - } - _onRxStats(track, info, raw) { - if (this.player) { - this.displayStats(CallManager2.Call.rxStatsToText(track, info, raw)); - } - } -} -class PeerVideoHiResCloned extends DynVideoCloned { - constructor(props) { - super(props, props.source); - this.noThumb = true; - this.requestVideo = this.dynRequestVideo; - } -} -class LocalVideoHiResCloned extends VideoNode { - constructor(props) { - super(props, props.chatRoom.call.getLocalStream()); - this.isLocal = true; - this.ownVideo = CallManager2.createVideoElement(); - } - get isLocalScreen() { - return this.source.av & Av.Screen; - } - requestVideo(forceVisible) { - if (d > 1 && forceVisible) { - console.debug('ignoring forceVisible'); - } - const vidCont = this.contRef.current; - if (!vidCont) { - return; - } - const track = this.source.sfuClient.localScreenTrack(); - if (!track) { - vidCont.replaceChildren(); - } else { - if (vidCont.firstChild !== this.ownVideo) { - this.displayVideoElement(this.ownVideo, vidCont); - } - SfuClient.playerPlay(this.ownVideo, track, true); + id, + title + } = chatRoom && chatRoom.scheduledMeeting || {}; + if (id) { + delay(`fetchical${id}`, () => { + eventlog(500038); + asyncApiReq({ + a: 'mcsmfical', + id + }).then(([, res]) => { + delay(`saveical${id}`, () => { + M.saveAs(base64urldecode(res), `${title.replace(/\W/g, '')}.ics`).then(nop).catch(() => { + msgDialog('error', '', l.calendar_add_failed, ''); + }); + }, 1000); + }).catch(() => { + msgDialog('error', '', l.calendar_add_failed, ''); + }); + }, 250); } } -} -class LocalVideoHiRes extends DynVideoDirect { - constructor(props) { - super(props, props.chatRoom.call.getLocalStream()); - this.isLocal = true; - } - get isLocalScreen() { - return this.source.av & Av.Screen; - } -} -class LocalVideoThumb extends VideoNode { - constructor(props) { - const source = props.chatRoom.call.getLocalStream(); - super(props, source); - this.isLocal = true; - this.isLocalScreen = source.av & Av.Screen && !(source.av & Av.Camera); - this.sfuClient = props.chatRoom.call.sfuClient; - this.ownVideo = CallManager2.createVideoElement(); - } - requestVideo() { - const vidCont = this.contRef.current; - if (!vidCont) { - return; - } - const currVideo = vidCont.firstChild; - const track = this.isLocalScreen ? this.sfuClient.localScreenTrack() : this.sfuClient.localCameraTrack(); - if (!track) { - if (currVideo) { - vidCont.replaceChildren(); - } - } else { - if (!currVideo) { - this.displayVideoElement(this.ownVideo, vidCont); - } else { - assert(currVideo === this.ownVideo); - } - SfuClient.playerPlay(this.ownVideo, track, true); + static getTitleText(meta) { + const { + mode, + recurring, + occurrence, + converted, + prevTiming + } = meta; + const { + MODE + } = ScheduleMetaChange; + switch (mode) { + case MODE.CREATED: + { + return recurring ? l.schedule_mgmt_new_recur : l.schedule_mgmt_new; + } + case MODE.EDITED: + { + if (converted) { + return recurring ? l.schedule_mgmt_update_convert_recur : l.schedule_mgmt_update_convert; + } + if (occurrence) { + return l.schedule_mgmt_update_occur; + } + if (prevTiming) { + return recurring ? l.schedule_mgmt_update_recur : l.schedule_mgmt_update; + } + return l.schedule_mgmt_update_desc; + } + case MODE.CANCELLED: + { + if (recurring) { + return occurrence ? l.schedule_mgmt_cancel_occur : l.schedule_mgmt_cancel_recur; + } + return l.schedule_mgmt_cancel; + } } + return ''; } - onAvChange() { - const av = this.sfuClient.availAv; - this.isLocalScreen = av & Av.Screen && !(av & Av.Camera); - super.onAvChange(); - } -} -class AudioLevelIndicator extends react0().Component { - constructor(props) { - super(props); - this.source = props.source; - this.indicatorRef = react0().createRef(); - this.updateAudioLevel = this.updateAudioLevel.bind(this); - } - componentDidMount() { - this.source.registerVuLevelConsumer(this); - } - componentWillUnmount() { - this.source.unregisterVuLevelConsumer(this); - } - updateAudioLevel(level) { - const levelInd = this.indicatorRef.current; - if (!levelInd) { - return; + renderTimingBlock() { + const { + message, + mode + } = this.props; + const { + meta + } = message; + const { + MODE + } = ScheduleMetaChange; + if (mode === MODE.CANCELLED && !meta.occurrence) { + return null; } - level = Math.round(level * 400); - if (level > 90) { - level = 90; + const [now, prev] = megaChat.plugins.meetingsManager.getOccurrenceStrings(meta); + return JSX_("div", { + className: "schedule-timing-block" + }, meta.prevTiming && JSX_("s", null, prev || ''), now); + } + checkAndFakeOccurrenceMeta(meta) { + const { + MODE + } = ScheduleMetaChange; + if (meta.occurrence && meta.mode === MODE.CANCELLED && !meta.calendar) { + const meeting = megaChat.plugins.meetingsManager.getMeetingOrOccurrenceParent(meta.handle); + if (meeting) { + const occurrences = meeting.getOccurrencesById(meta.handle); + if (occurrences) { + meta.calendar = { + date: new Date(occurrences[0].start).getDate(), + month: time2date(Math.floor(occurrences[0].start / 1000), 12) + }; + meta.timeRules.startTime = Math.floor(occurrences[0].start / 1000); + meta.timeRules.endTime = Math.floor(occurrences[0].end / 1000); + } + } } - levelInd.style.height = `${level + 10}%`; } render() { const { - audioMuted - } = this.source; - return react0().createElement("span", { - className: "simpletip", - "data-simpletip-class": "theme-dark-forced", - "data-simpletipposition": "top", - "data-simpletipoffset": "5", - "data-simpletip": audioMuted ? l.muted : '' - }, react0().createElement("i", { - className: ` - sprite-fm-mono - ${audioMuted ? 'icon-mic-off-thin-outline inactive' : 'icon-mic-thin-outline speaker-indicator'} - ` - }, audioMuted ? null : react0().createElement("div", { - ref: this.indicatorRef, - className: "mic-fill" - }))); + chatRoom, + message, + mode, + contact + } = this.props; + const { + meta, + messageId + } = message; + const { + scheduledMeeting + } = chatRoom; + const { + MODE + } = ScheduleMetaChange; + const { + link + } = this.state; + if (meta.gone) { + return null; + } + this.checkAndFakeOccurrenceMeta(meta); + return JSX_("div", null, JSX_("div", { + className: "message body", + "data-id": `id${messageId}`, + key: messageId + }, JSX_(_contacts_jsx2__ .eu, { + contact: contact.u, + className: "message avatar-wrapper small-rounded-avatar", + chatRoom + }), JSX_("div", { + className: "message schedule-message content-area small-info-txt selectable-txt" + }, JSX_(_contacts_jsx2__ .bq, { + className: "message", + chatRoom, + contact, + label: JSX_(_ui_utils_jsx3__ .zT, null, M.getNameByHandle(contact.u)) + }), JSX_("div", { + className: "message date-time simpletip", + "data-simpletip": time2date(this.getTimestamp()) + }, this.getTimestampAsString()), JSX_("div", { + className: "message text-block" + }, ScheduleMetaChange.getTitleText(meta), " ", !!d && meta.handle), JSX_("div", { + className: "message body-block" + }, (meta.prevTiming || meta.calendar || meta.topic && meta.onlyTitle || meta.recurring) && JSX_("div", { + className: "schedule-detail-block" + }, meta.calendar && scheduledMeeting && (meta.recurring && !scheduledMeeting.recurring || meta.occurrence && meta.mode === MODE.CANCELLED || !meta.recurring) && JSX_("div", { + className: "schedule-calendar-icon" + }, JSX_("div", { + className: "schedule-date" + }, meta.calendar.date), JSX_("div", { + className: "schedule-month" + }, meta.calendar.month)), JSX_("div", { + className: "schedule-detail-main" + }, JSX_("div", { + className: "schedule-meeting-title" + }, mode === MODE.CANCELLED ? JSX_("s", null, meta.topic || chatRoom.topic) : meta.topic || chatRoom.topic), this.renderTimingBlock()), chatRoom.iAmInRoom() && scheduledMeeting && mode !== MODE.CANCELLED && JSX_(_ui_buttons_jsx4__ .$, { + className: "mega-button", + onClick: () => this.onAddToCalendar() + }, JSX_("span", null, mode === MODE.CREATED && !meta.occurrence ? l.schedule_add_calendar : l.schedule_update_calendar))), mode === MODE.CREATED && scheduledMeeting && scheduledMeeting.description && JSX_("div", { + className: "schedule-description" + }, JSX_(_ui_utils_jsx3__ .P9, null, megaChat.html(scheduledMeeting.description).replace(/\n/g, '
'))), link && JSX_("div", null, JSX_("div", { + className: "schedule-link-instruction" + }, l.schedule_mgmt_link_instruct), JSX_("div", { + className: "schedule-meeting-link" + }, JSX_("span", null, link), JSX_(_ui_buttons_jsx4__ .$, { + className: "mega-button positive", + onClick: () => { + copyToClipboard(link, l[7654]); + delay('chat-event-sm-copy-link', () => eventlog(500039)); + } + }, JSX_("span", null, l[63]))), JSX_("span", null, l.schedule_link_note)))))); } } +ScheduleMetaChange.MODE = { + CREATED: 1, + EDITED: 2, + CANCELLED: 3 +}; +window.ScheduleMetaChange = ScheduleMetaChange; -}, + }, -539 + 187 (_, EXP_, REQ_) { "use strict"; -REQ_.d(EXP_, { -Ay: () => VideoNodeMenu, -EH: () => Privilege, -yU: () => Pin -}); -const react0__ = REQ_(594); -const react0 = REQ_.n(react0__); -const _mixins1__ = REQ_(137); -const _button_jsx2__ = REQ_(959); -const _call_jsx3__ = REQ_(3); - - - - - -const Privilege = ({ - chatRoom, - stream -}) => { - const { - call, - userHandle - } = stream || {}; - if (call && call.isPublic) { - const { - OPERATOR, - FULL - } = ChatRoom.MembersSet.PRIVILEGE_STATE; - const currentUserModerator = chatRoom.members[u_handle] === OPERATOR; - const targetUserModerator = chatRoom.members[userHandle] === OPERATOR; - return currentUserModerator && react0().createElement(_button_jsx2__.A, { - targetUserModerator, - icon: "sprite-fm-mono icon-admin-outline", - onClick: () => { - ['alterUserPrivilege', 'onCallPrivilegeChange'].map(event => chatRoom.trigger(event, [userHandle, targetUserModerator ? FULL : OPERATOR])); - } - }, react0().createElement("span", null, targetUserModerator ? l.remove_moderator : l.make_moderator)); - } - return null; -}; -const Contact = ({ - stream, - ephemeralAccounts, - onCallMinimize -}) => { - const { - userHandle - } = stream; - const IS_GUEST = (0,_call_jsx3__.P)() || ephemeralAccounts && ephemeralAccounts.includes(userHandle); - const HAS_RELATIONSHIP = M.u[userHandle].c === 1; - if (HAS_RELATIONSHIP) { - return react0().createElement(_button_jsx2__.A, { - icon: "sprite-fm-mono icon-chat", - onClick: () => { - onCallMinimize(); - loadSubPage(`fm/chat/p/${userHandle}`); - } - }, react0().createElement("span", null, l[7997])); - } - return react0().createElement(_button_jsx2__.A, { - className: IS_GUEST ? 'disabled' : '', - icon: "sprite-fm-mono icon-add", - onClick: () => { - return IS_GUEST ? false : M.syncContactEmail(userHandle, true).then(email => { - const OPC = Object.values(M.opc); - if (OPC && OPC.length && OPC.some(opc => opc.m === email)) { - return msgDialog('warningb', '', l[17545]); - } - msgDialog('info', l[150], l[5898]); - M.inviteContact(M.u[u_handle].m, email); - }).catch(() => mBroadcaster.sendMessage('meetings:ephemeralAdd', userHandle)); + REQ_.d(EXP_, { + d: () => getMessageString + }); +let getMessageString; +(function () { + let MESSAGE_STRINGS; + let MESSAGE_STRINGS_GROUP; + let MESSAGE_STRINGS_MEETING; + const _sanitizeStrings = function (arg) { + if (typeof arg === "undefined") { + return arg; + } else if (typeof arg === "string") { + return escapeHTML(arg); + } else if (arg.forEach) { + arg.forEach((v, k) => { + arg[k] = _sanitizeStrings(v); + }); + } else if (typeof arg === "object") { + Object.keys(arg).forEach((k) => { + arg[k] = _sanitizeStrings(arg[k]); + }); } - }, react0().createElement("span", null, l[24581])); -}; -const Pin = ({ - stream, - mode, - onSpeakerChange, - onModeChange -}) => { - return react0().createElement(_button_jsx2__.A, { - icon: "sprite-fm-mono grid-main", - onClick: () => mode === _call_jsx3__.g.THUMBNAIL ? onSpeakerChange == null ? void 0 : onSpeakerChange(stream) : onModeChange == null ? void 0 : onModeChange(_call_jsx3__.g.THUMBNAIL) - }, react0().createElement("span", null, mode === _call_jsx3__.g.THUMBNAIL ? l.display_in_main_view : l.switch_to_thumb_view)); -}; -class VideoNodeMenu extends _mixins1__.w9 { - constructor(...args) { - super(...args); - this.domRef = react0().createRef(); - this.ToggleCrop = ({ - videoNodeRef - }) => { - const videoNode = videoNodeRef == null ? void 0 : videoNodeRef.current; - if (!videoNode) { - return null; - } - return videoNode.isVideoCropped() ? react0().createElement(_button_jsx2__.A, { - icon: "sprite-fm-mono grid-main", - onClick: () => { - videoNode.uncropVideo(); - this.forceUpdate(); - } - }, react0().createElement("span", null, "Uncrop video")) : react0().createElement(_button_jsx2__.A, { - icon: "sprite-fm-mono grid-main", - onClick: () => { - videoNode.cropVideo(); - this.forceUpdate(); - } - }, react0().createElement("span", null, "Crop video")); - }; - } - render() { - const { - NAMESPACE - } = VideoNodeMenu; - const { - stream, - isPresenterNode, - mode, - onSpeakerChange, - onModeChange - } = this.props; - const { - userHandle, - clientId - } = stream; - if (isPresenterNode) { - return react0().createElement("div", { - ref: this.domRef, - className: ` - ${NAMESPACE} - theme-dark-forced - ${mode === _call_jsx3__.g.THUMBNAIL ? '' : 'presenter'} - ` - }, react0().createElement("div", { - className: `${NAMESPACE}-toggle` - }, react0().createElement("i", { - className: `sprite-fm-mono call-node-pin icon-pin${mode === _call_jsx3__.g.MAIN ? '-off' : ''}`, - onClick: () => mode === _call_jsx3__.g.THUMBNAIL ? onSpeakerChange == null ? void 0 : onSpeakerChange(stream) : onModeChange == null ? void 0 : onModeChange(_call_jsx3__.g.THUMBNAIL) - }))); + return arg; + }; + getMessageString = function (type, isGroupCall, isMeeting) { + if (!MESSAGE_STRINGS) { + MESSAGE_STRINGS = { + 'outgoing-call': l[5891].replace("[X]", "[[[X]]]"), + 'incoming-call': l[19964] || "[[%s]] is calling...", + 'call-timeout': [l[18698].replace("[X]", "[[[X]]]")], + 'call-starting': l[7206].replace("[X]", "[[[X]]]"), + 'call-feedback': l[7998].replace("[X]", "[[[X]]]"), + 'call-initialising': l[7207].replace("[X]", "[[[X]]]"), + 'call-ended': [l[19965] || "Call ended.", l[7208]], + 'remoteCallEnded': [l[19965] || "Call ended.", l[7208]], + 'call-failed-media': l[7204], + 'call-failed': [l[19966] || "Call failed.", l[7208]], + 'call-handled-elsewhere': l[5895].replace("[X]", "[[[X]]]"), + 'call-missed': l[17870], + 'call-rejected': l[19040], + 'call-canceled': l[19041], + 'remoteCallStarted': l[5888], + 'call-started': l[5888].replace("[X]", "[[[X]]]"), + 'alterParticipants': undefined, + 'privilegeChange': undefined, + 'truncated': l[8905] + }; + _sanitizeStrings(MESSAGE_STRINGS); + } + if (isGroupCall && !MESSAGE_STRINGS_GROUP) { + MESSAGE_STRINGS_GROUP = { + 'call-ended': [l[19967], l[7208]], + 'remoteCallEnded': [l[19967], l[7208]], + 'call-handled-elsewhere': l[19968], + 'call-canceled': l[19969], + 'call-started': l[19970] + }; + _sanitizeStrings(MESSAGE_STRINGS_GROUP); } - if (userHandle !== u_handle) { - const $$CONTROLS = { - Contact, - Pin, - Privilege + if (isMeeting && !MESSAGE_STRINGS_MEETING) { + MESSAGE_STRINGS_MEETING = { + 'call-ended': [l.meeting_mgmt_call_ended, l[7208]], + 'remoteCallEnded': [l.meeting_mgmt_call_ended, l[7208]], + 'call-started': l.meeting_mgmt_call_started }; - return react0().createElement("div", { - ref: this.domRef, - className: ` - ${NAMESPACE} - theme-dark-forced - ` - }, react0().createElement("div", { - className: `${NAMESPACE}-toggle` - }, react0().createElement("i", { - className: "sprite-fm-mono icon-more-horizontal-thin-outline" - })), react0().createElement("div", { - className: `${NAMESPACE}-content` - }, react0().createElement("ul", null, Object.values($$CONTROLS).map(($$CONTROL, i) => react0().createElement("li", { - key: `${Object.keys($$CONTROLS)[i]}-${clientId}-${userHandle}` - }, react0().createElement($$CONTROL, this.props)))))); } - return null; - } -} -VideoNodeMenu.NAMESPACE = 'node-menu'; + if (isMeeting && MESSAGE_STRINGS_MEETING[type]) { + return MESSAGE_STRINGS_MEETING[type]; + } + if (isGroupCall && MESSAGE_STRINGS_GROUP[type]) { + return MESSAGE_STRINGS_GROUP[type]; + } + return MESSAGE_STRINGS[type]; + }; +})(); + -}, + }, -269 + 4372 (_, EXP_, REQ_) { "use strict"; -REQ_.r(EXP_); -REQ_.d(EXP_, { -"default": () => Incoming -}); -const _extends0__ = REQ_(168); -const react1__ = REQ_(594); -const react1 = REQ_.n(react1__); -const _contacts_jsx2__ = REQ_(251); -const _ui_modalDialogs_jsx3__ = REQ_(318); -const _button_jsx4__ = REQ_(959); -const _call_jsx5__ = REQ_(3); -const _ui_utils_jsx6__ = REQ_(314); - - - - + REQ_.d(EXP_, { + Y: () => withUpdateObserver + }); + const _babel_runtime_helpers_extends0__ = REQ_(8168); + const react1__ = REQ_(1594); + const react1___default = REQ_.n(react1__); + const _mixins_js2__ = REQ_(8264); -class Incoming extends react1().Component { - constructor(props) { - super(props); +const withUpdateObserver = Component => class extends _mixins_js2__ .w9 { + constructor(...args) { + super(...args); + this.updateInterval = 600000; + this.instanceRef = react1___default().createRef(); + this.intervalRef = undefined; this.state = { - video: false, - unsupported: undefined, - hoveredSwitch: true, - hideOverlay: false - }; - this.renderSwitchControls = () => { - const className = `mega-button large round switch ${this.state.hoveredSwitch ? 'hovered' : ''}`; - const toggleHover = () => this.setState(state => ({ - hoveredSwitch: !state.hoveredSwitch - })); - return react1().createElement("div", { - className: "switch-button" - }, react1().createElement("div", { - className: "switch-button-container simpletip", - "data-simpletip": l.end_and_answer, - "data-simpletipposition": "top", - onMouseEnter: toggleHover, - onMouseLeave: toggleHover, - onClick: ev => { - ev.stopPropagation(); - this.props.onSwitch(); - } - }, react1().createElement(_button_jsx4__.A, { - className: `${className} negative`, - icon: "icon-end-call" - }), react1().createElement(_button_jsx4__.A, { - className: `${className} positive`, - icon: "icon-phone" - }))); + updated: 0 }; - this.renderAnswerControls = () => { - const { - video, - unsupported - } = this.state; - const { - onAnswer, - onToggleVideo - } = this.props; - return react1().createElement(react1().Fragment, null, react1().createElement(_button_jsx4__.A, { - className: ` - mega-button - positive - answer - ${unsupported ? 'disabled' : ''} - `, - icon: "icon-phone", - simpletip: unsupported ? null : { - position: 'top', - label: l[7205] - }, - onClick: unsupported ? null : onAnswer - }, react1().createElement("span", null, l[7205])), react1().createElement(_button_jsx4__.A, { - className: ` - mega-button - large - round - video - ${video ? '' : 'negative'} - ${unsupported ? 'disabled' : ''} - `, - icon: video ? 'icon-video-call-filled' : 'icon-video-off', - simpletip: unsupported ? null : { - position: 'top', - label: video ? l[22894] : l[22893] - }, - onClick: () => unsupported ? null : this.setState({ - video: !video - }, () => onToggleVideo(video)) - }, react1().createElement("span", null, video ? l[22894] : l[22893]))); + this.updateListener = () => { + return this.isComponentVisible() && document.visibilityState === 'visible' && this.setState(state => ({ + updated: ++state.updated + }), () => this.safeForceUpdate()); }; - this.state.unsupported = !megaChat.hasSupportForCalls; - this.state.hideOverlay = document.body.classList.contains('overlayed') && !$.msgDialog; - } - componentDidMount() { - this._old$dialog = $.dialog; - $.dialog = "chat-incoming-call"; } componentWillUnmount() { - $.dialog = this._old$dialog; + super.componentWillUnmount(); + document.removeEventListener('visibilitychange', this.updateListener); + clearInterval(this.intervalRef); + } + componentDidMount() { + super.componentDidMount(); + document.addEventListener('visibilitychange', this.updateListener); + this.intervalRef = setInterval(this.instanceRef.current[Component.updateListener] || this.updateListener, Component.updateInterval || this.updateInterval); } render() { - const { - chatRoom - } = this.props; - if (chatRoom) { - const { - NAMESPACE - } = Incoming; - const { - callerId, - onClose, - onReject - } = this.props; - const { - unsupported - } = this.state; - const CALL_IN_PROGRESS = window.sfuClient; - const isPrivateRoom = chatRoom.type === 'private'; - const rejectLabel = isPrivateRoom ? l[20981] : l.msg_dlg_cancel; - return react1().createElement(_ui_modalDialogs_jsx3__.A.ModalDialog, (0,_extends0__.A)({}, this.state, { - name: NAMESPACE, - className: NAMESPACE, - roomName: chatRoom.getRoomTitle(), - onClose: () => onClose() - }), react1().createElement("div", { - className: "fm-dialog-body" - }, react1().createElement("div", { - className: `${NAMESPACE}-avatar` - }, react1().createElement(_contacts_jsx2__.Avatar, { - contact: M.u[callerId] - })), react1().createElement("div", { - className: `${NAMESPACE}-info` - }, react1().createElement("h1", null, react1().createElement(_ui_utils_jsx6__.zT, null, chatRoom.getRoomTitle())), react1().createElement("span", null, isPrivateRoom ? l[17878] : l[19995])), react1().createElement("div", { - className: ` - ${NAMESPACE}-controls - ${CALL_IN_PROGRESS ? 'call-in-progress' : ''} - ` - }, react1().createElement(_button_jsx4__.A, { - className: ` - mega-button - large - round - negative - `, - icon: "icon-end-call", - simpletip: { - position: 'top', - label: rejectLabel - }, - onClick: onReject - }, react1().createElement("span", null, rejectLabel)), CALL_IN_PROGRESS ? this.renderSwitchControls() : this.renderAnswerControls()), unsupported && react1().createElement("div", { - className: `${NAMESPACE}-unsupported` - }, react1().createElement("div", { - className: "unsupported-message" - }, _call_jsx5__.Ay.getUnsupportedBrowserMessage())))); - } - console.error('Incoming dialog received missing chatRoom prop.'); - return null; + return JSX_(Component, (0,_babel_runtime_helpers_extends0__ .A)({ + ref: this.instanceRef + }, this.state, this.props)); } -} -Incoming.NAMESPACE = 'incoming-dialog'; -window.ChatCallIncomingDialog = Incoming; +}; -}, + }, -485 + 5779 (_, EXP_, REQ_) { "use strict"; -REQ_.d(EXP_, { -A: () => __WEBPACK_DEFAULT_EXPORT__ -}); -const react0__ = REQ_(594); -const react0 = REQ_.n(react0__); -const _mixins_js1__ = REQ_(137); -const _contacts_jsx2__ = REQ_(251); -const _call_jsx3__ = REQ_(3); -const _button_jsx4__ = REQ_(959); -const _permissionsObserver_jsx5__ = REQ_(542); + REQ_.d(EXP_, { + O1: () => prepareExportIo, + VV: () => prepareExportStreams, + li: () => withSuspense + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _ui_fallback_jsx1__ = REQ_(3439); + + +async function prepareExportIo(dl) { + const { + zname, + size + } = dl; + if (window.isSecureContext && typeof showSaveFilePicker === 'function' && typeof FileSystemFileHandle !== 'undefined' && 'createWritable' in FileSystemFileHandle.prototype && typeof FileSystemWritableFileStream !== 'undefined' && 'seek' in FileSystemWritableFileStream.prototype) { + const file = await window.showSaveFilePicker({ + suggestedName: zname + }).catch(ex => { + if (String(ex).includes('aborted')) { + throw new Error('Aborted'); + } + dump(ex); + }); + if (file) { + const stream = await file.createWritable().catch(dump); + if (stream) { + return { + stream, + write (data, position, done) { + this.stream.write({ + type: 'write', + position, + data + }).then(done).catch(dump); + }, + download () { + this.abort(); + }, + abort () { + this.stream.close(); + }, + setCredentials () { + this.begin(); + } + }; + } + } + } + if (MemoryIO.usable() && Math.min(MemoryIO.fileSizeLimit, 94371840) > size) { + return new MemoryIO('chat_0', dl); + } else if (window.requestFileSystem) { + return new FileSystemAPI('chat_0', dl); + } + throw new Error('Download methods are unsupported'); +} +function prepareExportStreams(attachNodes, onEmpty) { + return attachNodes.map(node => { + return { + name: node.name, + lastModified: new Date((node.mtime || node.ts) * 1000), + input: M.gfsfetch.getReadableStream(node, { + error(ex, n) { + if (d) { + console.error(`${n.h}: ${ex}`); + } + onEmpty(n.s); + } + }) + }; + }); +} +const withSuspense = Component => { + const Wrapped = props => JSX_(react0__.Suspense, { + fallback: JSX_(_ui_fallback_jsx1__ .A, null) + }, JSX_(Component, props)); + Wrapped.displayName = `withSuspense(${Component.displayName || Component.name || 'Component'})`; + return Wrapped; +}; + }, + 5155 +(_, EXP_, REQ_) { +"use strict"; + REQ_.d(EXP_, { + $: () => Button + }); + const _babel_runtime_helpers_extends0__ = REQ_(8168); + const react1__ = REQ_(1594); + const react1___default = REQ_.n(react1__); + const _chat_mixins_js2__ = REQ_(8264); -class Preview extends react0().Component { +const BLURRABLE_CLASSES = '.conversationsApp, .join-meeting, .main-blur-block'; +class Button extends _chat_mixins_js2__ .w9 { constructor(props) { super(props); - this.domRef = react0().createRef(); - this.videoRef = react0().createRef(); - this.stream = null; + this.domRef = react1___default().createRef(); + this.buttonClass = `.button`; this.state = { - audio: false, - video: false, - avatarMeta: undefined + focused: false, + hovered: false, + iconHovered: '' }; - this.getTrackType = type => !type ? 'getTracks' : type === Preview.STREAMS.AUDIO ? 'getAudioTracks' : 'getVideoTracks'; - this.startStream = type => { - this.stopStream(); - const { - audio, - video - } = this.state; - navigator.mediaDevices.getUserMedia({ - audio, - video - }).then(stream => { - const videoRef = this.videoRef.current; - if (videoRef) { - videoRef.srcObject = stream; - this.stream = stream; - if (this.props.onToggle) { - this.props.onToggle(this.state.audio, this.state.video); - } - } - }).catch(ex => { - const stream = type === Preview.STREAMS.AUDIO ? 'audio' : 'video'; - return this.domRef.current && this.setState(state => ({ - [stream]: !state[stream] - }), () => { - megaChat.trigger('onLocalMediaError', { - [type === Preview.STREAMS.AUDIO ? 'mic' : 'camera']: `${ex.name}: ${ex.message}` - }); - console.error(`${ex.name}: ${ex.message}`); + this.onBlur = e => { + let _this$domRef; + if (!this.isMounted()) { + return; + } + if (!e || !$(e.target).closest(this.buttonClass).is((_this$domRef = this.domRef) == null ? void 0 : _this$domRef.current)) { + this.setState({ + focused: false + }, () => { + this.unbindEvents(); + this.safeForceUpdate(); }); - }); - }; - this.stopStream = type => { - if (this.stream) { - const trackType = this.getTrackType(type); - const tracks = this.stream[trackType](); - for (const track of tracks) { - track.stop(); - } } }; - this.toggleStream = type => { - let _this$props$resetErro, _this$props; - const stream = type === Preview.STREAMS.AUDIO ? 'audio' : 'video'; - this.setState(state => ({ - [stream]: !state[stream] - }), () => { - if (this.props.onToggle) { - this.props.onToggle(this.state.audio, this.state.video); + this.onClick = e => { + let _this$domRef2; + if (this.props.disabled === true) { + e.preventDefault(); + e.stopPropagation(); + return; + } + if ($(e.target).closest('.popup').closest(this.buttonClass).is((_this$domRef2 = this.domRef) == null ? void 0 : _this$domRef2.current) && this.state.focused === true) { + e.preventDefault(); + e.stopPropagation(); + return; + } + if ($(e.target).is('input, textarea, select')) { + return; + } + if (this.state.focused === false) { + if (this.props.onClick) { + this.props.onClick(this, e); + } else if (react1___default().Children.count(this.props.children) > 0) { + this.setState({ + focused: true + }, () => this.safeForceUpdate()); } - return this.state[stream] ? this.startStream(type) : this.stopStream(type); - }); - (_this$props$resetErro = (_this$props = this.props).resetError) == null || _this$props$resetErro.call(_this$props, type === Preview.STREAMS.AUDIO ? Av.Audio : Av.Camera); - }; - this.renderAvatar = () => { - if ((0,_call_jsx3__.P)()) { - return react0().createElement("div", { - className: "avatar-guest" - }, react0().createElement("i", { - className: "sprite-fm-uni icon-owner" - })); + } else if (this.state.focused === true) { + this.setState({ + focused: false + }); + this.unbindEvents(); } - if (is_chatlink) { - const { - avatarUrl, - color, - shortName - } = this.state.avatarMeta || {}; - return react0().createElement("div", { - className: ` - avatar-wrapper - ${color ? `color${color}` : ''} - ` - }, avatarUrl && react0().createElement("img", { - src: avatarUrl, - alt: "" - }), color && react0().createElement("span", null, shortName)); + }; + this.state.iconHovered = this.props.iconHovered || ''; + } + UNSAFE_componentWillUpdate(nextProps, nextState) { + if (nextProps.disabled === true && nextState.focused === true) { + nextState.focused = false; + } + if (this.state.focused !== nextState.focused && nextState.focused === true) { + this.bindEvents(); + if (this._pageChangeListener) { + mBroadcaster.removeListener(this._pageChangeListener); } - return react0().createElement(_contacts_jsx2__.Avatar, { - contact: M.u[u_handle] + this._pageChangeListener = mBroadcaster.addListener('pagechange', () => { + if (this.state.focused === true) { + this.onBlur(); + } }); - }; - this.state.audio = this.props.audio || this.state.audio; - if (this.props.video) { - this.state.video = this.props.video; - this.startStream(Preview.STREAMS.VIDEO); - this.props.onToggle(this.state.audio, this.state.video); } } componentWillUnmount() { - this.stopStream(); + super.componentWillUnmount(); + this.unbindEvents(); } - componentDidMount() { - if (this.props.onToggle) { - this.props.onToggle(this.state.audio, this.state.video); - } - this.setState({ - avatarMeta: is_chatlink ? generateAvatarMeta(u_handle) : undefined - }); + renderChildren() { + return this.props.children && react1___default().Children.map(this.props.children, child => child && (typeof child.type === 'string' || child.type === undefined ? child : react1___default().cloneElement(child, { + active: this.state.focused, + closeDropdown: () => this.setState({ + focused: false + }, () => this.unbindEvents()), + onActiveChange: active => { + let _this$domRef3; + const $element = $(((_this$domRef3 = this.domRef) == null ? void 0 : _this$domRef3.current) || this.domNode); + const $scrollables = $element.parents('.ps'); + if ($scrollables.length > 0) { + $scrollables.map((k, element) => Ps[active ? 'disable' : 'enable'](element)); + } + child.props.onActiveChange == null || child.props.onActiveChange(active); + return this[active ? 'bindEvents' : 'unbindEvents'](); + } + }))); + } + bindEvents() { + $(BLURRABLE_CLASSES).rebind(`mousedown.button--${this.getUniqueId()}`, this.onBlur); + $(document).rebind(`keyup.button--${this.getUniqueId()}`, ev => this.state.focused === true && ev.keyCode === 27 && this.onBlur()); + $(document).rebind(`closeDropdowns.${this.getUniqueId()}`, this.onBlur); + } + unbindEvents() { + $(BLURRABLE_CLASSES).unbind(`mousedown.button--${this.getUniqueId()}`); + $(document).off(`keyup.button--${this.getUniqueId()}`); + $(document).off(`closeDropdowns.${this.getUniqueId()}`); + mBroadcaster.removeListener(this._pageChangeListener); } render() { const { - NAMESPACE - } = Preview; - const { - hasToRenderPermissionsWarning, - renderPermissionsWarning + className, + disabled, + style, + icon, + iconHovered, + label, + attrs, + toggle, + secondLabel, + secondLabelClass } = this.props; - const { - audio, - video - } = this.state; - const SIMPLETIP_PROPS = { - label: undefined, - position: 'top', - className: 'theme-dark-forced' - }; - return react0().createElement("div", { + const isMegaButton = className && className.indexOf('mega-button') > -1; + const TagName = isMegaButton ? 'button' : 'div'; + return JSX_(TagName, (0,_babel_runtime_helpers_extends0__ .A)({ ref: this.domRef, className: ` - ${NAMESPACE} - local-stream-mirrored - ` - }, video && react0().createElement("div", { - className: `${NAMESPACE}-video-overlay` - }), react0().createElement("video", { - className: video ? 'streaming' : '', - muted: true, - autoPlay: true, - ref: this.videoRef - }), !video && this.renderAvatar(), react0().createElement("div", { - className: `${NAMESPACE}-controls` - }, react0().createElement("div", { - className: "preview-control-wrapper" - }, react0().createElement(_button_jsx4__.A, { - simpletip: { - ...SIMPLETIP_PROPS, - label: audio ? l[16214] : l[16708] - }, - className: ` - mega-button - round - theme-light-forced - ${NAMESPACE}-control - ${audio ? '' : 'with-fill'} - `, - icon: audio ? 'icon-mic-thin-outline' : 'icon-mic-off-thin-outline', - onClick: () => { - this.toggleStream(Preview.STREAMS.AUDIO); - } - }), react0().createElement("span", null, l.mic_button), hasToRenderPermissionsWarning(Av.Audio) ? renderPermissionsWarning(Av.Audio) : null), react0().createElement("div", { - className: "preview-control-wrapper" - }, react0().createElement(_button_jsx4__.A, { - simpletip: { - ...SIMPLETIP_PROPS, - label: video ? l[22894] : l[22893] - }, + button + ${className || ''} + ${disabled ? 'disabled' : ''} + ${this.state.focused ? 'active active-dropdown' : ''} + `, + style, + onClick: this.onClick, + onMouseEnter: () => iconHovered && this.setState({ + hovered: true + }), + onMouseLeave: () => iconHovered && this.setState({ + hovered: false + }) + }, attrs), icon && !isMegaButton && JSX_("div", null, JSX_("i", { + className: this.state.hovered ? this.state.iconHovered : icon + })), icon && isMegaButton && JSX_("div", null, JSX_("i", { + className: this.state.hovered ? this.state.iconHovered : icon + })), label && JSX_("span", null, label), secondLabel && JSX_("span", { + className: secondLabelClass ? secondLabelClass : '' + }, secondLabel), toggle && JSX_("div", { className: ` - mega-button - round - theme-light-forced - ${NAMESPACE}-control - ${video ? '' : 'with-fill'} - `, - icon: video ? 'icon-video-thin-outline' : 'icon-video-off-thin-outline', - onClick: () => this.toggleStream(Preview.STREAMS.VIDEO) - }), react0().createElement("span", null, l.camera_button), hasToRenderPermissionsWarning(Av.Camera) ? renderPermissionsWarning(Av.Camera) : null))); + mega-switch + ${toggle.className ? toggle.className : ''} + ${toggle.enabled ? 'toggle-on' : ''} + `, + role: "switch", + "aria-checked": !!toggle.enabled, + onClick: ev => { + ev.stopPropagation(); + if (this.props.toggle.onClick) { + this.props.toggle.onClick(); + } + } + }, JSX_("div", { + className: `mega-feature-switch sprite-fm-mono-after + ${toggle.enabled ? 'icon-check-after' : 'icon-minimise-after'}` + })), this.renderChildren()); } } -Preview.NAMESPACE = 'preview-meeting'; -Preview.STREAMS = { - AUDIO: 1, - VIDEO: 2 -}; -const __WEBPACK_DEFAULT_EXPORT__ = (0,_mixins_js1__.Zz)(_permissionsObserver_jsx5__.$)(Preview); -}, + }, -890 + 1510 (_, EXP_, REQ_) { "use strict"; + REQ_.d(EXP_, { + ms: () => Dropdown, + tJ: () => DropdownItem + }); -// EXPORTS -REQ_.d(EXP_, { - A: () => GenericConversationMessage -}); - -// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/extends.js -const esm_extends = REQ_(168); -// EXTERNAL MODULE: external "React" -const React_ = REQ_(594); -const REaCt = REQ_.n(React_); -// EXTERNAL MODULE: ./js/chat/ui/messages/mixin.jsx -const mixin = REQ_(446); -// EXTERNAL MODULE: ./js/chat/ui/contacts.jsx -const ui_contacts = REQ_(251); -// EXTERNAL MODULE: ./js/ui/utils.jsx -const utils = REQ_(314); -;// ./js/chat/ui/messages/abstractGenericMessage.jsx + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _utils_jsx1__ = REQ_(6411); + const _chat_mixins2__ = REQ_(8264); + const _chat_ui_contacts_jsx3__ = REQ_(8022); -class AbstractGenericMessage extends mixin.M { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); +class Dropdown extends _chat_mixins2__ .w9 { + constructor(props) { + super(props); + this.domRef = react0___default().createRef(); + this.onActiveChange = this.onActiveChange.bind(this); + this.onResized = this.onResized.bind(this); + } + UNSAFE_componentWillUpdate(nextProps) { + if (this.props.active != nextProps.active) { + this.onActiveChange(nextProps.active); + } + } + specShouldComponentUpdate(nextProps, nextState) { + if (this.props.active != nextProps.active) { + if (this.props.onBeforeActiveChange) { + this.props.onBeforeActiveChange(nextProps.active); + } + return true; + } else if (this.props.focused != nextProps.focused) { + return true; + } else if (this.state && this.state.active != nextState.active) { + return true; + } + return undefined; + } + onActiveChange(newVal) { + if (this.props.onActiveChange) { + this.props.onActiveChange(newVal); + } + } + reposElementUsing(element, obj, info) { + let $element; + if (this.popupElement) { + $element = $(this.popupElement); + } else { + return; + } + const self = this; + let vertOffset = 0; + let horizOffset = 0; + if (!self.props.noArrow) { + const $arrow = $('.dropdown-white-arrow', $element); + let arrowHeight; + if (self.props.arrowHeight) { + arrowHeight = self.props.arrowHeight; + if (info.vertical === "top") { + arrowHeight = 0; + } else { + arrowHeight *= -1; + } + } else { + arrowHeight = $arrow.outerHeight(); + } + if (info.vertical === "top") { + $(element).removeClass("down-arrow").addClass("up-arrow"); + } else { + $(element).removeClass("up-arrow").addClass("down-arrow"); + } + vertOffset += info.vertical === "top" ? arrowHeight : 0; + } + if (self.props.vertOffset) { + vertOffset += self.props.vertOffset * (info.vertical === "top" ? 1 : -1); + } + if (self.props.horizOffset) { + horizOffset += self.props.horizOffset; + } + $(element).css({ + left: `${obj.left + 0 + horizOffset }px`, + top: `${obj.top + vertOffset }px` + }); + if (this.props.positionLeft) { + $(element).css({ + left: this.props.positionLeft + }); + } + } + onResized() { + const self = this; + if (this.props.active === true && this.popupElement) { + const $element = $(this.popupElement); + const $positionToElement = $('.button.active-dropdown:visible'); + if ($positionToElement.length === 0) { + return; + } + let $container = $positionToElement.closest('.messages.scroll-area'); + if ($container.length === 0) { + $container = $(document.body); + } + $element.css('margin-left', ''); + $element.position({ + of: $positionToElement, + my: self.props.positionMy ? self.props.positionMy : "center top", + at: self.props.positionAt ? self.props.positionAt : "center bottom", + collision: this.props.collision || 'flipfit', + within: self.props.wrapper || $container, + using (obj, info) { + self.reposElementUsing(this, obj, info); + } + }); + } + } + componentDidMount() { + super.componentDidMount(); + chatGlobalEventManager.addEventListener('resize', `drpdwn${ this.getUniqueId()}`, this.onResized.bind(this)); + this.onResized(); + const self = this; + $(document.body).rebind(`closeAllDropdownsExcept.drpdwn${ this.getUniqueId()}`, (e, target) => { + if (self.props.active && target !== self) { + if (self.props && self.props.closeDropdown) { + self.props.closeDropdown(); + } + } + }); } - getAvatar() { - const contact = this.getContact() || Message.getContactForMessage(this.props.message); - if (this.props.grouped) { - return null; + componentDidUpdate() { + this.onResized(); + } + componentWillUnmount() { + super.componentWillUnmount(); + $(document.body).unbind(`closeAllDropdownsExcept.drpdwn${ this.getUniqueId()}`); + if (this.props.active) { + this.onActiveChange(false); } - return contact ? REaCt().createElement(ui_contacts.Avatar, { - contact: this.getContact(), - className: "message avatar-wrapper small-rounded-avatar", - chatRoom: this.props.chatRoom - }) : null; + chatGlobalEventManager.removeEventListener('resize', `drpdwn${ this.getUniqueId()}`); + } + doRerender() { + const self = this; + setTimeout(() => { + self.safeForceUpdate(); + }, 100); + setTimeout(() => { + self.onResized(); + }, 200); } - getName() { - const contact = this.getContact() || Message.getContactForMessage(this.props.message); - if (this.props.grouped) { + renderChildren() { + const self = this; + return react0___default().Children.map(this.props.children, (child) => { + if (child) { + let activeVal = self.props.active || self.state.active; + activeVal = String(activeVal); + return react0___default().cloneElement(child, { + active: activeVal + }); + } return null; - } - return contact ? REaCt().createElement(ui_contacts.ContactButton, { - contact, - className: "message", - label: REaCt().createElement(utils.zT, null, M.getNameByHandle(contact.u)), - chatRoom: this.props.message.chatRoom, - dropdownDisabled: !!this.props.dialog - }) : null; + }); } - renderMessageActionButtons(buttons) { - if (!buttons) { + render() { + if (this.props.active !== true) { return null; } - const cnt = buttons.length; - if (cnt === 0) { - return null; + const self = this; + let child = null; + if (this.props.children) { + child = JSX_("div", { + ref: this.domRef + }, self.renderChildren()); + } else if (this.props.dropdownItemGenerator) { + child = this.props.dropdownItemGenerator(this); } - return REaCt().createElement("div", { - className: `right-aligned-msg-buttons ${cnt && cnt > 1 ? `total-${cnt}` : ''}` - }, buttons); - } - render() { - const { - message, - grouped, - additionalClasses, - hideActionButtons - } = this.props; - if (message.deleted) { + if (!child && !this.props.forceShowWhenEmpty) { + if (this.props.active !== false) { + queueMicrotask(() => { + self.onActiveChange(false); + }); + } return null; } - return REaCt().createElement("div", { - ref: this.domRef, - "data-id": message.messageId, + return JSX_(_utils_jsx1__ .Ay.RenderTo, { + element: document.body, className: ` - ${this.getClassNames ? this.getClassNames() : grouped ? 'grouped' : ''} - ${additionalClasses} - ${message.messageId} - message + dropdown body - ` - }, this.getAvatar && this.getAvatar(), REaCt().createElement("div", { - className: "message content-area selectable-txt" - }, this.getName && this.getName(), this.getMessageTimestamp ? this.getMessageTimestamp() : grouped ? null : REaCt().createElement("div", { - className: "message date-time simpletip", - "data-simpletip": time2date(this.getTimestamp(), 17), - "data-simpletipposition": "top", - "data-simpletipoffset": "4" - }, this.getTimestampAsString()), !hideActionButtons && this.getMessageActionButtons && this.renderMessageActionButtons(this.getMessageActionButtons()), this.getContents && this.getContents(), hideActionButtons ? null : this.getEmojisImages())); + ${this.props.noArrow ? '' : 'dropdown-arrow up-arrow'} + ${this.props.className || ''} + `, + style: this.popupElement && { + zIndex: 123, + position: 'absolute', + width: this.props.styles ? this.props.styles.width : undefined + }, + popupDidMount: popupElement => { + this.popupElement = popupElement; + this.onResized(); + }, + popupWillUnmount: () => { + delete this.popupElement; + } + }, JSX_("div", { + ref: this.domRef, + onClick: () => { + $(document.body).trigger('closeAllDropdownsExcept', this); + } + }, this.props.noArrow ? null : JSX_("i", { + className: "dropdown-white-arrow" + }), child)); } } -;// ./js/chat/ui/messages/utils.jsx -let getMessageString; -(function () { - let MESSAGE_STRINGS; - let MESSAGE_STRINGS_GROUP; - let MESSAGE_STRINGS_MEETING; - const _sanitizeStrings = function (arg) { - if (typeof arg === "undefined") { - return arg; - } else if (typeof arg === "string") { - return escapeHTML(arg); - } else if (arg.forEach) { - arg.forEach((v, k) => { - arg[k] = _sanitizeStrings(v); - }); - } else if (typeof arg === "object") { - Object.keys(arg).forEach((k) => { - arg[k] = _sanitizeStrings(arg[k]); - }); - } - return arg; - }; - getMessageString = function (type, isGroupCall, isMeeting) { - if (!MESSAGE_STRINGS) { - MESSAGE_STRINGS = { - 'outgoing-call': l[5891].replace("[X]", "[[[X]]]"), - 'incoming-call': l[19964] || "[[%s]] is calling...", - 'call-timeout': [l[18698].replace("[X]", "[[[X]]]")], - 'call-starting': l[7206].replace("[X]", "[[[X]]]"), - 'call-feedback': l[7998].replace("[X]", "[[[X]]]"), - 'call-initialising': l[7207].replace("[X]", "[[[X]]]"), - 'call-ended': [l[19965] || "Call ended.", l[7208]], - 'remoteCallEnded': [l[19965] || "Call ended.", l[7208]], - 'call-failed-media': l[7204], - 'call-failed': [l[19966] || "Call failed.", l[7208]], - 'call-handled-elsewhere': l[5895].replace("[X]", "[[[X]]]"), - 'call-missed': l[17870], - 'call-rejected': l[19040], - 'call-canceled': l[19041], - 'remoteCallStarted': l[5888], - 'call-started': l[5888].replace("[X]", "[[[X]]]"), - 'alterParticipants': undefined, - 'privilegeChange': undefined, - 'truncated': l[8905] - }; - _sanitizeStrings(MESSAGE_STRINGS); - } - if (isGroupCall && !MESSAGE_STRINGS_GROUP) { - MESSAGE_STRINGS_GROUP = { - 'call-ended': [l[19967], l[7208]], - 'remoteCallEnded': [l[19967], l[7208]], - 'call-handled-elsewhere': l[19968], - 'call-canceled': l[19969], - 'call-started': l[19970] - }; - _sanitizeStrings(MESSAGE_STRINGS_GROUP); - } - if (isMeeting && !MESSAGE_STRINGS_MEETING) { - MESSAGE_STRINGS_MEETING = { - 'call-ended': [l.meeting_mgmt_call_ended, l[7208]], - 'remoteCallEnded': [l.meeting_mgmt_call_ended, l[7208]], - 'call-started': l.meeting_mgmt_call_started - }; - } - if (isMeeting && MESSAGE_STRINGS_MEETING[type]) { - return MESSAGE_STRINGS_MEETING[type]; - } - if (isGroupCall && MESSAGE_STRINGS_GROUP[type]) { - return MESSAGE_STRINGS_GROUP[type]; - } - return MESSAGE_STRINGS[type]; - }; -})(); -mega.ui = mega.ui || {}; -mega.ui.chat = mega.ui.chat || {}; -mega.ui.chat.getMessageString = getMessageString; - -;// ./js/chat/ui/messages/types/local.jsx - - - - - -const MESSAGE_TYPE = { - OUTGOING: 'outgoing-call', - INCOMING: 'incoming-call', - TIMEOUT: 'call-timeout', - STARTING: 'call-starting', - FEEDBACK: 'call-feedback', - INITIALISING: 'call-initialising', - ENDED: 'call-ended', - ENDED_REMOTE: 'remoteCallEnded', - FAILED: 'call-failed', - FAILED_MEDIA: 'call-failed-media', - HANDLED_ELSEWHERE: 'call-handled-elsewhere', - MISSED: 'call-missed', - REJECTED: 'call-rejected', - CANCELLED: 'call-canceled', - STARTED: 'call-started', - STARTED_REMOTE: 'remoteCallStarted', - ALTER_PARTICIPANTS: 'alterParticipants', - PRIVILEGE_CHANGE: 'privilegeChange', - TRUNCATED: 'truncated' +Dropdown.defaultProps = { + 'requiresUpdateOnResize': true }; -class Local extends AbstractGenericMessage { - componentDidMount() { - super.componentDidMount(); - this._setClassNames(); - } - _roomIsGroup() { - return this.props.message.chatRoom.type === 'group' || this.props.message.chatRoom.type === 'public'; - } - _getParticipantNames(message) { - return message.meta && message.meta.participants && !!message.meta.participants.length && message.meta.participants.map(handle => `[[${megaChat.html(M.getNameByHandle(handle))}]]`); +class DropdownContactsSelector extends _chat_mixins2__ .w9 { + constructor(props) { + super(props); + this.state = { + 'selected': this.props.selected ? this.props.selected : [] + }; + this.onSelectClicked = this.onSelectClicked.bind(this); + this.onSelected = this.onSelected.bind(this); } - _getExtraInfo(message) { - const { - meta, - type - } = message; - const participantNames = this._getParticipantNames(message); - const HAS_PARTICIPANTS = participantNames && !!participantNames.length && participantNames.length > 1; - const HAS_DURATION = meta && meta.duration; - const ENDED = type === MESSAGE_TYPE.ENDED || type === MESSAGE_TYPE.FAILED || type === MESSAGE_TYPE.CANCELLED; - let messageExtraInfo = [HAS_PARTICIPANTS ? mega.utils.trans.listToString(participantNames, l[20234]) : '']; - if (ENDED) { - messageExtraInfo = [...messageExtraInfo, HAS_PARTICIPANTS ? '. ' : '', HAS_DURATION ? l[7208].replace('[X]', `[[${secToDuration(meta.duration)}]]`) : '']; + specShouldComponentUpdate(nextProps, nextState) { + if (this.props.active != nextProps.active) { + return true; + } else if (this.props.focused != nextProps.focused) { + return true; + } else if (this.state && this.state.active != nextState.active) { + return true; + } else if (this.state && JSON.stringify(this.state.selected) != JSON.stringify(nextState.selected)) { + return true; + } else { + return undefined; } - return messageExtraInfo && messageExtraInfo.reduce((acc, cur) => (acc + cur).replace(/\[\[/g, '').replace(/]]/g, '')); } - _setClassNames() { - let cssClass; - switch (this.props.message.type) { - case MESSAGE_TYPE.REJECTED: - cssClass = 'sprite-fm-theme icon-handset-rejected'; - break; - case MESSAGE_TYPE.MISSED: - cssClass = 'sprite-fm-theme icon-handset-missed'; - break; - case MESSAGE_TYPE.OUTGOING: - case MESSAGE_TYPE.HANDLED_ELSEWHERE: - cssClass = 'sprite-fm-theme icon-handset-outgoing'; - break; - case MESSAGE_TYPE.FAILED: - case MESSAGE_TYPE.FAILED_MEDIA: - cssClass = 'sprite-fm-theme icon-handset-failed'; - break; - case MESSAGE_TYPE.ENDED: - case MESSAGE_TYPE.TIMEOUT: - cssClass = 'sprite-fm-theme icon-handset-ended'; - break; - case MESSAGE_TYPE.CANCELLED: - cssClass = 'sprite-fm-theme icon-handset-cancelled'; - break; - case MESSAGE_TYPE.FEEDBACK: - case MESSAGE_TYPE.STARTING: - case MESSAGE_TYPE.STARTED: - cssClass = 'sprite-fm-mono icon-phone'; - break; - case MESSAGE_TYPE.INCOMING: - cssClass = 'sprite-fm-theme icon-handset-incoming'; - break; - default: - cssClass = `sprite-fm-mono ${ this.props.message.type}`; - break; + onSelected(nodes) { + this.setState({ + 'selected': nodes + }); + if (this.props.onSelected) { + this.props.onSelected(nodes); } - this.props.message.cssClass = cssClass; + this.forceUpdate(); + } + onSelectClicked() { + this.props.onSelectClicked(); + } + render() { + return JSX_(Dropdown, { + className: ` + popup contacts-search + ${this.props.className} + tooltip-blur + `, + active: this.props.active, + closeDropdown: this.props.closeDropdown, + ref: ref => { + this.dropdownRef = ref; + }, + positionMy: this.props.positionMy, + positionAt: this.props.positionAt, + arrowHeight: this.props.arrowHeight, + horizOffset: this.props.horizOffset, + vertOffset: this.props.vertOffset, + noArrow: true + }, JSX_(_chat_ui_contacts_jsx3__ .hU, { + onClose: this.props.closeDropdown, + onEventuallyUpdated: () => { + let _this$dropdownRef; + return (_this$dropdownRef = this.dropdownRef) == null ? void 0 : _this$dropdownRef.doRerender(); + }, + active: this.props.active, + className: "popup contacts-search tooltip-blur small-footer", + contacts: M.u, + selectFooter: this.props.selectFooter, + megaChat: this.props.megaChat, + exclude: this.props.exclude, + allowEmpty: this.props.allowEmpty, + multiple: this.props.multiple, + topButtons: this.props.topButtons, + showAddContact: this.props.showAddContact, + onAddContact: () => eventlog(500237), + onSelected: () => eventlog(500238), + onSelectDone: this.props.onSelectDone, + multipleSelectedButtonLabel: this.props.multipleSelectedButtonLabel, + singleSelectedButtonLabel: this.props.singleSelectedButtonLabel, + nothingSelectedButtonLabel: this.props.nothingSelectedButtonLabel + })); } - _getIcon(message) { - const MESSAGE_ICONS = { - [MESSAGE_TYPE.STARTED]: ` `, - [MESSAGE_TYPE.ENDED]: ` `, - DEFAULT: ` ` +} +DropdownContactsSelector.defaultProps = { + requiresUpdateOnResize: true +}; +class DropdownItem extends _chat_mixins2__ .w9 { + constructor(props) { + super(props); + this.domRef = react0___default().createRef(); + this.state = { + 'isClicked': false }; - return MESSAGE_ICONS[message.type] || MESSAGE_ICONS.DEFAULT; + this.onClick = this.onClick.bind(this); + this.onMouseOver = this.onMouseOver.bind(this); } - _getText() { - const { - message - } = this.props; - const IS_GROUP = this._roomIsGroup(); - let messageText = getMessageString(message.type, IS_GROUP, message.chatRoom.isMeeting); - if (!messageText) { - return console.error(`Message with type: ${message.type} -- no text string defined. Message: ${message}`); - } - messageText = CallManager2._getMltiStrTxtCntsForMsg(message, messageText.splice ? messageText : [messageText], true); - messageText = megaChat.html(messageText); - message.textContents = String(messageText).replace("[[", "").replace("]]", ""); - if (IS_GROUP) { - messageText = ` - ${this._getIcon(message)} -
- ${messageText} - ${this._getExtraInfo(message)} -
- `; - } - return messageText; - } - _getAvatarsListing() { - const { - message - } = this.props; - if (this._roomIsGroup() && message.type === MESSAGE_TYPE.STARTED && message.messageId === `${MESSAGE_TYPE.STARTED}-${message.chatRoom.getActiveCallMessageId()}`) { - const unique = message.chatRoom.uniqueCallParts ? Object.keys(message.chatRoom.uniqueCallParts) : []; - return unique.map(handle => REaCt().createElement(ui_contacts.Avatar, { - key: handle, - contact: M.u[handle], - simpletip: true, - className: "message avatar-wrapper small-rounded-avatar" - })); - } - return null; + renderChildren() { + const self = this; + return react0___default().Children.map(this.props.children, (child) => { + const props = { + active: self.state.isClicked, + closeDropdown () { + self.setState({ + 'isClicked': false + }); + } + }; + return react0___default().cloneElement(child, props); + }); } - _getButtons() { + onClick(ev) { const { - message + children, + persistent, + onClick } = this.props; - if (message.buttons && Object.keys(message.buttons).length) { - return REaCt().createElement("div", { - className: "buttons-block" - }, Object.keys(message.buttons).map(key => { - const button = message.buttons[key]; - return REaCt().createElement("button", { - key, - className: button.classes, - onClick: e => button.callback(e.target) - }, button.icon && REaCt().createElement("div", null, REaCt().createElement("i", { - className: `small-icon ${button.icon}` - })), REaCt().createElement("span", null, button.text)); - }), REaCt().createElement("div", { - className: "clear" - })); + if (children) { + ev.stopPropagation(); + ev.preventDefault(); + this.setState({ + isClicked: !this.state.isClicked + }); } - } - getAvatar() { - const { - message, - grouped - } = this.props; - if (message.type === MESSAGE_TYPE.FEEDBACK) { - return null; + if (!persistent) { + $(document).trigger('closeDropdowns'); } - const $$AVATAR = REaCt().createElement(ui_contacts.Avatar, { - contact: message.authorContact, - className: "message avatar-wrapper small-rounded-avatar", - chatRoom: message.chatRoom - }); - const $$ICON = REaCt().createElement("div", { - className: "feedback call-status-block" - }, REaCt().createElement("i", { - className: `sprite-fm-mono ${message.cssClass}` - })); - return message.showInitiatorAvatar ? grouped ? null : $$AVATAR : $$ICON - ; + return onClick && onClick(ev); } - getMessageTimestamp() { - let _this$props$message; - const callId = (_this$props$message = this.props.message) == null || (_this$props$message = _this$props$message.meta) == null ? void 0 : _this$props$message.callId; - let debugMsg = ""; - if (d && callId) { - debugMsg = `: callId: ${callId}`; + onMouseOver(e) { + if (this.props.submenu) { + const $contextItem = $(e.target).closest(".contains-submenu"); + const $subMenu = $contextItem.next('.submenu'); + const contextTopPos = $contextItem.position().top; + let contextleftPos = 0; + $contextItem.addClass("opened"); + $subMenu.addClass("active"); + contextleftPos = $contextItem.offset().left + $contextItem.outerWidth() + $subMenu.outerWidth() + 10; + if (contextleftPos > $(document.body).width()) { + $subMenu.addClass("left-position"); + } + $subMenu.css({ + "top": contextTopPos + }); + } else if (!$(e.target).parent('.submenu').length) { + const $dropdown = $(e.target).closest(".dropdown.body"); + $dropdown.find(".contains-submenu").removeClass("opened"); + $dropdown.find(".submenu").removeClass("active"); } - return REaCt().createElement("div", { - className: "message date-time simpletip", - "data-simpletip": time2date(this.getTimestamp(), 17) - }, this.getTimestampAsString(), debugMsg); - } - getClassNames() { - const { - message: { - showInitiatorAvatar, - type - }, - grouped - } = this.props; - const classNames = [showInitiatorAvatar && grouped && 'grouped', this._roomIsGroup() && type !== MESSAGE_TYPE.OUTGOING && type !== MESSAGE_TYPE.INCOMING && 'with-border']; - return classNames.filter(className => className).join(' '); - } - getName() { - const { - message, - grouped - } = this.props; - const contact = this.getContact(); - return message.showInitiatorAvatar && !grouped ? REaCt().createElement(ui_contacts.ContactButton, { - contact, - className: "message", - label: REaCt().createElement(utils.zT, null, message.authorContact ? M.getNameByHandle(message.authorContact.u) : ''), - chatRoom: message.chatRoom - }) : M.getNameByHandle(contact.u); } - getContents() { + render() { const { - message: { - getState - } + className, + disabled, + label, + icon, + submenu } = this.props; - return REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("div", { - className: "message text-block" - }, REaCt().createElement("div", { - className: "message call-inner-block" - }, REaCt().createElement("div", { - className: "call-info" - }, REaCt().createElement("div", { - className: "call-info-container" - }, REaCt().createElement(utils.P9, { - className: "info-wrapper" - }, this._getText())), REaCt().createElement("div", { - className: "call-info-avatars" - }, this._getAvatarsListing(), REaCt().createElement("div", { - className: "clear" - }))))), getState && getState() === Message.STATE.NOT_SENT ? null : this._getButtons()); + return JSX_("div", { + ref: this.domRef, + className: ` + dropdown-item + ${className ? className : ''} + ${submenu ? 'contains-submenu' : ''} + ${disabled ? 'disabled' : ''} + `, + onClick: disabled ? undefined : ev => this.onClick(ev), + onMouseOver: this.onMouseOver + }, icon && JSX_("i", { + className: icon + }), label && JSX_("span", null, label), submenu ? JSX_("i", { + className: "sprite-fm-mono icon-arrow-right submenu-icon" + }) : '', JSX_("div", null, this.renderChildren())); } } -// EXTERNAL MODULE: ./js/ui/dropdowns.jsx -const dropdowns = REQ_(911); -// EXTERNAL MODULE: ./js/ui/buttons.jsx -const buttons = REQ_(994); -;// ./js/chat/ui/messages/types/contact.jsx +DropdownItem.defaultProps = { + requiresUpdateOnResize: true +}; + }, + 1165 +(_, EXP_, REQ_) { +"use strict"; + REQ_.d(EXP_, { + A: () => DropdownEmojiSelector + }); + const _babel_runtime_helpers_extends0__ = REQ_(8168); + const react1__ = REQ_(1594); + const react1___default = REQ_.n(react1__); + const _chat_mixins_js2__ = REQ_(8264); + const _dropdowns_jsx3__ = REQ_(1510); + const _perfectScrollbar_jsx4__ = REQ_(1301); -class Contact extends AbstractGenericMessage { - constructor(...args) { - super(...args); - this.DIALOG = { - ADDED: addedEmail => msgDialog('info', l[150], l[5898].replace('[X]', addedEmail)), - DUPLICATE: () => msgDialog('warningb', '', l[17545]) + + +class DropdownEmojiSelector extends _chat_mixins_js2__ .w9 { + constructor(props) { + super(props); + this.domRef = react1___default().createRef(); + this.emojiSearchRef = react1___default().createRef(); + this.data_categories = null; + this.data_emojis = null; + this.data_emojiByCategory = null; + this.customCategoriesOrder = ["frequently_used", "people", "nature", "food", "activity", "travel", "objects", "symbols", "flags"]; + this.frequentlyUsedEmojis = ['slight_smile', 'grinning', 'smile', 'rofl', 'wink', 'yum', 'rolling_eyes', 'stuck_out_tongue', 'smiling_face_with_3_hearts', 'heart_eyes', 'kissing_heart', 'sob', 'pleading_face', 'thumbsup', 'pray', 'wave', 'fire', 'sparkles']; + this.heightDefs = { + 'categoryTitleHeight': 55, + 'emojiRowHeight': 35, + 'containerHeight': 302, + 'totalScrollHeight': 302, + 'numberOfEmojisPerRow': 9 }; + this.categoryLabels = { + 'frequently_used': l[17737], + 'people': l[8016], + 'objects': l[17735], + 'activity': l[8020], + 'nature': l[8017], + 'travel': l[8021], + 'symbols': l[17736], + 'food': l[8018], + 'flags': l[17703] + }; + this.state = this.getInitialState(); + this.onSearchChange = this.onSearchChange.bind(this); + this.onUserScroll = this.onUserScroll.bind(this); + this._onScrollChanged = this._onScrollChanged.bind(this); } - haveMoreContactListeners() { - const { - message - } = this.props; - const textContents = message.textContents.substring(2, message.textContents.length); - const attachmentMeta = JSON.parse(textContents); - if (!attachmentMeta) { - return false; - } - const contacts = attachmentMeta.map(v => v.u); - return contacts.length ? contacts : false; - } - _doAddContact(contactEmail) { - return M.inviteContact(M.u[u_handle] ? M.u[u_handle].m : u_attr.email, contactEmail); - } - _handleAddContact(contactEmail) { - let _this$props$chatRoom; - if ((_this$props$chatRoom = this.props.chatRoom) != null && _this$props$chatRoom.isAnonymous()) { - return this._doAddContact(contactEmail).then(addedEmail => this.DIALOG.ADDED(addedEmail)).catch(this.DIALOG.DUPLICATE); - } - return Object.values(M.opc).some(opc => opc.m === contactEmail) ? this.DIALOG.DUPLICATE() : this._doAddContact(contactEmail).then(addedEmail => this.DIALOG.ADDED(addedEmail)) - ; - } - _getContactAvatar(contact, className) { - return REaCt().createElement(ui_contacts.Avatar, { - className: `avatar-wrapper ${className}`, - contact: M.u[contact.u], - chatRoom: this.props.chatRoom + getInitialState() { + return clone({ + 'previewEmoji': null, + 'searchValue': '', + 'browsingCategory': false, + 'isActive': false, + 'isLoading': true, + 'loadFailed': false, + 'visibleCategories': "0" }); } - _getContactDeleteButton(message) { - if (message.isEditable()) { - return REaCt().createElement(REaCt().Fragment, null, REaCt().createElement("hr", null), REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-dialog-close", - label: l[83], - onClick: e => this.props.onDelete(e, message) - })); - } - } - _getContactCard(message, contact, contactEmail) { - const HAS_RELATIONSHIP = M.u[contact.u].c === 1; - let name = REaCt().createElement(ui_contacts.ContactAwareName, { - emoji: true, - contact: M.u[contact.u] - }); - const { - chatRoom - } = this.props; - const isAnonView = chatRoom.isAnonymous(); - if (megaChat.FORCE_EMAIL_LOADING) { - name += `(${ contact.m })`; - } - return REaCt().createElement(buttons.$, { - ref: ref => { - this.buttonRef = ref; + _generateEmoji(meta) { + const filename = twemoji.convert.toCodePoint(meta.u); + return JSX_("img", { + width: "20", + height: "20", + className: "emoji emoji-loading", + draggable: "false", + alt: meta.u, + title: `:${ meta.n }:`, + onLoad: e => { + e.target.classList.remove('emoji-loading'); }, - className: "tiny-button", - icon: "tiny-icon icons-sprite grey-dots" - }, REaCt().createElement(dropdowns.Dropdown, { - className: "white-context-menu shared-contact-dropdown", - noArrow: true, - positionMy: "left bottom", - positionAt: "right bottom", - horizOffset: 4 - }, REaCt().createElement("div", { - className: "dropdown-avatar rounded" - }, this._getContactAvatar(contact, 'context-avatar'), isAnonView ? REaCt().createElement("div", { - className: "dropdown-user-name" - }) : REaCt().createElement("div", { - className: "dropdown-user-name" - }, REaCt().createElement("div", { - className: "name" - }, HAS_RELATIONSHIP && (this.isLoadingContactInfo() ? REaCt().createElement("em", { - className: "contact-name-loading" - }) : name), !HAS_RELATIONSHIP && name, REaCt().createElement(ui_contacts.ContactPresence, { - className: "small", - contact - })), REaCt().createElement("div", { - className: "email" - }, M.u[contact.u].m))), REaCt().createElement(ui_contacts.ContactFingerprint, { - contact: M.u[contact.u] - }), HAS_RELATIONSHIP && REaCt().createElement(REaCt().Fragment, null, REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-user-filled", - label: l[5868], - onClick: () => { - loadSubPage(`fm/chat/contacts/${ contact.u}`); - mBroadcaster.sendMessage('contact:open'); - } - }), REaCt().createElement("hr", null), REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-chat-filled", - label: l[8632], - onClick: () => { - loadSubPage(`fm/chat/p/${ contact.u}`); - mBroadcaster.sendMessage('chat:open'); - } - })), u_type && u_type > 2 && contact.u !== u_handle && !HAS_RELATIONSHIP && !is_eplusplus && REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-add", - label: l[71], - onClick: () => this._handleAddContact(contactEmail) - }), this._getContactDeleteButton(message))); - } - getContents() { - const { - message, - chatRoom - } = this.props; - const textContents = message.textContents.substr(2, message.textContents.length); - const attachmentMeta = JSON.parse(textContents); - const isAnonView = chatRoom.isAnonymous(); - if (!attachmentMeta) { - return console.error(`Message w/ type: ${message.type} -- no attachment meta defined. Message: ${message}`); - } - let contacts = []; - attachmentMeta.forEach(v => { - let _this$buttonRef; - const contact = M.u && v.u in M.u && M.u[v.u].m ? M.u[v.u] : v; - const contactEmail = contact.email ? contact.email : contact.m; - if (!M.u[contact.u]) { - M.u.set(contact.u, new MegaDataObject(MEGA_USER_STRUCT, { - 'u': contact.u, - 'name': contact.name, - 'm': contact.email ? contact.email : contactEmail, - 'c': undefined - })); - } else if (M.u[contact.u] && !M.u[contact.u].m) { - M.u[contact.u].m = contact.email ? contact.email : contactEmail; - } - contacts = [...contacts, REaCt().createElement("div", { - key: contact.u - }, isAnonView ? REaCt().createElement("div", { - className: "message shared-info" - }) : REaCt().createElement("div", { - className: "message shared-info" - }, REaCt().createElement("div", { - className: "message data-title selectable-txt", - onClick: (_this$buttonRef = this.buttonRef) == null ? void 0 : _this$buttonRef.onClick - }, REaCt().createElement(utils.zT, null, M.getNameByHandle(contact.u))), M.u[contact.u] ? REaCt().createElement(ui_contacts.ContactVerified, { - className: "right-align", - contact: M.u[contact.u] - }) : null, REaCt().createElement("div", { - className: "user-card-email selectable-txt" - }, contactEmail)), REaCt().createElement("div", { - className: "message shared-data" - }, REaCt().createElement("div", { - className: "data-block-view semi-big" - }, M.u[contact.u] ? REaCt().createElement(ui_contacts.ContactPresence, { - className: "small", - contact: M.u[contact.u] - }) : null, this._getContactCard(message, contact, contactEmail), this._getContactAvatar(contact, 'medium-avatar')), REaCt().createElement("div", { - className: "clear" - })))]; + onError: e => { + e.target.classList.remove('emoji-loading'); + e.target.classList.add('emoji-loading-error'); + }, + src: `${staticpath }images/mega/twemojis/2_v2/72x72/${ filename }.png` }); - return REaCt().createElement("div", { - className: "message shared-block" - }, contacts); - } -} -;// ./js/chat/ui/messages/types/attachment.jsx - - - - -class Attachment extends AbstractGenericMessage { - _isRevoked(node) { - return !M.chd[node.ch] || node.revoked; - } - _isUserRegistered() { - return typeof u_type !== 'undefined' && u_type > 2; } - getContents() { - const { - message, - chatRoom - } = this.props; - const contact = this.getContact(); - const NODE_DOESNT_EXISTS_ANYMORE = {}; - const attachmentMeta = message.getAttachmentMeta() || []; - const files = []; - for (let i = 0; i < attachmentMeta.length; i++) { - var _this$buttonRef; - const v = attachmentMeta[i]; - if (this._isRevoked(v)) { - continue; - } - const { - icon, - isImage, - isVideo, - isAudio, - isText, - showThumbnail, - isPreviewable - } = M.getMediaProperties(v); - let dropdown = null; - let noThumbPrev = ''; - var previewButton = null; - if (isPreviewable) { - if (!showThumbnail) { - noThumbPrev = 'no-thumb-prev'; - } - let previewLabel = isAudio ? l[17828] : isVideo ? l[16275] : l[1899]; - let previewIcon = isAudio ? 'icon-play' : isVideo ? 'icon-video-call-filled' : 'icon-preview-reveal'; - if (isText) { - previewLabel = l[16797]; - previewIcon = "icon-file-edit"; + _generateEmojiElement(emoji, cat) { + const self = this; + const categoryName = self.data_categories[cat]; + return JSX_("div", { + "data-emoji": emoji.n, + className: "button square-button emoji", + key: `${categoryName }_${ emoji.n}`, + onMouseEnter: e => { + if (self.mouseEnterTimer) { + clearTimeout(self.mouseEnterTimer); } - previewButton = REaCt().createElement("span", { - key: "previewButton" - }, REaCt().createElement(dropdowns.DropdownItem, { - label: previewLabel, - icon: `sprite-fm-mono ${previewIcon}`, - disabled: mega.paywall, - onClick: e => { - mega.ui.mInfoPanel.hide(); - this.props.onPreviewStart(v, e); - } - })); - } - dropdown = contact.u === u_handle ? REaCt().createElement(buttons.$, { - ref: ref => { - this.buttonRef = ref; - }, - className: "tiny-button", - icon: "tiny-icon icons-sprite grey-dots" - }, REaCt().createElement(dropdowns.Dropdown, { - className: "white-context-menu attachments-dropdown", - noArrow: true, - positionMy: "left top", - positionAt: "left bottom", - horizOffset: -4, - vertOffset: 3, - onBeforeActiveChange: newState => { - if (newState === true) { - this.forceUpdate(); - } - }, - dropdownItemGenerator: dd => { - const linkButtons = []; - const firstGroupOfButtons = []; - let revokeButton = null; - let downloadButton = null; - let addToAlbumButton = null; - if (message.isEditable && message.isEditable()) { - revokeButton = REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-dialog-close", - label: l[83], - onClick: () => { - chatRoom.megaChat.plugins.chatdIntegration.updateMessage(chatRoom, message.internalId || message.orderValue, ""); - } - }); - } - if (!M.d[v.h] && !NODE_DOESNT_EXISTS_ANYMORE[v.h]) { - dbfetch.acquire(v.h).always(() => { - if (!M.d[v.h]) { - NODE_DOESNT_EXISTS_ANYMORE[v.h] = true; - dd.doRerender(); - } else { - dd.doRerender(); - } - }); - return REaCt().createElement("span", { - className: "loading" - }, l[5533]); - } else if (!NODE_DOESNT_EXISTS_ANYMORE[v.h]) { - downloadButton = REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-download-small", - label: l[1187], - disabled: mega.paywall, - onClick: () => this.props.onDownloadStart(v) - }); - if (M.getNodeRoot(v.h) !== M.RubbishID) { - this.props.onAddLinkButtons(v.h, linkButtons); - } - firstGroupOfButtons.push(REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-info", - label: l[6859], - key: "infoDialog", - onClick: () => { - mega.ui.mInfoPanel.show([v.ch]); - } - })); - this.props.onAddFavouriteButtons(v.h, firstGroupOfButtons); - linkButtons.push(REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-send-to-chat", - label: l[17764], - key: "sendToChat", - disabled: mega.paywall, - onClick: () => { - $.selected = [v.h]; - openSendToChatDialog(); - } - })); - if (M.isGalleryNode(v)) { - addToAlbumButton = REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono rectangle-stack-plus-small-regular-outline", - label: l.add_to_album, - disabled: mega.paywall, - onClick: () => mega.gallery.albums.addToAlbum([v.h]) - }); - } - } - if (!previewButton && firstGroupOfButtons.length === 0 && !downloadButton && !addToAlbumButton && linkButtons.length === 0 && !revokeButton) { - return null; - } - if (previewButton && (firstGroupOfButtons.length > 0 || downloadButton || addToAlbumButton || linkButtons.length > 0 || revokeButton)) { - previewButton = [previewButton, REaCt().createElement("hr", { - key: "preview-sep" - })]; - } - return REaCt().createElement("div", null, previewButton, firstGroupOfButtons, firstGroupOfButtons && firstGroupOfButtons.length > 0 ? REaCt().createElement("hr", null) : "", addToAlbumButton, addToAlbumButton ? REaCt().createElement("hr", null) : "", downloadButton, linkButtons, revokeButton && downloadButton ? REaCt().createElement("hr", null) : "", revokeButton); + e.stopPropagation(); + e.preventDefault(); + self.mouseEnterTimer = setTimeout(() => { + self.setState({ + 'previewEmoji': emoji + }); + }, 250); + }, + onMouseLeave: e => { + if (self.mouseEnterTimer) { + clearTimeout(self.mouseEnterTimer); } - })) : REaCt().createElement(buttons.$, { - ref: ref => { - this.buttonRef = ref; - }, - className: "tiny-button", - icon: "tiny-icon icons-sprite grey-dots" - }, REaCt().createElement(dropdowns.Dropdown, { - className: "white-context-menu attachments-dropdown", - noArrow: true, - positionMy: "left top", - positionAt: "left bottom", - horizOffset: -4, - vertOffset: 3 - }, previewButton, previewButton && REaCt().createElement("hr", null), REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-download-small", - label: l[1187], - disabled: mega.paywall, - onClick: () => this.props.onDownloadStart(v) - }), !is_chatlink && this._isUserRegistered() && REaCt().createElement(REaCt().Fragment, null, REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-cloud", - label: l[1988], - disabled: mega.paywall, - onClick: () => this.props.onAddToCloudDrive(v, false) - }), REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-send-to-chat", - label: l[17764], - disabled: mega.paywall, - onClick: () => this.props.onAddToCloudDrive(v, true) - })))); - if (M.getNodeShare(v.h).down) { - dropdown = null; - } - const attachmentClasses = "message shared-data"; - let preview = REaCt().createElement("div", { - className: `data-block-view medium ${ noThumbPrev}`, - onClick: ({ - target - }) => { - if (isPreviewable && !target.classList.contains('tiny-button')) { - mega.ui.mInfoPanel.hide(); - this.props.onPreviewStart(v); - } + e.stopPropagation(); + e.preventDefault(); + self.setState({ + 'previewEmoji': null + }); + }, + onClick: e => { + if (self.props.onClick) { + self.props.onClick(e, emoji.n, emoji); + $(document).trigger('closeDropdowns'); } - }, dropdown, REaCt().createElement("div", { - className: "data-block-bg" - }, REaCt().createElement("div", { - className: `item-type-icon-90 icon-${ icon }-90` - }))); - if (showThumbnail) { - const src = v.src || window.noThumbURI || ''; - let thumbClass = v.src ? '' : " no-thumb"; - let thumbOverlay = null; - if (isImage) { - thumbClass += " image"; - thumbOverlay = REaCt().createElement("div", { - className: "thumb-overlay", - onClick: () => { - mega.ui.mInfoPanel.hide(); - this.props.onPreviewStart(v); - } + } + }, self._generateEmoji(emoji)); + } + UNSAFE_componentWillUpdate(nextProps, nextState) { + if (nextState.searchValue !== this.state.searchValue || nextState.browsingCategories !== this.state.browsingCategories) { + this._cachedNodes = {}; + if (this.scrollableArea) { + this.scrollableArea.scrollToY(0); + } + this._onScrollChanged(0, nextState); + } + if (nextState.isActive === true) { + const self = this; + if (nextState.isLoading === true || !self.loadingPromise && (!self.data_categories || !self.data_emojis)) { + const p = [megaChat.getEmojiDataSet('categories'), megaChat.getEmojiDataSet('emojis')]; + this.loadingPromise = Promise.all(p).then(([categories, emojis]) => { + this.data_emojis = emojis; + this.data_categories = categories; + self.data_categories.push('frequently_used'); + self.data_categoriesWithCustomOrder = []; + self.customCategoriesOrder.forEach((catName) => { + self.data_categoriesWithCustomOrder.push(self.data_categories.indexOf(catName)); }); - } else { - thumbClass = `${thumbClass } video ${ isPreviewable ? " previewable" : "non-previewable"}`; - thumbOverlay = REaCt().createElement("div", { - className: "thumb-overlay", - onClick: () => { - if (isPreviewable) { - mega.ui.mInfoPanel.hide(); - this.props.onPreviewStart(v); - } + self.data_emojiByCategory = {}; + const frequentlyUsedEmojisMeta = {}; + self.data_emojis.forEach((emoji) => { + const cat = emoji.c; + if (!self.data_emojiByCategory[cat]) { + self.data_emojiByCategory[cat] = []; } - }, isPreviewable && REaCt().createElement("div", { - className: "thumb-overlay-play" - }, REaCt().createElement("div", { - className: "thumb-overlay-circle" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-play" - }))), REaCt().createElement("div", { - className: "video-thumb-details" - }, v.playtime && REaCt().createElement("i", { - className: "sprite-fm-mono icon-play" - }), REaCt().createElement("span", null, secondsToTimeShort(v.playtime || -1)))); - } - preview = src ? REaCt().createElement("div", { - id: v.ch, - className: `shared-link thumb ${thumbClass}` - }, thumbOverlay, dropdown, REaCt().createElement("img", { - alt: "", - className: `thumbnail-placeholder ${ v.h}`, - src, - key: `thumb-${ v.ch}`, - onClick: () => isPreviewable && this.props.onPreviewStart(v) - })) : preview; - } - files.push(REaCt().createElement("div", { - key: `attachment-${v.ch}`, - className: attachmentClasses - }, REaCt().createElement("div", { - className: "message shared-info", - onClick: (_this$buttonRef = this.buttonRef) == null ? void 0 : _this$buttonRef.onClick - }, REaCt().createElement("div", { - className: "message data-title selectable-txt" - }, l[17669], REaCt().createElement("span", { - className: "file-name" - }, v.name)), REaCt().createElement("div", { - className: "message file-size" - }, bytesToSize(v.s))), preview, REaCt().createElement("div", { - className: "clear" - }))); - } - return REaCt().createElement("div", { - className: "message shared-block" - }, files); - } -} -// EXTERNAL MODULE: ./js/chat/mixins.js -const mixins = REQ_(137); -;// ./js/chat/ui/messages/types/partials/audioPlayer.jsx - - -class AudioPlayer extends mixins.w9 { - constructor(props) { - super(props); - this.domRef = REaCt().createRef(); - this.state = { - currentTime: null, - progressWidth: 0, - isBeingPlayed: false, - isPaused: false - }; - this.handleOnTimeUpdate = this.handleOnTimeUpdate.bind(this); - this.handleOnMouseDown = this.handleOnMouseDown.bind(this); - } - play() { - const audio = this.audioEl; - if (audio.paused) { - const result = audio.play(); - if (result instanceof Promise) { - result.catch(ex => { - if (ex.name !== 'AbortError') { - console.error(ex); + if (self.frequentlyUsedEmojis.indexOf(emoji.n) > -1) { + frequentlyUsedEmojisMeta[emoji.n] = emoji.u; + } + emoji.element = self._generateEmojiElement(emoji, cat); + self.data_emojiByCategory[cat].push(emoji); + }); + self.data_emojiByCategory[8] = []; + self.frequentlyUsedEmojis.forEach((slug) => { + const emoji = { + 'n': slug, + 'u': frequentlyUsedEmojisMeta[slug] + }; + emoji.element = self._generateEmojiElement(emoji, 99); + self.data_emojiByCategory[8].push(emoji); + }); + self._onScrollChanged(0); + self.setState({ + 'isLoading': false + }); + }).catch(ex => { + if (d) { + console.error("Emoji loading failed.", ex); } + this.setState({ + 'loadFailed': true, + 'isLoading': false + }); }); } - const audios = document.getElementsByClassName('audio-player__player'); - Array.prototype.filter.call(audios, audioElement => audioElement.id !== this.props.audioId).forEach(audioElement => { - if (!audioElement.paused) { - audioElement.pause(); + } else if (nextState.isActive === false) { + if (this.data_emojis) { + for (let i = this.data_emojis.length; i--;) { + delete this.data_emojis[i].element; } - }); - this.setState({ - isPaused: false - }); - } else { - audio.pause(); - this.setState({ - isPaused: true - }); + } + this.data_emojis = null; + this.data_categories = null; + this.data_emojiByCategory = null; + this.loadingPromise = null; } } - handleOnTimeUpdate() { - const { - currentTime, - duration - } = this.audioEl; - this.setState({ - currentTime: secondsToTimeShort(currentTime), - progressWidth: currentTime / duration * 100 + onSearchChange(e) { + const self = this; + self.setState({ + searchValue: e.target.value, + browsingCategory: false }); } - handleOnMouseDown(event) { - event.preventDefault(); - const { - sliderPin, - slider - } = this; - const shiftX = event.clientX - sliderPin.getBoundingClientRect().left; - const onMouseMove = event => { - let newLeft = event.clientX - shiftX - slider.getBoundingClientRect().left; - if (newLeft < 0) { - newLeft = 0; - } - const rightEdge = slider.offsetWidth - sliderPin.offsetWidth; - if (newLeft > rightEdge) { - newLeft = rightEdge; - } - sliderPin.style.left = `${newLeft}px`; - const pinPosition = newLeft / slider.getBoundingClientRect().width; - const newTime = Math.ceil(this.props.playtime * pinPosition); - const newCurrentTime = secondsToTimeShort(newTime); - this.audioEl.currentTime = newTime; - this.setState({ - currentTime: newCurrentTime, - progressWidth: pinPosition > 1 ? 100 : pinPosition * 100 - }); - }; - function onMouseUp() { - document.removeEventListener('mouseup', onMouseUp); - document.removeEventListener('mousemove', onMouseMove); + onUserScroll($ps) { + if (this.state.browsingCategory) { + const $cat = $(`.emoji-category-container[data-category-name="${ this.state.browsingCategory }"]`); + if (!elementInViewport($cat)) { + this.setState({ + 'browsingCategory': false + }); + } } - document.addEventListener('mousemove', onMouseMove); - document.addEventListener('mouseup', onMouseUp); - sliderPin.ondragstart = () => false; + this._onScrollChanged($ps.getScrollPositionY()); } - render() { - const { - source, - audioId, - loading, - playtime - } = this.props; - const { - progressWidth, - isBeingPlayed, - isPaused, - currentTime - } = this.state; - let playtimeStyles = null; - if (isBeingPlayed) { - playtimeStyles = { - color: 'var(--secondary-red)' - }; + generateEmojiElementsByCategory(categoryId, posTop, stateObj) { + const self = this; + if (!self._cachedNodes) { + self._cachedNodes = {}; } - let btnClass = 'icon-pause'; - if (!isBeingPlayed || isPaused) { - btnClass = 'icon-play'; + if (!stateObj) { + stateObj = self.state; } - let controls = REaCt().createElement("span", { - onClick: () => { - this.play(); - if (this.props.source === null) { - this.props.getAudioFile(); - } - } - }, REaCt().createElement("i", { - className: `sprite-fm-mono ${btnClass}` - })); - if (loading) { - controls = REaCt().createElement("div", { - className: "small-blue-spinner audio-player__spinner" - }); + if (typeof self._cachedNodes[categoryId] !== 'undefined') { + return self._cachedNodes[categoryId]; } - return REaCt().createElement("div", { - ref: this.domRef, - className: "audio-player" - }, controls, REaCt().createElement("div", { - className: "slider", - ref: slider => { - this.slider = slider; - } - }, REaCt().createElement("div", { - className: "slider__progress", - style: { - width: `${progressWidth}%` + const categoryName = self.data_categories[categoryId]; + const emojis = []; + const {searchValue} = stateObj; + let totalEmojis = 0; + self.data_emojiByCategory[categoryId].forEach((meta) => { + const slug = meta.n; + if (searchValue.length > 0) { + if (`:${ slug }:`.toLowerCase().indexOf(searchValue.toLowerCase()) < 0) { + return; + } } - }), REaCt().createElement("div", { - className: "slider__progress__pin", - style: { - left: `${progressWidth}%` - }, - ref: sliderPin => { - this.sliderPin = sliderPin; - }, - onMouseDown: this.handleOnMouseDown - })), REaCt().createElement("span", { - className: "audio-player__time", - style: playtimeStyles - }, currentTime || secondsToTimeShort(playtime)), REaCt().createElement("audio", { - src: source, - className: "audio-player__player", - id: audioId, - ref: audio => { - this.audioEl = audio; - }, - onPlaying: () => this.setState({ - isBeingPlayed: true - }), - onPause: () => this.setState({ - isPaused: true - }), - onEnded: () => this.setState({ - progressWidth: 0, - isBeingPlayed: false, - currentTime: 0 - }), - onTimeUpdate: this.handleOnTimeUpdate - })); - } -} -;// ./js/chat/ui/messages/types/partials/audioContainer.jsx - - -class AudioContainer extends REaCt().Component { - constructor(props) { - super(props); - this.state = { - audioBlobUrl: null, - loading: false - }; - this.getAudioFile = this.getAudioFile.bind(this); - } - getAudioFile() { - const { - mime, - h - } = this.props; - this.setState({ - loading: true + totalEmojis++; + emojis.push(meta.element); }); - if (mime !== 'audio/mp4') { - if (d) { - console.warn('cannot play this file type (%s)', mime, h, [this]); - } - return false; + if (emojis.length > 0) { + const totalHeight = self.heightDefs.categoryTitleHeight + Math.ceil(totalEmojis / self.heightDefs.numberOfEmojisPerRow) * self.heightDefs.emojiRowHeight; + return self._cachedNodes[categoryId] = [totalHeight, JSX_("div", { + key: categoryName, + "data-category-name": categoryName, + className: "emoji-category-container", + style: { + 'position': 'absolute', + 'top': posTop + } + }, emojis.length > 0 ? JSX_("div", { + className: "clear" + }) : null, JSX_("div", { + className: "emoji-type-txt" + }, self.categoryLabels[categoryName] ? self.categoryLabels[categoryName] : categoryName), JSX_("div", { + className: "clear" + }), emojis, JSX_("div", { + className: "clear" + }))]; + } else { + return self._cachedNodes[categoryId] = undefined; } - M.gfsfetch(h, 0, -1).then(({ - buffer - }) => { - this.setState(() => { - return { - audioBlobUrl: mObjectURL([buffer], 'audio/mp4'), - loading: false - }; - }); - }).catch(ex => { - console.error(ex); - }); - return true; - } - componentWillUnmount() { - URL.revokeObjectURL(this.state.audioBlobUrl); } - render() { - const { - audioBlobUrl, - loading - } = this.state; - const { - playtime, - mime, - audioId - } = this.props; - return REaCt().createElement("div", { - className: "audio-container" - }, REaCt().createElement(AudioPlayer, { - source: audioBlobUrl, - audioId, - loading, - mime, - getAudioFile: this.getAudioFile, - playtime - })); + _isVisible(scrollTop, scrollBottom, elTop, elBottom) { + const visibleTop = elTop < scrollTop ? scrollTop : elTop; + const visibleBottom = elBottom > scrollBottom ? scrollBottom : elBottom; + return visibleBottom - visibleTop > 0; } -} -AudioContainer.defaultProps = { - h: null, - mime: null -}; -;// ./js/chat/ui/messages/types/voiceClip.jsx - - - - - -class VoiceClip extends AbstractGenericMessage { - _getActionButtons() { - const { - isBeingEdited, - chatRoom, - message, - dialog, - onDelete - } = this.props; - if (message.isEditable() && !isBeingEdited() && !chatRoom.isReadOnly() && !dialog) { - return REaCt().createElement(buttons.$, { - className: "tiny-button", - icon: "tiny-icon icons-sprite grey-dots" - }, REaCt().createElement(dropdowns.Dropdown, { - className: "white-context-menu attachments-dropdown", - noArrow: true, - positionMy: "left bottom", - positionAt: "right bottom", - horizOffset: 4 - }, REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-dialog-close", - label: l[1730], - onClick: ev => onDelete(ev, message) - }))); + _onScrollChanged(scrollPositionY, stateObj) { + const self = this; + if (!self.data_categoriesWithCustomOrder) { + return; } - return null; - } - _getAudioContainer() { - const { - message - } = this.props; - const attachmentMeta = message.getAttachmentMeta(); - if (attachmentMeta && attachmentMeta.length) { - return attachmentMeta.map(voiceClip => REaCt().createElement(AudioContainer, { - key: voiceClip.h, - h: voiceClip.h, - mime: voiceClip.mime, - playtime: voiceClip.playtime, - audioId: `vm${message.messageId}` - })); + if (scrollPositionY === false) { + scrollPositionY = self.scrollableArea.getScrollPositionY(); } - } - getContents() { - return REaCt().createElement(REaCt().Fragment, null, this.props.message.getState() === Message.STATE.NOT_SENT ? null : this._getActionButtons(), this._getAudioContainer()); - } -} -;// ./js/chat/ui/messages/types/partials/metaRichpreview.jsx -const React = REQ_(594); -const ConversationMessageMixin = REQ_(446).M; -const MetaRichPreviewLoading = REQ_(707).a; -class MetaRichpreview extends ConversationMessageMixin { - getBase64Url(b64incoming) { - if (!b64incoming || !b64incoming.split) { - return; + if (!stateObj) { + stateObj = self.state; + } + const visibleStart = scrollPositionY; + const visibleEnd = visibleStart + self.heightDefs.containerHeight; + let currentPos = 0; + let visibleCategories = []; + self._emojiReactElements = []; + self.data_categoryPositions = {}; + self.data_categoriesWithCustomOrder.forEach((k) => { + const categoryDivMeta = self.generateEmojiElementsByCategory(k, currentPos, stateObj); + if (categoryDivMeta) { + const startPos = currentPos; + currentPos += categoryDivMeta[0]; + const endPos = currentPos; + self.data_categoryPositions[k] = startPos; + if (self._isVisible(visibleStart, visibleEnd, startPos, endPos)) { + visibleCategories.push(k); + self._emojiReactElements.push(categoryDivMeta[1]); + } + } + }); + if (self._emojiReactElements.length === 0) { + const emojisNotFound = JSX_("span", { + className: "emojis-not-found", + key: 'emojis-not-found' + }, l[20920]); + self._emojiReactElements.push(emojisNotFound); } - let exti = b64incoming.split(":"); - const b64i = exti[1]; - exti = exti[0]; - return `data:image/${ exti };base64,${ b64i}`; + visibleCategories = visibleCategories.join(','); + self.setState({ + 'totalScrollHeight': currentPos, + visibleCategories + }); } - render() { + _renderEmojiPickerPopup() { const self = this; - const {message} = this.props; - const output = []; - const metas = message.meta && message.meta.extra ? message.meta.extra : []; - const failedToLoad = message.meta.isLoading && unixtime() - message.meta.isLoading > 300; - const isLoading = !!message.meta.isLoading; - if (failedToLoad) { - return null; + let preview; + if (self.state.previewEmoji) { + const meta = self.state.previewEmoji; + preview = JSX_("div", { + className: "emoji-preview" + }, self._generateEmoji(meta), JSX_("div", { + className: "emoji title" + }, `:${ meta.n }:`)); } - for (let i = 0; i < metas.length; i++) { - const meta = metas[i]; - if (!meta.d && !meta.t && !message.meta.isLoading) { - continue; - } - const previewCss = {}; - if (meta.i) { - previewCss.backgroundImage = `url(${ self.getBase64Url(meta.i) })`; - previewCss.backgroundRepeat = "no-repeat"; - previewCss.backgroundPosition = "center center"; + const categoryIcons = { + "frequently_used": "icon-emoji-type-frequent", + "people": "icon-emoji-type-people", + "nature": "icon-emoji-type-nature", + "food": "icon-emoji-type-food", + "activity": "icon-emoji-type-activity", + "travel": "icon-emoji-type-travel", + "objects": "icon-emoji-type-objects", + "symbols": "icon-emoji-type-symbol", + "flags": "icon-emoji-type-flag" + }; + const categoryButtons = []; + let activeCategoryName = false; + if (!self.state.searchValue) { + const firstActive = self.state.visibleCategories.split(",")[0]; + if (firstActive) { + activeCategoryName = self.data_categories[firstActive]; } - var previewContainer; - if (isLoading) { - previewContainer = React.createElement(MetaRichPreviewLoading, { - message, - isLoading: message.meta.isLoading - }); - } else { - let domainName = meta.url; - domainName = domainName.replace("https://", "").replace("http://", "").split("/")[0]; - previewContainer = React.createElement("div", { - className: "message richpreview body" - }, meta.i ? React.createElement("div", { - className: "message richpreview img-wrapper" - }, React.createElement("div", { - className: "message richpreview preview", - style: previewCss - })) : undefined, React.createElement("div", { - className: "message richpreview inner-wrapper" - }, React.createElement("div", { - className: "message richpreview data-title selectable-txt" - }, React.createElement("span", { - className: "message richpreview title" - }, meta.t)), React.createElement("div", { - className: "message richpreview desc" - }, ellipsis(meta.d, 'end', 82)), React.createElement("div", { - className: "message richpreview url-container" - }, meta.ic ? React.createElement("span", { - className: "message richpreview url-favicon" - }, React.createElement("img", { - src: self.getBase64Url(meta.ic), - width: 16, - height: 16, - onError: e => { - e.target.parentNode.removeChild(e.target); - }, - alt: "" - })) : "", React.createElement("span", { - className: "message richpreview url" - }, domainName)))); - } - output.push(React.createElement("div", { - key: meta.url, - className: `message richpreview container ${ meta.i ? "have-preview" : "no-preview" } ${ meta.d ? "have-description" : "no-description" } ${ isLoading ? "is-loading" : "done-loading"}`, - onClick: function (url) { - if (!message.meta.isLoading) { - window.open(url, "_blank", 'noopener,noreferrer'); - } - }.bind(this, meta.url) - }, previewContainer, React.createElement("div", { - className: "clear" - }))); } - return React.createElement("div", { - className: "message richpreview previews-container" - }, output); - } -} - -;// ./js/chat/ui/messages/types/partials/metaRichpreviewConfirmation.jsx -const metaRichpreviewConfirmation_React = REQ_(594); -const metaRichpreviewConfirmation_ConversationMessageMixin = REQ_(446).M; -class MetaRichprevConfirmation extends metaRichpreviewConfirmation_ConversationMessageMixin { - doAllow() { - const {message} = this.props; - const {megaChat} = this.props.message.chatRoom; - delete message.meta.requiresConfirmation; - RichpreviewsFilter.confirmationDoConfirm(); - megaChat.plugins.richpreviewsFilter.processMessage({}, message); - message.trackDataChange(); - } - doNotNow() { - const {message} = this.props; - delete message.meta.requiresConfirmation; - RichpreviewsFilter.confirmationDoNotNow(); - message.trackDataChange(); - } - doNever() { - const {message} = this.props; - msgDialog('confirmation', l[870], l[18687], '', (e) => { - if (e) { - delete message.meta.requiresConfirmation; - RichpreviewsFilter.confirmationDoNever(); - message.trackDataChange(); - } + self.customCategoriesOrder.forEach(categoryName => { + categoryButtons.push(JSX_("div", { + visiblecategories: this.state.visibleCategories, + className: ` + button square-button emoji + ${activeCategoryName === categoryName ? 'active' : ''} + `, + key: categoryIcons[categoryName], + onClick: e => { + e.stopPropagation(); + e.preventDefault(); + this.setState({ + browsingCategory: categoryName, + searchValue: '' + }); + this._cachedNodes = {}; + const categoryPosition = this.data_categoryPositions[this.data_categories.indexOf(categoryName)] + 10; + this.scrollableArea.scrollToY(categoryPosition); + this._onScrollChanged(categoryPosition); + const { + current + } = this.emojiSearchRef || !1; + current == null || current.focus(); + } + }, JSX_("i", { + className: `sprite-fm-mono ${categoryIcons[categoryName]}` + }))); }); + return JSX_(react1___default().Fragment, null, JSX_("div", { + className: "popup-header emoji" + }, preview || JSX_("div", { + className: "search-block emoji" + }, JSX_("i", { + className: "sprite-fm-mono icon-preview-reveal" + }), JSX_("input", { + ref: this.emojiSearchRef, + type: "search", + placeholder: l[102], + onChange: this.onSearchChange, + autoFocus: true, + value: this.state.searchValue + }))), JSX_(_perfectScrollbar_jsx4__ .O, { + className: "popup-scroll-area emoji perfectScrollbarContainer", + searchValue: this.state.searchValue, + onUserScroll: this.onUserScroll, + visibleCategories: this.state.visibleCategories, + ref: ref => { + this.scrollableArea = ref; + } + }, JSX_("div", { + className: "popup-scroll-content emoji" + }, JSX_("div", { + style: { + height: this.state.totalScrollHeight + } + }, this._emojiReactElements))), JSX_("div", { + className: "popup-footer emoji" + }, categoryButtons)); } render() { const self = this; - let notNowButton = null; - let neverButton = null; - if (RichpreviewsFilter.confirmationCount >= 2) { - neverButton = metaRichpreviewConfirmation_React.createElement("button", { - className: "mega-button right negative", - onClick () { - self.doNever(); - } - }, metaRichpreviewConfirmation_React.createElement("span", null, l[1051])); - } - notNowButton = metaRichpreviewConfirmation_React.createElement("button", { - className: "mega-button right", - onClick () { - self.doNotNow(); - } - }, metaRichpreviewConfirmation_React.createElement("span", null, l[18682])); - return metaRichpreviewConfirmation_React.createElement("div", { - className: "message richpreview previews-container" - }, metaRichpreviewConfirmation_React.createElement("div", { - className: "message richpreview container confirmation" - }, metaRichpreviewConfirmation_React.createElement("div", { - className: "message richpreview body" - }, metaRichpreviewConfirmation_React.createElement("div", { - className: "message richpreview img-wrapper" - }, metaRichpreviewConfirmation_React.createElement("div", { - className: " message richpreview preview-confirmation sprite-fm-illustration img-chat-url-preview " - })), metaRichpreviewConfirmation_React.createElement("div", { - className: "message richpreview inner-wrapper" - }, metaRichpreviewConfirmation_React.createElement("div", { - className: "message richpreview data-title selectable-txt" - }, metaRichpreviewConfirmation_React.createElement("span", { - className: "message richpreview title" - }, l[18679])), metaRichpreviewConfirmation_React.createElement("div", { - className: "message richpreview desc" - }, l[18680])), metaRichpreviewConfirmation_React.createElement("div", { - className: "buttons-block" - }, metaRichpreviewConfirmation_React.createElement("button", { - className: "mega-button right positive", - onClick: () => { - self.doAllow(); + let popupContents = null; + if (self.state.isActive === true) { + if (self.state.loadFailed === true) { + popupContents = JSX_("div", { + className: "loading" + }, l[1514]); + } else if (this.state.isLoading || !this.data_emojiByCategory || !this.data_categories) { + popupContents = JSX_("div", { + className: "loading" + }, l[5533]); + } else { + popupContents = self._renderEmojiPickerPopup(); } - }, metaRichpreviewConfirmation_React.createElement("span", null, l[18681])), notNowButton, neverButton)), metaRichpreviewConfirmation_React.createElement("div", { - className: "clear" - }))); - } -} - -;// ./js/chat/ui/messages/types/partials/geoLocation.jsx - -function GeoLocation(props) { - const { - latitude, - lng - } = props; - const handleOnclick = (lat, lng) => { - const openGmaps = () => { - window.open(`https://www.google.com/maps/search/?api=1&query=${lat},${lng}`, '_blank', 'noopener,noreferrer'); - }; - if (GeoLocationLinks.gmapsConfirmation === -1 || GeoLocationLinks.gmapsConfirmation === false) { - msgDialog('confirmation', 'geolocation-link', l[20788], l.confirm_ext_link, answer => { - if (answer) { - GeoLocationLinks.confirmationDoConfirm(); - closeDialog(); - openGmaps(); - } else { - GeoLocationLinks.confirmationDoNever(); - } - }); - } else if (GeoLocationLinks.gmapsConfirmation) { - openGmaps(); + } else { + popupContents = null; } - }; - return REaCt().createElement("div", { - className: "geolocation-container" - }, REaCt().createElement("div", { - className: "geolocation", - onClick: () => handleOnclick(latitude, lng) - }, REaCt().createElement("div", { - className: "geolocation__details" - }, REaCt().createElement("div", { - className: "geolocation__icon" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-location" - })), REaCt().createElement("ul", { - className: "geolocation__data-list" - }, REaCt().createElement("li", null, REaCt().createElement("span", { - className: "geolocation__title" - }, l[20789])), REaCt().createElement("li", null, REaCt().createElement("p", null, REaCt().createElement("span", { - className: "geolocation__coordinates-icon" - }), REaCt().createElement("span", { - className: "geolocation__coordinates" - }, "https://maps.google.com"))))))); -} -const geoLocation = GeoLocation; -// EXTERNAL MODULE: ./js/chat/ui/messages/types/partials/metaRichPreviewLoading.jsx -const metaRichPreviewLoading = REQ_(707); -;// ./js/chat/ui/messages/types/partials/metaRichpreviewMegaLinks.jsx - - - - - -class MetaRichpreviewMegaLinks extends mixin.M { - render() { - const {message} = this.props; - const {chatRoom} = this.props.message; - let previewContainer; - const output = []; - const megaLinks = message.megaLinks ? message.megaLinks : []; - for (let i = 0; i < megaLinks.length; i++) { - const megaLinkInfo = megaLinks[i]; - if (megaLinkInfo.failed) { - continue; - } - if (megaLinkInfo.hadLoaded() === false) { - if (megaLinkInfo.startedLoading() === false) { - megaLinkInfo.getInfo().then(() => { - const { - megaLinks - } = this.props.message; - const contactLinkHandles = megaLinks.filter(link => link.is_contactlink).map(link => link.info.h); - if (contactLinkHandles.length) { - this.addContactListenerIfMissing(contactLinkHandles); - } - }).catch(reportError).finally(() => { - message.trackDataChange(); - onIdle(() => { - this.safeForceUpdate(); - }); + return JSX_(_dropdowns_jsx3__ .ms, (0,_babel_runtime_helpers_extends0__ .A)({ + className: "popup emoji" + }, self.props, { + isLoading: self.state.isLoading, + loadFailed: self.state.loadFailed, + visibleCategories: this.state.visibleCategories, + forceShowWhenEmpty: true, + onActiveChange: newValue => { + if (newValue === false) { + self.setState(self.getInitialState()); + self._cachedNodes = {}; + self._onScrollChanged(0); + } else { + self.setState({ + 'isActive': true }); } - previewContainer = REaCt().createElement(metaRichPreviewLoading.a, { - message, - isLoading: megaLinkInfo.hadLoaded() - }); - } else if (megaLinkInfo.is_contactlink) { - const fakeContact = M.u[megaLinkInfo.info.h] ? M.u[megaLinkInfo.info.h] : { - 'u': megaLinkInfo.info.h, - 'm': megaLinkInfo.info.e, - 'firstName': megaLinkInfo.info.fn, - 'lastName': megaLinkInfo.info.ln, - 'name': `${megaLinkInfo.info.fn } ${ megaLinkInfo.info.ln}` - }; - if (!M.u[fakeContact.u]) { - M.u.set(fakeContact.u, new MegaDataObject(MEGA_USER_STRUCT, { - 'u': fakeContact.u, - 'name': `${fakeContact.firstName } ${ fakeContact.lastName}`, - 'm': fakeContact.m ? fakeContact.m : "", - 'c': undefined - })); - } - const contact = M.u[megaLinkInfo.info.h]; - previewContainer = REaCt().createElement("div", { - key: megaLinkInfo.info.h, - className: "message shared-block contact-link" - }, REaCt().createElement("div", { - className: "message shared-info" - }, REaCt().createElement("div", { - className: "message data-title selectable-txt" - }, contact.name), REaCt().createElement(ui_contacts.ContactVerified, { - className: "right-align", - contact - }), REaCt().createElement("div", { - className: "user-card-email selectable-txt" - }, contact.m)), REaCt().createElement("div", { - className: "message shared-data" - }, REaCt().createElement("div", { - className: "data-block-view semi-big" - }, REaCt().createElement(ui_contacts.ContactPresence, { - className: "small", - contact - }), REaCt().createElement(ui_contacts.Avatar, { - className: "avatar-wrapper medium-avatar", - contact, - chatRoom - })), REaCt().createElement("div", { - className: "clear" - }))); - } else { - var desc; - const is_icon = megaLinkInfo.is_dir ? true : !(megaLinkInfo.havePreview() && megaLinkInfo.info.preview_url); - if (megaLinkInfo.is_chatlink) { - desc = l[8876].replace('%1', megaLinkInfo.info.ncm); - } else if (!megaLinkInfo.is_dir) { - desc = bytesToSize(megaLinkInfo.info.size); - } else { - const totalNumberOfFiles = megaLinkInfo.info.s[1]; - const numOfVersionedFiles = megaLinkInfo.info.s[4]; - const folderCount = megaLinkInfo.info.s[2]; - const totalFileSize = megaLinkInfo.info.size; - const versionsSize = megaLinkInfo.info.s[3]; - desc = REaCt().createElement("span", null, fm_contains(totalNumberOfFiles - numOfVersionedFiles, folderCount - 1), REaCt().createElement("br", null), bytesToSize(totalFileSize - versionsSize)); + if (self.props.onActiveChange) { + self.props.onActiveChange(newValue); } - previewContainer = REaCt().createElement("div", { - className: `message richpreview body ${ is_icon ? "have-icon" : "no-icon" } ${ megaLinkInfo.is_chatlink ? "is-chat" : ""}` - }, megaLinkInfo.havePreview() && megaLinkInfo.info.preview_url ? REaCt().createElement("div", { - className: "message richpreview img-wrapper" - }, REaCt().createElement("div", { - className: "message richpreview preview", - style: { - "backgroundImage": `url(${ megaLinkInfo.info.preview_url })` - } - })) : REaCt().createElement("div", { - className: "message richpreview img-wrapper" - }, megaLinkInfo.is_chatlink ? REaCt().createElement("i", { - className: "huge-icon conversations" - }) : REaCt().createElement("div", { - className: `message richpreview icon item-type-icon-90 icon-${ megaLinkInfo.is_dir ? "folder" : fileIcon(megaLinkInfo.info) }-90` - })), REaCt().createElement("div", { - className: "message richpreview inner-wrapper" - }, REaCt().createElement("div", { - className: "message richpreview data-title selectable-txt" - }, REaCt().createElement("span", { - className: "message richpreview title" - }, REaCt().createElement(utils.zT, null, megaLinkInfo.info.name || megaLinkInfo.info.topic || ""))), REaCt().createElement("div", { - className: "message richpreview desc" - }, desc), REaCt().createElement("div", { - className: "message richpreview url-container" - }, REaCt().createElement("span", { - className: "message richpreview url-favicon" - }, REaCt().createElement("img", { - src: `https://mega.${mega.tld}/favicon.ico?v=3&c=1`, - width: 16, - height: 16, - onError: e => { - if (e && e.target && e.target.parentNode) { - e.target.parentNode.removeChild(e.target); - } - }, - alt: "" - })), REaCt().createElement("span", { - className: "message richpreview url" - }, ellipsis(megaLinkInfo.getLink(), 'end', 40))))); - } - output.push(REaCt().createElement("div", { - key: `${megaLinkInfo.node_key }_${ output.length}`, - className: `message richpreview container ${ megaLinkInfo.havePreview() ? "have-preview" : "no-preview" } ${ megaLinkInfo.d ? "have-description" : "no-description" } ${ !megaLinkInfo.hadLoaded() ? "is-loading" : "done-loading"}`, - onClick: function (url, megaLinkInfo) { - if (megaLinkInfo.hadLoaded()) { - if (window.sfuClient && megaLinkInfo.is_chatlink) { - const { - chatRoom: callRoom - } = megaChat.activeCall; - const peers = callRoom ? callRoom.getParticipantsExceptMe(callRoom.getCallParticipants()).map(h => M.getNameByHandle(h)) : []; - const body = peers.length ? mega.utils.trans.listToString(peers, l.cancel_with_to_join) : l.cancel_to_join; - return msgDialog('confirmation', undefined, l.call_in_progress, body, e => e && window.open(url, '_blank', 'noopener,noreferrer')); - } - window.open(url, '_blank', 'noopener,noreferrer'); - } - }.bind(this, megaLinkInfo.getLink(), megaLinkInfo) - }, previewContainer, REaCt().createElement("div", { - className: "clear" - }))); - } - return REaCt().createElement("div", { - className: "message richpreview previews-container" - }, output); + }, + searchValue: self.state.searchValue, + browsingCategory: self.state.browsingCategory, + previewEmoji: self.state.previewEmoji + }), JSX_("div", { + ref: this.domRef + }, popupContents)); } } +DropdownEmojiSelector.defaultProps = { + 'requiresUpdateOnResize': true, + 'hideable': true +}; -// EXTERNAL MODULE: ./js/chat/ui/typingArea.jsx + 1 modules -const typingArea = REQ_(795); -// EXTERNAL MODULE: ./js/ui/perfectScrollbar.jsx -const perfectScrollbar = REQ_(486); -;// ./js/chat/ui/messages/types/text.jsx - - - - - + }, + 8120 +(_, EXP_, REQ_) { +"use strict"; +// EXPORTS +REQ_.d(EXP_, { + A: () => modalDialogs +}); +// UNUSED EXPORTS: ExtraFooterElement +// EXTERNAL MODULE: external "React" +const external_React_ = REQ_(1594); +const REaCt = REQ_.n(external_React_); +// EXTERNAL MODULE: ./js/ui/utils.jsx +const utils = REQ_(6411); +// EXTERNAL MODULE: ./js/chat/mixins.js +const mixins = REQ_(8264); +;// ./js/ui/forms.jsx -class Text extends AbstractGenericMessage { +class Checkbox extends mixins.w9 { constructor(props) { super(props); + this.domRef = REaCt().createRef(); this.state = { - editText: '' + checked: this.props.checked ? this.props.checked : false }; + this.onLabelClick = this.onLabelClick.bind(this); + this.onChange = this.onChange.bind(this); } - isRichPreview(message) { - return message.metaType === Message.MESSAGE_META_TYPE.RICH_PREVIEW; + onLabelClick(e) { + const state = !this.state.checked; + this.setState({ + 'checked': state + }); + if (this.props.onLabelClick) { + this.props.onLabelClick(e, state); + } + this.onChange(e); } - isGeoLocation(message) { - return message.metaType === Message.MESSAGE_META_TYPE.GEOLOCATION; + onChange(e) { + if (this.props.onChange) { + this.props.onChange(e, this.state.checked); + } } - getClassNames() { + render() { const { - message, - isBeingEdited, - grouped + name, + id, + children } = this.props; - const REQUIRES_CONFIRMATION = this.isRichPreview(message) && message.meta.requiresConfirmation && !isBeingEdited() && (message.source === Message.SOURCE.SENT || message.confirmed === true); - return ` - ${REQUIRES_CONFIRMATION ? 'preview-requires-confirmation-container' : ''} - ${grouped ? 'grouped' : ''} - `; + const className = this.state.checked ? 'checkboxOn' : 'checkboxOff'; + return JSX_("div", { + ref: this.domRef, + className: "formsCheckbox" + }, JSX_("div", { + className: ` + checkdiv + ${className} + `, + onClick: this.onLabelClick + }, JSX_("input", { + type: "checkbox", + name, + id, + className, + checked: this.state.checked, + onChange: this.onChange + })), JSX_("label", { + htmlFor: id, + className: "radio-txt" + }, children)); + } +} + const ui_forms = { + Checkbox +}; +// EXTERNAL MODULE: ./js/chat/ui/contacts.jsx +const contacts = REQ_(8022); +;// ./js/ui/modalDialogs.jsx + + + + + +class ExtraFooterElement extends REaCt().Component { + render() { + return this.props.children; } - renderMessageIndicators() { +} +class SafeShowDialogController extends mixins.w9 { + constructor(props) { + super(props); + this.dialogName = 'unnamed-dialog'; + this.dialogBecameVisible = null; const { - message, - spinnerElement, - isBeingEdited, - onRetry, - onCancelRetry - } = this.props; - if (!message || spinnerElement || isBeingEdited()) { - return null; - } - const state = message.getState == null ? void 0 : message.getState(); - if (![Message.STATE.NOT_SENT, Message.STATE.NOT_SENT_EXPIRED].includes(state)) { + render + } = this; + this.render = () => { + if (this.dialogBecameVisible) { + console.assert($.dialog === this.dialogName, `${this.dialogName} state overridden.`); + return render.call(this); + } return null; - } - const props = { - 'data-simpletipposition': 'top', - 'data-simpletipoffset': 8 }; - return message.requiresManualRetry ? REaCt().createElement("div", { - className: "not-sent-indicator clickable" - }, REaCt().createElement("span", (0,esm_extends.A)({ - className: "simpletip" - }, props, { - "data-simpletip": l[8883], - onClick: ev => onRetry(ev, message) - }), REaCt().createElement("i", { - className: "small-icon refresh-circle" - })), REaCt().createElement("span", (0,esm_extends.A)({ - className: "simpletip" - }, props, { - "data-simpletip": l[8884], - onClick: ev => onCancelRetry(ev, message) - }), REaCt().createElement("i", { - className: "sprite-fm-mono icon-dialog-close" - }))) : REaCt().createElement("div", (0,esm_extends.A)({ - className: "not-sent-indicator simpletip" - }, props, { - "data-simpletip": l[8882] - }), REaCt().createElement("i", { - className: "small-icon yellow-triangle" - })); } - getMessageActionButtons() { - const { - chatRoom, - message, - isBeingEdited - } = this.props; - if (isBeingEdited()) { - return []; - } - let extraPreButtons = []; - let messageActionButtons = null; - const IS_GEOLOCATION = this.isGeoLocation(message); - if (!message.deleted && this.isRichPreview(message)) { - if (!message.meta.requiresConfirmation) { - if (message.isEditable()) { - if (message.meta.isLoading) { - extraPreButtons = [...extraPreButtons, REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-eye-hidden", - key: "stop-link-preview", - label: l[18684], - className: "", - onClick: e => { - e.stopPropagation(); - e.preventDefault(); - chatRoom.megaChat.plugins.richpreviewsFilter.cancelLoading(chatRoom, message); - } - })]; - } else { - extraPreButtons = [...extraPreButtons, REaCt().createElement(dropdowns.DropdownItem, { - key: "remove-link-preview", - icon: "sprite-fm-mono icon-eye-hidden", - label: l[18684], - className: "", - onClick: e => { - e.stopPropagation(); - e.preventDefault(); - chatRoom.megaChat.plugins.richpreviewsFilter.revertToText(chatRoom, message); - } - })]; - } - } - } else if (!isBeingEdited() && !(message.source === Message.SOURCE.SENT || message.confirmed === true)) { - extraPreButtons = [...extraPreButtons, REaCt().createElement(dropdowns.DropdownItem, { - key: "insert-link-preview", - icon: "icons-sprite bold-eye", - label: l[18683], - className: "", - onClick: e => { - e.stopPropagation(); - e.preventDefault(); - chatRoom.megaChat.plugins.richpreviewsFilter.insertPreview(message); - } - })]; - } - } - if (!message.deleted && message.isEditable() && !isBeingEdited() && !chatRoom.isReadOnly() && !message.requiresManualRetry) { - const editButton = !IS_GEOLOCATION && REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-rename", - label: l[1342], - onClick: () => this.props.onEditToggle(true) - }); - messageActionButtons = REaCt().createElement(buttons.$, { - key: "delete-msg", - className: "tiny-button", - icon: "sprite-fm-mono icon-options" - }, REaCt().createElement(dropdowns.Dropdown, { - className: "white-context-menu attachments-dropdown", - noArrow: true, - positionMy: "left bottom", - positionAt: "right bottom", - horizOffset: 4 - }, extraPreButtons, editButton, editButton ? REaCt().createElement("hr", null) : null, REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-dialog-close", - label: l[1730], - onClick: e => this.props.onDelete(e, message) - }))); - } - let parentButtons; - if (super.getMessageActionButtons) { - parentButtons = super.getMessageActionButtons(); - } - const returnedButtons = []; - if (messageActionButtons) { - returnedButtons.push(messageActionButtons); - } - if (message.messageHtml && message.messageHtml.includes('
') && message.messageHtml.includes('
')) { - returnedButtons.push(REaCt().createElement(buttons.$, { - key: "copy-msg", - className: "tiny-button simpletip copy-txt-block", - icon: "sprite-fm-mono icon-copy", - attrs: { - 'data-simpletip': l.copy_txt_block_tip, - 'data-simpletipoffset': '3', - 'data-simpletipposition': 'top' - }, - onClick: () => { - copyToClipboard(message.textContents.replace(/```/g, ''), l.copy_txt_block_toast); - } - })); - } - if (parentButtons) { - returnedButtons.push(parentButtons); + shouldComponentUpdate(nextProps, nextState) { + if (!this.dialogBecameVisible) { + return false; } - return returnedButtons; + return super.shouldComponentUpdate(nextProps, nextState); } - getContents() { - const { - message, - chatRoom, - onUpdate, - isBeingEdited, - spinnerElement - } = this.props; - let textMessage = message.messageHtml; - const IS_GEOLOCATION = this.isGeoLocation(message); - const { - lng, - la: latitude - } = IS_GEOLOCATION && message.meta.extra[0]; - if (message.textContents === '' && !message.dialogType) { - message.deleted = true; - } - let subMessageComponent = []; - if (!message.deleted) { - if (this.isRichPreview(message)) { - if (!message.meta.requiresConfirmation) { - subMessageComponent = [...subMessageComponent, REaCt().createElement(MetaRichpreview, { - key: "richprev", - message, - chatRoom - })]; - } else if (!isBeingEdited()) { - if (message.source === Message.SOURCE.SENT || message.confirmed === true) { - subMessageComponent = [...subMessageComponent, REaCt().createElement(MetaRichprevConfirmation, { - key: "confirm", - message, - chatRoom - })]; - } - } + componentDidMount() { + super.componentDidMount(); + M.safeShowDialog(this.dialogName, () => { + if (!this.isMounted()) { + throw new Error(`${this.dialogName} component is no longer mounted.`); } - if (message.megaLinks) { - subMessageComponent = [...subMessageComponent, REaCt().createElement(MetaRichpreviewMegaLinks, { - key: "richprevml", - message, - chatRoom - })]; + this.dialogBecameVisible = 1; + this.forceUpdate(); + }); + } + componentWillUnmount() { + super.componentWillUnmount(); + if (this.dialogBecameVisible) { + this.dialogBecameVisible = false; + console.assert($.dialog === this.dialogName); + if ($.dialog === this.dialogName) { + closeDialog(); } } - let messageDisplayBlock; - if (isBeingEdited() === true) { - let msgContents = message.textContents; - msgContents = megaChat.plugins.emoticonsFilter.fromUtfToShort(msgContents); - messageDisplayBlock = REaCt().createElement(typingArea.T, { - iconClass: "small-icon writing-pen textarea-icon", - initialText: msgContents, - text: this.state.editText || msgContents, - chatRoom, - showButtons: true, - editing: true, - className: "edit-typing-area", - onUpdate: () => onUpdate ? onUpdate : null, - onConfirm: messageContents => { - this.props.onEditToggle(false); - if (this.props.onEditDone) { - Soon(() => { - const tmpMessageObj = { - textContents: messageContents - }; - megaChat.plugins.emoticonsFilter.processOutgoingMessage({}, tmpMessageObj); - this.props.onEditDone(tmpMessageObj.textContents); - if (this.isMounted()) { - this.forceUpdate(); - } - }); - } - return true; - }, - onResized: this.props.onResized ? this.props.onResized : false, - onValueChanged: val => { - this.setState({ - editText: val - }); + } + componentDidUpdate() { + assert(this.dialogBecameVisible); + super.componentDidUpdate(); + if (++this.dialogBecameVisible === 2) { + requestAnimationFrame(() => { + const dialog = document.querySelectorAll(`.${this.dialogName}`); + console.assert(dialog.length === 1, `Unexpected ${this.dialogName} state.`); + console.assert($.dialog === this.dialogName, `${this.dialogName} state overridden.`); + if (dialog.length === 1 && $.dialog === this.dialogName) { + dialog[0].classList.remove('hidden', 'arrange-to-back'); } }); - } else { - if (message.updated > 0 && !message.metaType) { - textMessage = `${textMessage} ${l[8887]} `; - } - if (this.props.initTextScrolling) { - messageDisplayBlock = REaCt().createElement(perfectScrollbar.O, { - className: "message text-block scroll" - }, REaCt().createElement("div", { - className: "message text-scroll" - }, REaCt().createElement(utils.P9, null, textMessage))); - } else { - messageDisplayBlock = REaCt().createElement("div", { - className: "message text-block" - }, REaCt().createElement(utils.P9, null, textMessage)); - } } - return REaCt().createElement(REaCt().Fragment, null, this.renderMessageIndicators(), IS_GEOLOCATION ? null : messageDisplayBlock, subMessageComponent, spinnerElement, IS_GEOLOCATION && REaCt().createElement(geoLocation, { - latitude, - lng - })); } } -// EXTERNAL MODULE: ./js/chat/ui/gifPanel/gifPanel.jsx + 3 modules -const gifPanel = REQ_(691); -;// ./js/chat/ui/messages/types/giphy.jsx - - - - - -class Giphy extends AbstractGenericMessage { - constructor(...args) { - super(...args); - this.gifRef = REaCt().createRef(); - this.viewStateListener = `viewstateChange.giphy--${this.getUniqueId()}`; - this.state = { - src: undefined - }; +class ModalDialog extends mixins.w9 { + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.onBlur = this.onBlur.bind(this); + this.onCloseClicked = this.onCloseClicked.bind(this); + this.onPopupDidMount = this.onPopupDidMount.bind(this); } componentDidMount() { super.componentDidMount(); - megaChat.rebind(this.viewStateListener, ({ - data - }) => { - const gifRef = this.gifRef && this.gifRef.current; - if (gifRef) { - const { - state - } = data; - if (state === 'active' && gifRef.paused || state !== 'active' && !gifRef.paused) { - this.toggle(); + if (!this.props.hideOverlay) { + $(document.body).addClass('overlayed'); + $('.fm-dialog-overlay').removeClass('hidden'); + } + $('textarea:focus').trigger("blur"); + if (!this.props.noCloseOnClickOutside) { + const convApp = document.querySelector('.conversationsApp'); + if (convApp) { + convApp.removeEventListener('click', this.onBlur); + convApp.addEventListener('click', this.onBlur); + } + $('.fm-modal-dialog').rebind(`click.modalDialogOv${ this.getUniqueId()}`, ({ + target + }) => { + if ($(target).is('.fm-modal-dialog')) { + this.onBlur(); + } + }); + $('.fm-dialog-overlay').rebind(`click.modalDialog${ this.getUniqueId()}`, () => { + if (this.props.closeDlgOnClickOverlay) { + this.onBlur(); } + return false; + }); + } + $(document).rebind(`keyup.modalDialog${ this.getUniqueId()}`, ({ + keyCode + }) => { + if (!this.props.stopKeyPropagation && keyCode === 27) { + this.onBlur(); } }); } + onBlur(e) { + let _this$domRef; + const $element = $((_this$domRef = this.domRef) == null ? void 0 : _this$domRef.current); + if (!e || !$(e.target).closest('.mega-dialog').is($element)) { + const convApp = document.querySelector('.conversationsApp'); + if (convApp) { + convApp.removeEventListener('click', this.onBlur); + } + this.onCloseClicked(); + } + } componentWillUnmount() { + let _this$props$popupWill, _this$props; super.componentWillUnmount(); - megaChat.off(this.viewStateListener); + if (!this.props.noCloseOnClickOutside) { + const convApp = document.querySelector('.conversationsApp'); + if (convApp) { + convApp.removeEventListener('click', this.onBlur); + } + $('.fm-dialog-overlay').off(`click.modalDialog${ this.getUniqueId()}`); + } + if (!this.props.hideOverlay) { + $(document.body).removeClass('overlayed'); + $('.fm-dialog-overlay').addClass('hidden'); + } + $(this.domNode).off(`dialog-closed.modalDialog${ this.getUniqueId()}`); + $(document).off(`keyup.modalDialog${ this.getUniqueId()}`); + (_this$props$popupWill = (_this$props = this.props).popupWillUnmount) == null || _this$props$popupWill.call(_this$props); } - onVisibilityChange(isIntersecting) { - this.setState({ - src: isIntersecting ? gifPanel.nC.convert(this.props.message.meta.src) : undefined - }, () => { - let _this$gifRef; - (_this$gifRef = this.gifRef) == null || (_this$gifRef = _this$gifRef.current) == null || _this$gifRef[isIntersecting ? 'load' : 'pause'](); - this.safeForceUpdate(); - }); + onCloseClicked() { + const self = this; + if (self.props.onClose) { + self.props.onClose(self); + } } - toggle() { - const video = this.gifRef.current; - Promise.resolve(video[video.paused ? 'play' : 'pause']()).catch(nop); + onPopupDidMount(elem) { + this.domNode = elem; + $(elem).rebind(`dialog-closed.modalDialog${ this.getUniqueId()}`, () => this.onCloseClicked()); + if (this.props.popupDidMount) { + this.props.popupDidMount(elem); + } } - getMessageActionButtons() { - const { - onDelete, - message - } = this.props; - const $$BUTTONS = [message.isEditable() && REaCt().createElement(buttons.$, { - key: "delete-GIPHY-button", - className: "tiny-button", - icon: "sprite-fm-mono icon-options" - }, REaCt().createElement(dropdowns.Dropdown, { - className: "white-context-menu attachments-dropdown", - noArrow: true, - positionMy: "left bottom", - positionAt: "right bottom", - horizOffset: 4 - }, REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono icon-dialog-close", - label: l[1730], - onClick: e => onDelete(e, message) - }))), super.getMessageActionButtons && super.getMessageActionButtons()]; - return $$BUTTONS.filter(button => button); - } - getContents() { - const { - message, - hideActionButtons - } = this.props; - const { - s, - w, - h, - src - } = message.meta; - const autoPlay = parseInt(s, 10) < 4e6; - return REaCt().createElement("video", { - className: "giphy-block", - ref: this.gifRef, - title: message.textContents, - autoPlay, - loop: true, - muted: true, - controls: false, - width: w, - height: h, - style: { - cursor: autoPlay ? 'default' : 'pointer', - height: `${h}px` - }, - onClick: () => !autoPlay && this.toggle(), - src: hideActionButtons ? gifPanel.nC.convert(src) : this.state.src + render() { + const self = this; + let classes = 'mega-dialog'; + let selectedNumEle = null; + let footer = null; + const extraFooterElements = []; + const otherElements = []; + let x = 0; + REaCt().Children.forEach(self.props.children, (child) => { + if (!child) { + return; + } + if (child.type.name === 'ExtraFooterElement') { + extraFooterElements.push(REaCt().cloneElement(child, { + key: x++ + })); + } else { + otherElements.push(REaCt().cloneElement(child, { + key: x++ + })); + } }); + if (self.props.className) { + classes += ` ${self.props.className}`; + } + if (self.props.dialogType) { + classes += ` dialog-template-${self.props.dialogType}`; + } + if (self.props.dialogName) { + classes += ` ${self.props.dialogName}`; + } + if (self.props.showSelectedNum && self.props.selectedNum) { + selectedNumEle = JSX_("div", { + className: "selected-num" + }, JSX_("span", null, self.props.selectedNum)); + } + let buttons; + if (self.props.buttons) { + buttons = []; + self.props.buttons.forEach((v, i) => { + if (v) { + buttons.push(JSX_("button", { + className: (v.defaultClassname ? v.defaultClassname : "mega-button") + (v.className ? ` ${ v.className}` : "") + (self.props.dialogType === "action" ? "large" : ""), + onClick: e => { + if ($(e.target).is(".disabled")) { + return false; + } + if (v.onClick) { + v.onClick(e, self); + } + }, + key: v.key + i + }, v.iconBefore ? JSX_("div", null, JSX_("i", { + className: v.iconBefore + })) : null, JSX_("span", null, v.label), v.iconAfter ? JSX_("div", null, JSX_("i", { + className: v.iconAfter + })) : null)); + } + }); + if (buttons && buttons.length > 0 || extraFooterElements && extraFooterElements.length > 0) { + footer = JSX_("footer", null, buttons && buttons.length > 0 ? JSX_("div", { + className: "footer-container" + }, buttons) : null, extraFooterElements && extraFooterElements.length > 0 ? JSX_("aside", null, extraFooterElements) : null); + } + } + return JSX_(utils.Ay.RenderTo, { + element: document.body, + className: "fm-modal-dialog", + popupDidMount: this.onPopupDidMount + }, JSX_("div", { + ref: this.domRef, + id: self.props.id, + className: classes, + "aria-labelledby": self.props.dialogName ? `${self.props.dialogName }-title` : null, + role: "dialog", + "aria-modal": "true", + onClick: self.props.onClick + }, JSX_("button", { + className: "close", + onClick: self.onCloseClicked + }, JSX_("i", { + className: "sprite-fm-mono icon-dialog-close" + })), self.props.title ? self.props.dialogType === "message" ? JSX_("header", null, self.props.icon ? JSX_("i", { + className: `graphic ${self.props.icon}` + }) : self.props.iconElement, JSX_("div", null, JSX_("h3", { + id: self.props.dialogName ? `${self.props.dialogName }-title` : null + }, self.props.title, selectedNumEle), self.props.subtitle ? JSX_("p", null, self.props.subtitle) : null, otherElements)) : JSX_("header", null, self.props.icon ? JSX_("i", { + className: `graphic ${self.props.icon}` + }) : self.props.iconElement, JSX_("h2", { + id: self.props.dialogName ? `${self.props.dialogName }-title` : null + }, self.props.title, selectedNumEle), self.props.subtitle ? JSX_("p", null, self.props.subtitle) : null) : null, self.props.dialogType !== "message" ? otherElements : null, buttons || extraFooterElements ? footer : null)); } } -;// ./js/chat/ui/messages/generic.jsx - - - - - - - - - - -class GenericConversationMessage extends mixin.M { +ModalDialog.defaultProps = { + 'hideable': true, + 'noCloseOnClickOutside': false, + 'closeDlgOnClickOverlay': true, + 'showSelectedNum': false, + 'selectedNum': 0 +}; +class SelectContactDialog extends mixins.w9 { constructor(props) { super(props); - this.containerRef = REaCt().createRef(); + this.dialogName = 'send-contact-dialog'; this.state = { - editing: this.props.editing + selected: [] }; - this.pid = `__geom_${ String(Math.random()).substr(2)}`; - } - isBeingEdited() { - return this.state.editing === true || this.props.editing === true; + this.state.selected = this.props.selected || []; + this.onSelected = this.onSelected.bind(this); } - componentDidUpdate(oldProps, oldState) { - const isBeingEdited = this.isBeingEdited(); - const isMounted = this.isMounted(); - if (isBeingEdited && isMounted) { - let _this$containerRef; - const $generic = $((_this$containerRef = this.containerRef) == null ? void 0 : _this$containerRef.current); - const $textarea = $('textarea', $generic); - if ($textarea.length > 0 && !$textarea.is(":focus")) { - $textarea.trigger("focus"); - moveCursortoToEnd($textarea[0]); - } - if (!oldState.editing && this.props.onEditStarted) { - this.props.onEditStarted($generic); - moveCursortoToEnd($textarea); - } - } - if (isMounted && !isBeingEdited && oldState.editing === true && this.props.onUpdate) { - this.props.onUpdate(); - } + onSelected(nodes) { + let _this$props$onSelecte, _this$props2; + this.setState({ + selected: nodes + }); + (_this$props$onSelecte = (_this$props2 = this.props).onSelected) == null || _this$props$onSelecte.call(_this$props2, nodes); } componentDidMount() { - let _this$containerRef2; super.componentDidMount(); - const $node = $((_this$containerRef2 = this.containerRef) == null ? void 0 : _this$containerRef2.current); - if (this.isBeingEdited() && this.isMounted()) { - const $textarea = $('textarea', $node); - if ($textarea.length > 0 && !$textarea.is(':focus')) { - $textarea.trigger('focus'); - moveCursortoToEnd($textarea[0]); - } - } + M.safeShowDialog(this.dialogName, () => $(`.${this.dialogName}`)); } - haveMoreContactListeners() { - if (!this.props.message || !this.props.message.meta) { - return false; - } - if (this.props.message.meta && this.props.message.meta.participants) { - return this.props.message.meta.participants; + componentWillUnmount() { + super.componentWillUnmount(); + if ($.dialog === this.dialogName) { + closeDialog(); } - return false; } - doDelete(e, msg) { - e.preventDefault(e); - e.stopPropagation(e); - if (msg.getState() === Message.STATE.NOT_SENT_EXPIRED) { - this.doCancelRetry(e, msg); - } else { - this.props.onDeleteClicked(this.props.message); - } - } - doCancelRetry(e, msg) { - e.preventDefault(e); - e.stopPropagation(e); - const {chatRoom} = this.props.message; - const {messageId} = msg; - chatRoom.messagesBuff.messages.removeByKey(messageId); - chatRoom.megaChat.plugins.chatdIntegration.discardMessage(chatRoom, messageId); - } - doRetry(e, msg) { - e.preventDefault(e); - e.stopPropagation(e); - const {chatRoom} = this.props.message; - this.doCancelRetry(e, msg); - chatRoom._sendMessageToTransport(msg).then(internalId => { - msg.internalId = internalId; - this.safeForceUpdate(); - }); + render() { + return JSX_(ModalDialog, { + title: l.share_contact_title, + className: ` + send-contact + contrast + small-footer + dialog-template-tool + ${this.props.className} + ${this.dialogName} + `, + selected: this.state.selected, + buttons: [{ + key: "cancel", + label: this.props.cancelLabel, + onClick: ev => { + this.props.onClose(); + ev.preventDefault(); + ev.stopPropagation(); + } + }, { + key: "select", + label: this.props.selectLabel, + className: this.state.selected.length === 0 ? 'positive disabled' : 'positive', + onClick: ev => { + if (this.state.selected.length > 0) { + let _this$props$onSelecte2, _this$props3; + (_this$props$onSelecte2 = (_this$props3 = this.props).onSelected) == null || _this$props$onSelecte2.call(_this$props3, this.state.selected); + this.props.onSelectClicked(this.state.selected); + } + ev.preventDefault(); + ev.stopPropagation(); + } + }], + onClose: this.props.onClose + }, JSX_("section", { + className: "content" + }, JSX_("div", { + className: "content-block" + }, JSX_(contacts.hU, { + megaChat: this.props.megaChat, + exclude: this.props.exclude, + selectableContacts: "true", + onSelectDone: this.props.onSelectClicked, + onSelected: this.onSelected, + onClose: this.props.onClose, + selected: this.state.selected, + contacts: M.u, + headerClasses: "left-aligned", + multiple: true + })))); } - _favourite(h) { - if (M.isInvalidUserStatus()) { - return; - } - const newFavState = Number(!M.isFavourite(h)); - M.favourite([h], newFavState); +} +SelectContactDialog.clickTime = 0; +SelectContactDialog.defaultProps = { + selectLabel: l.share_contact_action, + cancelLabel: l.msg_dlg_cancel, + hideable: true +}; +class ConfirmDialog extends mixins.w9 { + static saveState(o) { + const state = mega.config.get('xcod') >>> 0; + mega.config.set('xcod', state | 1 << o.props.pref); } - _addFavouriteButtons(h, arr) { - const self = this; - if (M.getNodeRights(h) > 1) { - const isFav = M.isFavourite(h); - arr.push(REaCt().createElement(dropdowns.DropdownItem, { - icon: ` - sprite-fm-mono - context - ${isFav ? 'icon-favourite-removed' : 'icon-favourite'} - `, - label: isFav ? l[5872] : l[5871], - isFav, - key: "fav", - disabled: mega.paywall, - onClick: e => { - self._favourite(h); - e.stopPropagation(); - e.preventDefault(); - return false; - } - })); - return isFav; - } - return false; + static clearState(o) { + const state = mega.config.get('xcod') >>> 0; + mega.config.set('xcod', state & ~(1 << o.props.pref)); } - _isNodeHavingALink(h) { - return M.getNodeShare(h) !== false; + static autoConfirm(o) { + console.assert(o.props.pref > 0); + const state = mega.config.get('xcod') >>> 0; + return !!(state & 1 << o.props.pref); } - _addLinkButtons(h, arr) { - const self = this; - const haveLink = self._isNodeHavingALink(h) === true; - const getManageLinkText = haveLink ? l[6909] : l[5622]; - arr.push(REaCt().createElement(dropdowns.DropdownItem, { - icon: `sprite-fm-mono icon-link${haveLink ? '-gear' : ''}`, - key: "getLinkButton", - label: getManageLinkText, - disabled: mega.paywall, - onClick: self._getLink.bind(self, h) - })); - if (haveLink) { - arr.push(REaCt().createElement(dropdowns.DropdownItem, { - icon: "sprite-fm-mono context icon-link-remove", - key: "removeLinkButton", - label: l[6821], - disabled: mega.paywall, - onClick: self._removeLink.bind(self, h) - })); - return true; - } - return false; + constructor(props) { + super(props); + this.dialogName = 'confirm-dialog'; + this._wasAutoConfirmed = undefined; + this._keyUpEventName = `keyup.confirmDialog${ this.getUniqueId()}`; + this.dialogName = this.props.name || this.dialogName; + lazy(this, '_autoConfirm', () => this.props.onConfirmClicked && this.props.dontShowAgainCheckbox && ConfirmDialog.autoConfirm(this)); } - _startDownload(v) { - M.addDownload([v]); + unbindEvents() { + $(document).off(this._keyUpEventName); } - _addToCloudDrive(v, openSendToChat) { - $.selected = [v.h]; - $.chatAttachmentShare = true; - if ($.dialog === 'onboardingDialog') { - closeDialog(); - } - if (openSendToChat) { - openSendToChatDialog(); - return; - } - openSaveToDialog(v, (node, target) => { - target = target || M.RootID; - M.injectNodes(node, target, res => { - if (!Array.isArray(res) || !res.length) { - if (d) { - console.warn('Unable to inject nodes... no longer existing?', res); + componentDidMount() { + super.componentDidMount(); + M.safeShowDialog(this.dialogName, () => { + queueMicrotask(() => { + if (!this.isMounted()) { + return; + } + if (this._autoConfirm) { + if (!this._wasAutoConfirmed) { + this._wasAutoConfirmed = 1; + queueMicrotask(() => { + this.onConfirmClicked(); + }); } - } else { - mega.ui.toast.show(mega.icu.format(l.toast_import_file, res.length).replace('%s', M.getNameByHandle(target)), 4, l[16797], { - actionButtonCallback() { - M.openFolder(target).then(() => { - if (window.selectionManager) { - let reset = false; - for (let i = res.length; i--;) { - const n = M.getNodeByHandle(res[i]); - if (n.p === target) { - if (reset) { - selectionManager.add_to_selection(n.h); - } else { - selectionManager.resetTo(n.h); - reset = true; - } - } - } - } - }).catch(dump); - } - }); + return; } + $(document).rebind(this._keyUpEventName, e => { + if (e.which === 13 || e.keyCode === 13) { + if (!this.isMounted()) { + this.unbindEvents(); + return; + } + this.onConfirmClicked(); + return false; + } + }); }); - }, false); + }); } - _getLink(h, e) { - if (u_type === 0) { - ephemeralDialog(l[1005]); - } else { - $.selected = [h]; - mega.Share.initCopyrightsDialog([h]); - } - if (e) { - e.preventDefault(); - e.stopPropagation(); + componentWillUnmount() { + super.componentWillUnmount(); + this.unbindEvents(); + if ($.dialog === this.dialogName) { + closeDialog(); } + delete this._wasAutoConfirmed; } - _removeLink(h, e) { - if (u_type === 0) { - ephemeralDialog(l[1005]); - } else { - const exportLink = new mega.Share.ExportLink({ - 'updateUI': true, - 'nodesToProcess': [h] - }); - exportLink.removeExportLink(); - } - if (e) { - e.preventDefault(); - e.stopPropagation(); + onConfirmClicked() { + this.unbindEvents(); + if (this.props.onConfirmClicked) { + this.props.onConfirmClicked(); } } - _startPreview(v, e) { - if ($(e && e.target).is('.tiny-button')) { - return; + render() { + const self = this; + if (this._autoConfirm) { + return null; } - assert(M.chat, 'Not in chat.'); - if (e) { - e.preventDefault(); - e.stopPropagation(); + const classes = `delete-message${ self.props.name ? ` ${self.props.name}` : "" }${self.props.className ? ` ${self.props.className}` : ""}`; + let dontShowCheckbox = null; + if (self.props.dontShowAgainCheckbox) { + dontShowCheckbox = JSX_("div", { + className: "footer-checkbox" + }, JSX_(ui_forms.Checkbox, { + name: "delete-confirm", + id: "delete-confirm", + onLabelClick: (e, state) => { + if (state === true) { + ConfirmDialog.saveState(self); + } else { + ConfirmDialog.clearState(self); + } + } + }, l[7039])); } - M.viewMediaFile(v); - } - render() { - const { - message, - chatRoom - } = this.props; - const {megaChat} = this.props.message.chatRoom; - const {textContents} = message; - let additionalClasses = ""; - let spinnerElement = null; - let messageIsNowBeingSent = false; - if (this.props.className) { - additionalClasses += this.props.className; - } - if (message instanceof Message) { - if (!message.wasRendered || !message.messageHtml) { - message.messageHtml = htmlentities(textContents).replace(/\n/gi, "
").replace(/\t/g, ' '); - message.processedBy = {}; - const evtObj = { - message, - room: chatRoom - }; - megaChat.trigger('onPreBeforeRenderMessage', evtObj); - const event = new MegaDataEvent('onBeforeRenderMessage'); - megaChat.trigger(event, evtObj); - megaChat.trigger('onPostBeforeRenderMessage', evtObj); - if (event.isPropagationStopped()) { - this.logger.warn(`Event propagation stopped receiving (rendering) of message: ${message}`); - return false; + return JSX_(ModalDialog, { + title: this.props.title, + subtitle: this.props.subtitle, + className: classes, + dialogId: this.props.name, + dialogType: this.props.dialogType, + icon: this.props.icon, + onClose: () => { + self.props.onClose(self); + }, + buttons: [{ + "label": self.props.cancelLabel, + "key": "cancel", + "onClick" (e) { + ConfirmDialog.clearState(self); + self.props.onClose(self); + e.preventDefault(); + e.stopPropagation(); } - message.wasRendered = 1; - } - const state = message.getState(); - const stateText = message.getStateText(state); - if (state === Message.STATE.NOT_SENT) { - messageIsNowBeingSent = unixtime() - message.delay < 5; - if (messageIsNowBeingSent) { - additionalClasses += ' sending'; - spinnerElement = REaCt().createElement("div", { - className: "small-blue-spinner" - }); - if (!message.sending) { - message.sending = true; - delay(this.pid + message.messageId, () => { - if (chatRoom.messagesBuff.messages[message.messageId] && message.sending === true) { - chatRoom.messagesBuff.trackDataChange(); - if (this.isMounted()) { - this.forceUpdate(); - } - } - }, (5 - (unixtime() - message.delay)) * 1000); - } - } else { - additionalClasses += ' not-sent'; - if (message.sending === true) { - message.sending = false; - message.trigger('onChange', [message, 'sending', true, false]); - } - if (message.requiresManualRetry) { - additionalClasses += ' retrying requires-manual-retry'; - } else { - additionalClasses += ' retrying'; - } + }, { + "label": self.props.confirmLabel, + "key": "select", + "className": "positive", + "onClick" (e) { + self.onConfirmClicked(); + e.preventDefault(); + e.stopPropagation(); } - } else { - additionalClasses += ` ${ stateText}`; - } - } - const MESSAGE = { - TYPE: { - ATTACHMENT: textContents[1] === Message.MANAGEMENT_MESSAGE_TYPES.ATTACHMENT, - CONTACT: textContents[1] === Message.MANAGEMENT_MESSAGE_TYPES.CONTACT, - REVOKE_ATTACHMENT: textContents[1] === Message.MANAGEMENT_MESSAGE_TYPES.REVOKE_ATTACHMENT, - VOICE_CLIP: textContents[1] === Message.MANAGEMENT_MESSAGE_TYPES.VOICE_CLIP, - GIPHY: message.metaType && message.metaType === Message.MESSAGE_META_TYPE.GIPHY, - TEXT: textContents[0] !== Message.MANAGEMENT_MESSAGE_TYPES.MANAGEMENT, - INLINE: !(message instanceof Message) && message.type && !!message.type.length, - REVOKED: message.revoked - }, - props: { - ...this.props, - additionalClasses - }, - isBeingEdited: () => this.isBeingEdited(), - onDelete: (e, message) => this.doDelete(e, message) - }; - const $$CONTAINER = children => REaCt().createElement("div", { - ref: this.containerRef - }, children); - switch (true) { - case MESSAGE.TYPE.REVOKED || MESSAGE.TYPE.REVOKE_ATTACHMENT: - return null; - case MESSAGE.TYPE.ATTACHMENT: - return $$CONTAINER(REaCt().createElement(Attachment, (0,esm_extends.A)({}, MESSAGE.props, { - onPreviewStart: (v, e) => this._startPreview(v, e), - onDownloadStart: v => this._startDownload(v), - onAddLinkButtons: (h, arr) => this._addLinkButtons(h, arr), - onAddToCloudDrive: (v, openSendToChat) => this._addToCloudDrive(v, openSendToChat), - onAddFavouriteButtons: (h, arr) => this._addFavouriteButtons(h, arr) - }))); - case MESSAGE.TYPE.CONTACT: - return $$CONTAINER(REaCt().createElement(Contact, (0,esm_extends.A)({}, MESSAGE.props, { - onDelete: MESSAGE.onDelete - }))); - case MESSAGE.TYPE.VOICE_CLIP: - return $$CONTAINER(REaCt().createElement(VoiceClip, (0,esm_extends.A)({}, MESSAGE.props, { - isBeingEdited: MESSAGE.isBeingEdited, - onDelete: MESSAGE.onDelete - }))); - case MESSAGE.TYPE.INLINE: - return $$CONTAINER(REaCt().createElement(Local, MESSAGE.props)); - case MESSAGE.TYPE.GIPHY: - return $$CONTAINER(REaCt().createElement(Giphy, (0,esm_extends.A)({}, MESSAGE.props, { - onDelete: MESSAGE.onDelete - }))); - case MESSAGE.TYPE.TEXT: - return $$CONTAINER(REaCt().createElement(Text, (0,esm_extends.A)({}, MESSAGE.props, { - onEditToggle: editing => this.setState({ - editing - }), - onDelete: MESSAGE.onDelete, - onRetry: (e, message) => this.doRetry(e, message), - onCancelRetry: (e, message) => this.doCancelRetry(e, message), - isBeingEdited: MESSAGE.isBeingEdited, - spinnerElement - }))); - default: - return null; - } + }] + }, self.props.children, dontShowCheckbox ? JSX_(ExtraFooterElement, null, dontShowCheckbox) : null); } } +lazy(ConfirmDialog, 'defaultProps', () => { + return freeze({ + 'confirmLabel': l[6826], + 'cancelLabel': l.msg_dlg_cancel, + 'dontShowAgainCheckbox': true, + 'hideable': true, + 'dialogType': 'message' + }); +}); + const modalDialogs = { + ModalDialog, + SelectContactDialog, + SafeShowDialogController, + ConfirmDialog +}; -}, + }, -446 + 1301 (_, EXP_, REQ_) { "use strict"; -REQ_.d(EXP_, { -M: () => ConversationMessageMixin -}); -const react0__ = REQ_(594); -const react0 = REQ_.n(react0__); -const _mixins1__ = REQ_(137); -const _ui_buttons_jsx2__ = REQ_(994); -const _ui_emojiDropdown_jsx3__ = REQ_(844); - + REQ_.d(EXP_, { + O: () => PerfectScrollbar + }); + const _babel_runtime_helpers_applyDecoratedDescriptor0__ = REQ_(793); + const react1__ = REQ_(1594); + const react1___default = REQ_.n(react1__); + const _chat_mixins2__ = REQ_(8264); +let _dec, _dec2, _class, _PerfectScrollbar; -class ConversationMessageMixin extends _mixins1__.u9 { +const PerfectScrollbar = (_dec = (0,_chat_mixins2__ .hG)(30, true), _dec2 = (0,_chat_mixins2__ .hG)(30, true), _class = (_PerfectScrollbar = class PerfectScrollbar extends _chat_mixins2__ .w9 { constructor(props) { super(props); - this.attachRerenderCallbacks = false; - this._reactionContactHandles = []; - this.__cmmUpdateTickCount = 0; - this._contactChangeListeners = false; - this.onAfterRenderWasTriggered = false; - lazy(this, '__cmmId', () => { - return `${this.getUniqueId() }--${ String(Math.random()).slice(-7)}`; - }); - this._emojiOnActiveStateChange = this._emojiOnActiveStateChange.bind(this); - this.emojiSelected = this.emojiSelected.bind(this); - const { - message: msg - } = this.props; - if (msg instanceof Message && msg._reactions && msg.messageId.length === 11 && msg.isSentOrReceived() && !Object.hasOwnProperty.call(msg, 'reacts')) { - msg.reacts.forceLoad().then(() => { - this.addContactListenerIfMissing(this._reactionContacts()); - }).catch(dump.bind(null, `reactions.load.${msg.messageId}`)); - } + this.domRef = react1___default().createRef(); + this.isUserScroll = true; + this.scrollEventIncId = 0; } - UNSAFE_componentWillMount() { - if (super.UNSAFE_componentWillMount) { - super.UNSAFE_componentWillMount(); - } - const {chatRoom} = this.props; - if (chatRoom) { - chatRoom.rebind(`onChatShown.${ this.__cmmId}`, () => { - if (!this._contactChangeListeners) { - this.addContactListeners(); - } - }).rebind(`onChatHidden.${ this.__cmmId}`, () => { - if (this._contactChangeListeners) { - this.removeContactListeners(); - } - }); + get$Node() { + if (!this.$Node) { + let _this$domRef; + this.$Node = $((_this$domRef = this.domRef) == null ? void 0 : _this$domRef.current); } - this.addContactListeners(); - } - haveMeetingsCall() { - return document.querySelector('.meetings-call') && document.querySelector('.chat-opened'); + return this.$Node; } - removeContactListeners() { - const users = this._contactChangeListeners; - if (d > 3) { - console.warn('%s.removeContactListeners', this.getReactId(), [this], users); - } - for (let i = users.length; i--;) { - users[i].removeEventHandler(this); + doProgramaticScroll(newPos, forced, isX, skipReinitialised) { + if (!this.isMounted()) { + return; } - this._contactChangeListeners = false; + const self = this; + const $elem = self.get$Node(); + let animFrameInner = false; + const prop = !isX ? 'scrollTop' : 'scrollLeft'; + const event = `scroll.progscroll${ self.scrollEventIncId++}`; + $elem.rebind(event, () => { + if (animFrameInner) { + cancelAnimationFrame(animFrameInner); + animFrameInner = false; + } + $elem.off(event); + if (!skipReinitialised) { + self.reinitialised(true); + } else if (typeof skipReinitialised === 'function') { + onIdle(skipReinitialised); + } + self.isUserScroll = true; + }); + self.isUserScroll = false; + $elem[0][prop] = Math.round(newPos); + Ps.update($elem[0]); + animFrameInner = requestAnimationFrame(() => { + animFrameInner = false; + self.isUserScroll = true; + $elem.off(event); + }); + return true; } - _reactionContacts() { - const { - message - } = this.props; - const { - reacts - } = message; - const handles = []; - const reactions = Object.values(reacts.reactions); - for (let i = 0; i < reactions.length; i++) { - handles.push(...Object.keys(reactions[i])); + componentDidMount() { + let _this$props$didMount, _this$props; + super.componentDidMount(); + const self = this; + const $elem = self.get$Node(); + $elem.height('100%'); + const options = Object.assign({}, { + 'handlers': ['click-rail', 'drag-thumb', 'keyboard', 'wheel', 'touch'], + 'minScrollbarLength': 20 + }, self.props.options); + Ps.initialize($elem[0], options); + if (self.props.onFirstInit) { + self.props.onFirstInit(self, $elem); } - this._reactionContactHandles = array.unique(handles); - return this._reactionContactHandles; - } - addContactListeners() { - const users = this._contactChangeListeners || []; - const addUser = user => { - if (user instanceof MegaDataMap && users.indexOf(user) < 0) { - users.push(user); + $elem.rebind(`ps-scroll-y.ps${ self.getUniqueId()}`, (e) => { + if ($elem.attr('data-scroll-disabled') === "true") { + e.stopPropagation(); + e.preventDefault(); + e.originalEvent.stopPropagation(); + e.originalEvent.preventDefault(); + return false; } - }; - addUser(this.getContact()); - if (this.haveMoreContactListeners) { - const moreIds = this.haveMoreContactListeners(); - if (moreIds) { - for (let i = moreIds.length; i--;) { - const handle = moreIds[i]; - addUser(handle in M.u && M.u[handle]); - } + if (self.props.onUserScroll && self.isUserScroll === true && $elem.is(e.target)) { + self.props.onUserScroll(self, $elem, e); } + }); + $elem.rebind(`disable-scroll.ps${ self.getUniqueId()}`, () => { + Ps.destroy($elem[0]); + }); + $elem.rebind(`enable-scroll.ps${ self.getUniqueId()}`, () => { + Ps.initialize($elem[0], options); + }); + $elem.rebind(`forceResize.ps${ self.getUniqueId()}`, (e, forced, scrollPositionYPerc, scrollToElement) => { + self.onResize(forced, scrollPositionYPerc, scrollToElement); + }); + self.onResize(); + this.attachAnimationEvents(); + (_this$props$didMount = (_this$props = this.props).didMount) == null || _this$props$didMount.call(_this$props, this.getUniqueId(), this); + } + componentWillUnmount() { + let _this$props$willUnmou, _this$props2; + super.componentWillUnmount(); + const $elem = this.get$Node(); + $elem.off(`ps-scroll-y.ps${ this.getUniqueId()}`); + const ns = `.ps${ this.getUniqueId()}`; + $elem.parents('.have-animation').unbind(`animationend${ ns } webkitAnimationEnd${ ns } oAnimationEnd${ ns}`); + (_this$props$willUnmou = (_this$props2 = this.props).willUnmount) == null || _this$props$willUnmou.call(_this$props2, this.getUniqueId(), this); + } + attachAnimationEvents() {} + eventuallyReinitialise(forced, scrollPositionYPerc, scrollToElement) { + const self = this; + if (!self.isComponentEventuallyVisible()) { + return; } - for (let i = this._reactionContactHandles.length; i--;) { - addUser(this._reactionContactHandles[i] in M.u && M.u[this._reactionContactHandles[i]]); - } - if (d > 3) { - console.warn('%s.addContactListeners', this.getReactId(), [this], users); - } - for (let i = users.length; i--;) { - users[i].addChangeListener(this); + const $elem = self.get$Node(); + const h = self.getContentHeight(); + if (forced || self._currHeight !== h) { + self._currHeight = h; + self._doReinit(scrollPositionYPerc, scrollToElement, forced, $elem); } - this._contactChangeListeners = users; } - addContactListenerIfMissing(contacts) { - if (!this._contactChangeListeners) { - return false; - } - if (!Array.isArray(contacts)) { - contacts = [contacts]; + _doReinit(scrollPositionYPerc, scrollToElement, forced, $elem) { + let fired = false; + if (this.props.onReinitialise) { + fired = this.props.onReinitialise(this, $elem, forced, scrollPositionYPerc, scrollToElement); } - const added = []; - for (let i = 0; i < contacts.length; i++) { - const user = M.u[contacts[i]]; - if (user && !this._contactChangeListeners.includes(user)) { - this._contactChangeListeners.push(user); - user.addChangeListener(this); - added.push(user.h); + if (fired === false) { + if (scrollPositionYPerc) { + if (scrollPositionYPerc === -1) { + this.scrollToBottom(true); + } else { + this.scrollToPercentY(scrollPositionYPerc, true); + } + } else if (scrollToElement) { + this.scrollToElement(scrollToElement, true); } } - if (d > 1) { - console.warn('%s.addContactListenerIfMissing', this.getReactId(), [this], added); - } } - eventuallyUpdate() { - super.eventuallyUpdate(); - this.__cmmUpdateTickCount = -2; + scrollToBottom(skipReinitialised) { + this.reinitialise(skipReinitialised, true); } - handleChangeEvent(x, z, k) { - if (k === 'ts' || k === 'ats') { + reinitialise(skipReinitialised, bottom) { + let _this$domRef2; + const $elem = (_this$domRef2 = this.domRef) == null ? void 0 : _this$domRef2.current; + if (!$elem) { return; } - if (this.isComponentEventuallyVisible()) { - if (++this.__cmmUpdateTickCount > 5) { - this.eventuallyUpdate(); - delay.cancel(this.__cmmId); - } else { - delay(this.__cmmId, () => this.eventuallyUpdate(), 90); - } - } else { - this._requiresUpdateOnResize = true; + this.isUserScroll = false; + if (bottom) { + $elem.scrollTop = this.getScrollHeight(); + } + Ps.update($elem); + this.isUserScroll = true; + if (!skipReinitialised) { + this.reinitialised(true); } } - componentWillUnmount() { - super.componentWillUnmount(); - const {chatRoom} = this.props; - if (chatRoom) { - chatRoom.off(`onChatShown.${ this.__cmmId}`).off(`onChatHidden.${ this.__cmmId}`); + getDOMRect(node) { + let _this$domRef3; + node = node || ((_this$domRef3 = this.domRef) == null ? void 0 : _this$domRef3.current); + return node && node.getBoundingClientRect(); + } + getScrollOffset(value) { + let _this$domRef4; + const $elem = (_this$domRef4 = this.domRef) == null ? void 0 : _this$domRef4.current; + if ($elem) { + return this.getDOMRect($elem.children[0])[value] - this.getDOMRect($elem)[value]; } - if (this._contactChangeListeners) { - this.removeContactListeners(); + return 0; + } + getScrollHeight() { + const res = this.getScrollOffset('height'); + if (res < 1) { + return this._lastKnownScrollHeight || 0; } + this._lastKnownScrollHeight = res; + return res; } - getContact() { - if (this.props.contact) { - return this.props.contact; + getScrollWidth() { + const res = this.getScrollOffset('width'); + if (res < 1) { + return this._lastKnownScrollWidth || 0; } - const {message} = this.props; - return Message.getContactForMessage(message); + this._lastKnownScrollWidth = res; + return res; } - getTimestampAsString() { - return toLocaleTime(this.getTimestamp()); + getContentHeight() { + const $elem = this.get$Node(); + return $elem[0].scrollHeight; } - getTimestamp(forUpdated) { - const { - message - } = this.props; - const timestamp = (message.getDelay == null ? void 0 : message.getDelay()) || message.delay || unixtime(); - return forUpdated && message.updated > 0 ? timestamp + message.updated : timestamp; + getContentWidth() { + const $elem = this.get$Node(); + return $elem[0].scrollWidth; } - componentDidUpdate() { - const self = this; - const {chatRoom} = self.props.message; - if (!self.onAfterRenderWasTriggered) { - const msg = self.props.message; - let shouldRender = true; - if (msg.isManagement && msg.isManagement() === true && msg.isRenderableManagement() === false) { - shouldRender = false; - } - if (shouldRender) { - chatRoom.trigger("onAfterRenderMessage", self.props.message); - self.onAfterRenderWasTriggered = true; - } - } + setCssContentHeight(h) { + const $elem = this.get$Node(); + return $elem.css('height', h); } - getCurrentUserReactions() { - const { - reactions - } = this.props.message.reacts; - return Object.keys(reactions).filter(utf => { - let _reactions$utf; - return (_reactions$utf = reactions[utf]) == null ? void 0 : _reactions$utf[u_handle]; - }); + isAtTop() { + let _this$domRef5; + return ((_this$domRef5 = this.domRef) == null ? void 0 : _this$domRef5.current.scrollTop) === 0; } - emojiSelected(e, slug, meta) { - const { - chatRoom, - message, - onEmojiBarChange - } = this.props; - if (chatRoom.isReadOnly()) { - return false; - } - const { - reactions - } = this.props.message.reacts; - const CURRENT_USER_REACTIONS = this.getCurrentUserReactions().length; - const REACTIONS_LIMIT = { - TOTAL: 50, - PER_PERSON: 24 - }; - const addReaction = () => chatRoom.messagesBuff.userAddReaction(message.messageId, slug, meta); - const emoji = megaChat._emojiData.emojisSlug[slug] || meta; - if (emoji && message.reacts.getReaction(u_handle, emoji.u)) { - if (onEmojiBarChange && Object.keys(reactions).length === 1 && Object.keys(reactions[emoji.u]).length === 1) { - onEmojiBarChange(false); - } - return chatRoom.messagesBuff.userDelReaction(message.messageId, slug, meta); - } - if (emoji && reactions[emoji.u] && CURRENT_USER_REACTIONS < REACTIONS_LIMIT.PER_PERSON) { - return addReaction(); - } - if (CURRENT_USER_REACTIONS >= REACTIONS_LIMIT.PER_PERSON) { - return msgDialog('info', '', l[24205].replace('%1', REACTIONS_LIMIT.PER_PERSON)); - } - if (Object.keys(reactions).length >= REACTIONS_LIMIT.TOTAL) { - return msgDialog('info', '', l[24206].replace('%1', REACTIONS_LIMIT.TOTAL)); - } else if (onEmojiBarChange && Object.keys(reactions).length === 0) { - onEmojiBarChange(true); - } - return addReaction(); + isAtBottom() { + return Math.round(this.getScrollPositionY()) === Math.round(this.getScrollHeight()); } - _emojiOnActiveStateChange(newVal) { - this.setState(() => { - return { - reactionsDropdownActive: newVal - }; - }); + isCloseToBottom(minPixelsOff) { + return this.getScrollHeight() - this.getScrollPositionY() <= minPixelsOff; } - getEmojisImages() { - const { - chatRoom, - message - } = this.props; - const isReadOnlyClass = chatRoom.isReadOnly() ? " disabled" : ""; - let emojisImages = message._reactions && message.reacts.reactions && Object.keys(message.reacts.reactions).map(utf => { - const reaction = message.reacts.reactions[utf]; - const count = Object.keys(reaction).length; - if (!count) { - return null; - } - const filename = twemoji.convert.toCodePoint(utf); - const currentUserReacted = !!reaction[u_handle]; - const names = []; - if (reaction) { - ChatdIntegration._ensureContactExists(Object.keys(reaction)); - const rKeys = Object.keys(reaction); - for (let i = 0; i < rKeys.length; i++) { - const uid = rKeys[i]; - if (reaction[uid]) { - if (uid === u_handle) { - names.push(l[24071] || 'You'); - } else if (uid in M.u) { - names.push(M.getNameByHandle(uid) || megaChat.plugins.userHelper.SIMPLETIP_USER_LOADER); - } - } - } - } - let emojiData = megaChat._emojiData.emojisUtf[utf]; - if (!emojiData) { - emojiData = Object.create(null); - emojiData.u = utf; - } - let slug = emojiData && emojiData.n || ""; - let tipText; - slug = slug ? `:${slug}:` : utf; - if (Object.keys(reaction).length === 1 && reaction[u_handle]) { - tipText = (l[24068] || "You (click to remove) [G]reacted with %s[/G]").replace("%s", slug); - } else { - tipText = mega.utils.trans.listToString(names, (l[24069] || "%s [G]reacted with %s2[/G]").replace("%s2", slug)); - } - const notFoundEmoji = slug && slug[0] !== ":"; - return react0().createElement("div", { - key: slug, - onClick: ((e, slug, meta) => () => this.emojiSelected(e, slug, meta))(null, slug, emojiData), - className: ` - reactions-bar__reaction - simpletip - ${currentUserReacted ? 'user-reacted' : ''} - ${notFoundEmoji ? 'emoji-loading-error' : ''} - ${isReadOnlyClass} - `, - "data-simpletip": tipText, - "data-simpletipoffset": "3", - "data-simpletipposition": "top" - }, react0().createElement("img", { - width: "10", - height: "10", - className: "emoji emoji-loading", - draggable: "false", - onError: e => { - const textNode = document.createElement("em"); - textNode.classList.remove('emoji-loading'); - textNode.append(document.createTextNode(utf)); - e.target.replaceWith(textNode); - textNode.parentNode.classList.add('emoji-loading-error'); - }, - onLoad: e => { - e.target.classList.remove('emoji-loading'); - }, - src: `${staticpath }images/mega/twemojis/2_v2/72x72/${ filename }.png` - }), react0().createElement("span", { - className: "message text-block" - }, count)); - }); - emojisImages = emojisImages && emojisImages.filter((v) => { - return !!v; - }); - if (emojisImages && emojisImages.length > 0) { - const reactionBtn = !chatRoom.isReadOnly() ? react0().createElement(_ui_buttons_jsx2__.$, { - className: "popup-button reactions-button hover-colorized simpletip", - icon: "sprite-fm-theme icon-emoji-reactions reactions-icon", - disabled: false, - key: "add-reaction-button", - attrs: { - 'data-simpletip': l[24070] || "Add reaction...", - 'data-simpletipoffset': "3", - 'data-simpletipposition': "top" - } - }, react0().createElement(_ui_emojiDropdown_jsx3__.L, { - horizOffset: this.haveMeetingsCall() ? -150 : 0, - onActiveChange: this._emojiOnActiveStateChange, - className: "popup emoji reactions-dropdown", - onClick: this.emojiSelected - })) : null; - emojisImages.push(reactionBtn); + getScrolledPercentY() { + return 100 / this.getScrollHeight() * this.getScrollPositionY(); + } + getScrollPositionY() { + let _this$domRef6; + return (_this$domRef6 = this.domRef) == null ? void 0 : _this$domRef6.current.scrollTop; + } + getScrollPositionX() { + let _this$domRef7; + return (_this$domRef7 = this.domRef) == null ? void 0 : _this$domRef7.current.scrollLeft; + } + getClientWidth() { + let _this$domRef8; + return (_this$domRef8 = this.domRef) == null ? void 0 : _this$domRef8.current.clientWidth; + } + getClientHeight() { + let _this$domRef9; + return (_this$domRef9 = this.domRef) == null ? void 0 : _this$domRef9.current.clientHeight; + } + scrollToPercentY(posPerc, skipReinitialised) { + const $elem = this.get$Node(); + const targetPx = this.getScrollHeight() / 100 * posPerc; + if ($elem[0].scrollTop !== targetPx) { + this.doProgramaticScroll(targetPx, 0, 0, skipReinitialised); } - return emojisImages ? react0().createElement("div", { - className: "reactions-bar", - id: "reactions-bar", - onMouseEnter: () => { - if (this._loadedReacts) { - return false; - } - this._loadedReacts = megaChat.plugins.userHelper.fetchAllNames(this._reactionContacts(), chatRoom).catch(dump).finally(() => { - this._loadedReacts = true; - this.safeForceUpdate(); - }); - } - }, emojisImages) : null; } - getMessageActionButtons() { + scrollToPercentX(posPerc, skipReinitialised) { + const $elem = this.get$Node(); + const targetPx = this.getScrollWidth() / 100 * posPerc; + if ($elem[0].scrollLeft !== targetPx) { + this.doProgramaticScroll(targetPx, false, true, skipReinitialised); + } + } + scrollToY(posY, skipReinitialised) { + const $elem = this.get$Node(); + if ($elem[0].scrollTop !== posY) { + this.doProgramaticScroll(posY, 0, 0, skipReinitialised); + } + } + scrollToElement(element, skipReinitialised) { + if (element && element.offsetParent) { + this.doProgramaticScroll(element.offsetTop, 0, 0, skipReinitialised); + } + } + disable() { + if (this.isMounted()) { + const $elem = this.get$Node(); + $elem.attr('data-scroll-disabled', true); + $elem.addClass('ps-disabled'); + Ps.disable($elem[0]); + } + } + enable() { + if (this.isMounted()) { + const $elem = this.get$Node(); + $elem.removeAttr('data-scroll-disabled'); + $elem.removeClass('ps-disabled'); + Ps.enable($elem[0]); + } + } + reinitialised(forced) { + if (this.props.onReinitialise) { + this.props.onReinitialise(this, this.get$Node(), forced ? forced : false); + } + } + onResize(forced, scrollPositionYPerc, scrollToElement) { + if (forced && forced.originalEvent) { + forced = true; + scrollPositionYPerc = undefined; + } + this.eventuallyReinitialise(forced, scrollPositionYPerc, scrollToElement); + } + inViewport(domNode) { + return verge.inViewport(domNode); + } + componentDidUpdate() { + if (this.props.requiresUpdateOnResize || this.requiresUpdateOnResize) { + this.onResize(true); + } + this.attachAnimationEvents(); + } + customIsEventuallyVisible() { + const {chatRoom} = this.props; + return !chatRoom || chatRoom.isCurrentlyActive; + } + render() { const { - chatRoom, - message + style, + className, + children } = this.props; - return message instanceof Message && message.isSentOrReceived() && !chatRoom.isReadOnly() ? react0().createElement(_ui_buttons_jsx2__.$, { - className: "popup-button reactions-button tiny-button simpletip", - icon: `${"sprite-fm-theme reactions-icon"} icon-emoji-reactions`, - iconHovered: `${"sprite-fm-theme reactions-icon"} icon-emoji-reactions-active`, - disabled: false, - key: "add-reaction-button", - attrs: { - 'data-simpletip': l[24070] || "Add reaction...", - 'data-simpletipoffset': "3", - 'data-simpletipposition': "top" - } - }, react0().createElement(_ui_emojiDropdown_jsx3__.L, { - horizOffset: this.haveMeetingsCall() ? -110 : 0, - noArrow: true, - onActiveChange: this._emojiOnActiveStateChange, - className: "popup emoji reactions-dropdown", - onClick: this.emojiSelected - })) : null; + return JSX_("div", { + ref: this.domRef, + style, + className + }, children); } -} - +}, _PerfectScrollbar.defaultProps = { + className: "perfectScrollbarContainer", + requiresUpdateOnResize: true +}, _PerfectScrollbar), (0,_babel_runtime_helpers_applyDecoratedDescriptor0__ .A)(_class.prototype, "eventuallyReinitialise", [_dec], Object.getOwnPropertyDescriptor(_class.prototype, "eventuallyReinitialise"), _class.prototype), (0,_babel_runtime_helpers_applyDecoratedDescriptor0__ .A)(_class.prototype, "onResize", [_dec2], Object.getOwnPropertyDescriptor(_class.prototype, "onResize"), _class.prototype), _class); -}, + }, -757 + 6411 (_, EXP_, REQ_) { "use strict"; -REQ_.d(EXP_, { -A: () => ScheduleMetaChange -}); -const react0__ = REQ_(594); -const react0 = REQ_.n(react0__); -const _mixin_jsx1__ = REQ_(446); -const _contacts_jsx2__ = REQ_(251); -const _ui_utils_jsx3__ = REQ_(314); -const _ui_buttons_jsx4__ = REQ_(994); - - - - - -class ScheduleMetaChange extends _mixin_jsx1__.M { + REQ_.d(EXP_, { + Ay: () => __WEBPACK_DEFAULT_EXPORT__, + P9: () => ParsedHTML, + T9: () => withOverflowObserver, + lI: () => reactStringWrap, + oM: () => OFlowParsedHTML, + sp: () => OFlowEmoji, + zT: () => Emoji + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const react_dom1__ = REQ_(5206); + const react_dom1___default = REQ_.n(react_dom1__); + const _chat_mixins_js2__ = REQ_(8264); + + + +class RenderTo extends react0___default().Component { constructor(...args) { super(...args); - this.state = { - link: '' - }; - } - componentDidMount() { - super.componentDidMount(); - if (this.props.mode === ScheduleMetaChange.MODE.CREATED) { - if (is_chatlink) { - this.setState({ - link: `${getBaseUrl()}/chat/${is_chatlink.ph}#${is_chatlink.key}` - }); - } else { - const { - chatRoom - } = this.props; - chatRoom.updatePublicHandle().then(() => { - if (this.isMounted() && !this.state.link && chatRoom.publicLink) { - this.setState({ - link: `${getBaseUrl()}/${chatRoom.publicLink}` - }); - } - }).catch(dump); - } - } - if (this.props.message.meta.ap) { - const { - meetingsManager - } = megaChat.plugins; - this.redrawListener = `${meetingsManager.EVENTS.OCCURRENCES_UPDATE}.redraw${this.getUniqueId()}`; - megaChat.rebind(this.redrawListener, () => { - onIdle(() => { - const { - meta - } = this.props.message; - if (!meta.ap) { - return; - } - this.props.message.meta = meetingsManager.noCsMeta(meta.handle, meta.ap, megaChat.chats[meta.cid]); - this.safeForceUpdate(); - }); - megaChat.off(this.redrawListener); - delete this.redrawListener; - }); - } + this.$$rootRef = undefined; + this.popupElement = undefined; } - componentWillUnmount() { - super.componentWillUnmount(); - if (this.redrawListener) { - megaChat.off(this.redrawListener); - } + _setClassNames() { + this.popupElement.className = this.props.className || ''; } - specShouldComponentUpdate(nextProps) { - if (this.props.mode === ScheduleMetaChange.MODE.CREATED && this.props.link !== nextProps.link) { - return true; - } - return null; + _renderLayer() { + this.$$rootRef.render(this.props.children); + queueMicrotask(() => { + let _this$props$popupDidM, _this$props; + return (_this$props$popupDidM = (_this$props = this.props).popupDidMount) == null ? void 0 : _this$props$popupDidM.call(_this$props, this.popupElement); + }); } - componentDidUpdate(prevProps) { - if (this.props.mode === ScheduleMetaChange.MODE.CREATED && prevProps.link !== this.props.link) { - this.setState({ - link: this.props.link ? `${getBaseUrl()}/${this.props.link}` : '' - }); - } + componentDidUpdate() { + this._setClassNames(); + this._renderLayer(); } - onAddToCalendar() { - const { - chatRoom - } = this.props; - const { - id, - title - } = chatRoom && chatRoom.scheduledMeeting || {}; - if (id) { - delay(`fetchical${id}`, () => { - eventlog(500038); - asyncApiReq({ - a: 'mcsmfical', - id - }).then(([, res]) => { - delay(`saveical${id}`, () => { - M.saveAs(base64urldecode(res), `${title.replace(/\W/g, '')}.ics`).then(nop).catch(() => { - msgDialog('error', '', l.calendar_add_failed, ''); - }); - }, 1000); - }).catch(() => { - msgDialog('error', '', l.calendar_add_failed, ''); - }); - }, 250); - } + componentWillUnmount() { + let _this$props$popupWill, _this$props2; + onIdle(() => this.$$rootRef.unmount()); + (_this$props$popupWill = (_this$props2 = this.props).popupWillUnmount) == null || _this$props$popupWill.call(_this$props2, this.popupElement); + this.props.element.removeChild(this.popupElement); } - static getTitleText(meta) { - const { - mode, - recurring, - occurrence, - converted, - prevTiming - } = meta; - const { - MODE - } = ScheduleMetaChange; - switch (mode) { - case MODE.CREATED: - { - return recurring ? l.schedule_mgmt_new_recur : l.schedule_mgmt_new; - } - case MODE.EDITED: - { - if (converted) { - return recurring ? l.schedule_mgmt_update_convert_recur : l.schedule_mgmt_update_convert; - } - if (occurrence) { - return l.schedule_mgmt_update_occur; - } - if (prevTiming) { - return recurring ? l.schedule_mgmt_update_recur : l.schedule_mgmt_update; - } - return l.schedule_mgmt_update_desc; - } - case MODE.CANCELLED: - { - if (recurring) { - return occurrence ? l.schedule_mgmt_cancel_occur : l.schedule_mgmt_cancel_recur; - } - return l.schedule_mgmt_cancel; - } + componentDidMount() { + this.popupElement = document.createElement('div'); + this.$$rootRef = (0,react_dom1__.createRoot)(this.popupElement); + this._setClassNames(); + if (this.props.style) { + $(this.popupElement).css(this.props.style); } - return ''; + this.props.element.appendChild(this.popupElement); + this._renderLayer(); } - renderTimingBlock() { + render() { + return null; + } +} +const withOverflowObserver = Component => class extends _chat_mixins_js2__ .u9 { + constructor(props) { + super(props); + this.displayName = 'OverflowObserver'; + this.ref = react0___default().createRef(); + this.state = { + overflowed: false + }; + this.handleMouseEnter = this.handleMouseEnter.bind(this); + } + handleMouseEnter() { + const element = this.ref && this.ref.current; + if (element) { + this.setState({ + overflowed: element.scrollWidth > element.offsetWidth + }); + } + } + shouldComponentUpdate(nextProps, nextState) { + return nextState.overflowed !== this.state.overflowed || nextProps.children !== this.props.children || nextProps.content !== this.props.content; + } + render() { const { - message, - mode + simpletip } = this.props; - const { - meta - } = message; - const { - MODE - } = ScheduleMetaChange; - if (mode === MODE.CANCELLED && !meta.occurrence) { - return null; - } - const [now, prev] = megaChat.plugins.meetingsManager.getOccurrenceStrings(meta); - return react0().createElement("div", { - className: "schedule-timing-block" - }, meta.prevTiming && react0().createElement("s", null, prev || ''), now); + return JSX_("div", { + ref: this.ref, + className: ` + overflow-observer + ${this.state.overflowed ? 'simpletip simpletip-tc' : ''} + `, + "data-simpletipposition": (simpletip == null ? void 0 : simpletip.position) || 'top', + "data-simpletipoffset": simpletip == null ? void 0 : simpletip.offset, + "data-simpletip-class": (simpletip == null ? void 0 : simpletip.className) || 'medium-width center-align', + onMouseEnter: this.handleMouseEnter + }, JSX_(Component, this.props)); } - checkAndFakeOccurrenceMeta(meta) { +}; +const Emoji = ({ + children +}) => { + return JSX_(ParsedHTML, { + content: megaChat.html(children) + }); +}; +class ParsedHTML extends react0___default().Component { + constructor(...args) { + super(...args); + this.ref = react0___default().createRef(); + } + updateInternalState() { const { - MODE - } = ScheduleMetaChange; - if (meta.occurrence && meta.mode === MODE.CANCELLED && !meta.calendar) { - const meeting = megaChat.plugins.meetingsManager.getMeetingOrOccurrenceParent(meta.handle); - if (meeting) { - const occurrences = meeting.getOccurrencesById(meta.handle); - if (occurrences) { - meta.calendar = { - date: new Date(occurrences[0].start).getDate(), - month: time2date(Math.floor(occurrences[0].start / 1000), 12) - }; - meta.timeRules.startTime = Math.floor(occurrences[0].start / 1000); - meta.timeRules.endTime = Math.floor(occurrences[0].end / 1000); + children, + content + } = this.props; + const ref = this.ref && this.ref.current; + if (!children && !content) { + return d > 1 && console.warn('Emoji: No content passed.'); + } + if (ref) { + if (ref.childNodes.length) { + while (ref.firstChild) { + ref.removeChild(ref.firstChild); } } + ref.appendChild(parseHTML(children || content)); } } + shouldComponentUpdate(nextProps) { + return nextProps && (nextProps.children !== this.props.children || nextProps.content !== this.props.content); + } + componentDidUpdate() { + this.updateInternalState(); + } + componentDidMount() { + this.updateInternalState(); + } render() { const { - chatRoom, - message, - mode, - contact + className, + onClick, + tag } = this.props; - const { - meta, - messageId - } = message; - const { - scheduledMeeting - } = chatRoom; - const { - MODE - } = ScheduleMetaChange; - const { - link - } = this.state; - if (meta.gone) { - return null; - } - this.checkAndFakeOccurrenceMeta(meta); - return react0().createElement("div", null, react0().createElement("div", { - className: "message body", - "data-id": `id${messageId}`, - key: messageId - }, react0().createElement(_contacts_jsx2__.Avatar, { - contact: contact.u, - className: "message avatar-wrapper small-rounded-avatar", - chatRoom - }), react0().createElement("div", { - className: "message schedule-message content-area small-info-txt selectable-txt" - }, react0().createElement(_contacts_jsx2__.ContactButton, { - className: "message", - chatRoom, - contact, - label: react0().createElement(_ui_utils_jsx3__.zT, null, M.getNameByHandle(contact.u)) - }), react0().createElement("div", { - className: "message date-time simpletip", - "data-simpletip": time2date(this.getTimestamp()) - }, this.getTimestampAsString()), react0().createElement("div", { - className: "message text-block" - }, ScheduleMetaChange.getTitleText(meta), " ", !!d && meta.handle), react0().createElement("div", { - className: "message body-block" - }, (meta.prevTiming || meta.calendar || meta.topic && meta.onlyTitle || meta.recurring) && react0().createElement("div", { - className: "schedule-detail-block" - }, meta.calendar && scheduledMeeting && (meta.recurring && !scheduledMeeting.recurring || meta.occurrence && meta.mode === MODE.CANCELLED || !meta.recurring) && react0().createElement("div", { - className: "schedule-calendar-icon" - }, react0().createElement("div", { - className: "schedule-date" - }, meta.calendar.date), react0().createElement("div", { - className: "schedule-month" - }, meta.calendar.month)), react0().createElement("div", { - className: "schedule-detail-main" - }, react0().createElement("div", { - className: "schedule-meeting-title" - }, mode === MODE.CANCELLED ? react0().createElement("s", null, meta.topic || chatRoom.topic) : meta.topic || chatRoom.topic), this.renderTimingBlock()), chatRoom.iAmInRoom() && scheduledMeeting && mode !== MODE.CANCELLED && react0().createElement(_ui_buttons_jsx4__.$, { - className: "mega-button", - onClick: () => this.onAddToCalendar() - }, react0().createElement("span", null, mode === MODE.CREATED && !meta.occurrence ? l.schedule_add_calendar : l.schedule_update_calendar))), mode === MODE.CREATED && scheduledMeeting && scheduledMeeting.description && react0().createElement("div", { - className: "schedule-description" - }, react0().createElement(_ui_utils_jsx3__.P9, null, megaChat.html(scheduledMeeting.description).replace(/\n/g, '
'))), link && react0().createElement("div", null, react0().createElement("div", { - className: "schedule-link-instruction" - }, l.schedule_mgmt_link_instruct), react0().createElement("div", { - className: "schedule-meeting-link" - }, react0().createElement("span", null, link), react0().createElement(_ui_buttons_jsx4__.$, { - className: "mega-button positive", - onClick: () => { - copyToClipboard(link, l[7654]); - delay('chat-event-sm-copy-link', () => eventlog(500039)); - } - }, react0().createElement("span", null, l[63]))), react0().createElement("span", null, l.schedule_link_note)))))); + return JSX_(tag || 'span', { + ref: this.ref, + className, + onClick + }); } } -ScheduleMetaChange.MODE = { - CREATED: 1, - EDITED: 2, - CANCELLED: 3 +const reactStringWrap = (src, find, WrapClass, wrapProps) => { + const endTag = find.replace('[', '[/'); + return JSX_(react0___default().Fragment, null, src.split(find)[0], JSX_(WrapClass, wrapProps, src.substring(src.indexOf(find) + find.length, src.indexOf(endTag))), src.split(endTag)[1]); +}; +const OFlowEmoji = withOverflowObserver(Emoji); +const OFlowParsedHTML = withOverflowObserver(ParsedHTML); + const __WEBPACK_DEFAULT_EXPORT__ = { + RenderTo, + SoonFcWrap: _chat_mixins_js2__ .hG, + OFlowEmoji, + OFlowParsedHTML }; -window.ScheduleMetaChange = ScheduleMetaChange; -}, + }, -707 -(_, EXP_, REQ_) { + 1594 +(module) { "use strict"; -REQ_.d(EXP_, { -a: () => MetaRichpreviewLoading -}); -const React = REQ_(594); -const ConversationMessageMixin = REQ_(446).M; -class MetaRichpreviewLoading extends ConversationMessageMixin { - render() { - return React.createElement("div", { - className: "loading-spinner light small" - }, React.createElement("div", { - className: "main-loader" - })); - } +module.exports = React; + + }, + + 5206 +(module) { + +"use strict"; +module.exports = ReactDOM; + + }, + + 793 +(__webpack_module__, EXP_, REQ_) { + +"use strict"; + REQ_.d(EXP_, { + A: () => _applyDecoratedDescriptor + }); +function _applyDecoratedDescriptor(i, e, r, n, l) { + let a = {}; + return Object.keys(n).forEach((i) => { + a[i] = n[i]; + }), a.enumerable = !!a.enumerable, a.configurable = !!a.configurable, ("value" in a || a.initializer) && (a.writable = !0), a = r.slice().reverse().reduce((r, n) => { + return n(i, e, r) || r; + }, a), l && void 0 !== a.initializer && (a.value = a.initializer ? a.initializer.call(l) : void 0, a.initializer = void 0), void 0 === a.initializer ? (Object.defineProperty(i, e, a), null) : a; } -}, + }, -795 -(_, EXP_, REQ_) { + 8168 +(__webpack_module__, EXP_, REQ_) { "use strict"; + REQ_.d(EXP_, { + A: () => _extends + }); +function _extends() { + return _extends = Object.assign ? Object.assign.bind() : function (n) { + for (let e = 1; e < arguments.length; e++) { + const t = arguments[e]; + for (const r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); + } + return n; + }, _extends.apply(null, arguments); +} -// EXPORTS -REQ_.d(EXP_, { - T: () => TypingArea -}); -// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/applyDecoratedDescriptor.js -const applyDecoratedDescriptor = REQ_(793); -// EXTERNAL MODULE: external "React" -const React_ = REQ_(594); -const REaCt = REQ_.n(React_); -// EXTERNAL MODULE: ./js/ui/utils.jsx -const utils = REQ_(314); -// EXTERNAL MODULE: ./js/chat/mixins.js -const mixins = REQ_(137); -// EXTERNAL MODULE: ./js/ui/emojiDropdown.jsx -const emojiDropdown = REQ_(844); -// EXTERNAL MODULE: ./js/ui/buttons.jsx -const ui_buttons = REQ_(994); -;// ./js/chat/ui/emojiAutocomplete.jsx + } + + }; + + // The module cache + const __webpack_module_cache__ = {}; + + // The require function + function REQ_(moduleId) { + // Check if module is in cache + const cachedModule = __webpack_module_cache__[moduleId]; + if (cachedModule !== undefined) { + return cachedModule.exports; + } + // Create a new module (and put it into the cache) + const module = __webpack_module_cache__[moduleId] = { + // no module.id needed + // no module.loaded needed + exports: {} + }; + + // Execute the module function + __webpack_modules__[moduleId](module, module.exports, REQ_); + + // Return the exports of the module + return module.exports; + } + + // expose the modules object (__webpack_modules__) + REQ_.m = __webpack_modules__; + + + + (() => { + // getDefaultExport function for compatibility with non-harmony modules + REQ_.n = (module) => { + const getter = module && module.__esModule ? + () => module.default : + () => module; + REQ_.d(getter, { a: getter }); + return getter; + }; + })(); + + + (() => { + // define getter functions for harmony exports + REQ_.d = (exports, definition) => { + for(const key in definition) { + if(REQ_.o(definition, key) && !REQ_.o(exports, key)) { + Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); + } + } + }; + })(); + + + (() => { + REQ_.f = {}; + // This file contains only the entry chunk. + // The chunk loading function for additional chunks + REQ_.e = (chunkId) => { + return Promise.all(Object.keys(REQ_.f).reduce((promises, key) => { + REQ_.f[key](chunkId, promises); + return promises; + }, [])); + }; + })(); + + + (() => { + // This function allow to reference async chunks + REQ_.u = (chunkId) => { + // return url for filenames based on template + return `js/chat/bundle.${ {"253":"contacts-panel","313":"cloud-browser","493":"core-ui","543":"start-conversation","716":"schedule-meeting","752":"waiting-room","987":"call"}[chunkId] }.js`; + }; + })(); + + + (() => { + REQ_.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop) + })(); + + + (() => { + const inProgress = {}; + const dataWebpackPrefix = "@meganz/webclient:"; + // loadScript function to load a script via script tag + REQ_.l = (url, done, key, chunkId) => { + if(inProgress[url]) { inProgress[url].push(done); return; } + let script, needAttach; + if(key !== undefined) { + const scripts = document.getElementsByTagName("script"); + for(let i = 0; i < scripts.length; i++) { + const s = scripts[i]; + if(s.getAttribute("src") == url || s.getAttribute("data-webpack") == dataWebpackPrefix + key) { script = s; break; } + } + } + if(!script) { + needAttach = true; + script = document.createElement('script'); + + script.charset = 'utf-8'; + if (REQ_.nc) { + script.setAttribute("nonce", REQ_.nc); + } + script.setAttribute("data-webpack", dataWebpackPrefix + key); + + script.src = url; + } + inProgress[url] = [done]; + const onScriptComplete = (prev, event) => { + // avoid mem leaks in IE. + script.onerror = script.onload = null; + clearTimeout(timeout); + const doneFns = inProgress[url]; + delete inProgress[url]; + script.parentNode && script.parentNode.removeChild(script); + doneFns && doneFns.forEach((fn) => fn(event)); + if(prev) return prev(event); + } + var timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000); + script.onerror = onScriptComplete.bind(null, script.onerror); + script.onload = onScriptComplete.bind(null, script.onload); + needAttach && document.head.appendChild(script); + }; + })(); + + + (() => { + // define __esModule on exports + REQ_.r = (exports) => { + if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { + Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); + } + Object.defineProperty(exports, '__esModule', { value: true }); + }; + })(); + + + (() => { + REQ_.p = "/"; + })(); + + + (() => { + // no baseURI + + // object to store loaded and loading chunks + // undefined = chunk not loaded, null = chunk preloaded/prefetched + // [resolve, reject, Promise] = chunk loading, 0 = chunk loaded + const installedChunks = { + 524: 0 + }; + + REQ_.f.j = (chunkId, promises) => { + // JSONP chunk loading for javascript + let installedChunkData = REQ_.o(installedChunks, chunkId) ? installedChunks[chunkId] : undefined; + if(installedChunkData !== 0) { // 0 means "already installed". + + // a Promise means "currently loading". + if(installedChunkData) { + promises.push(installedChunkData[2]); + } else { + if(true) { // all chunks have JS + // setup Promise in chunk cache + const promise = new Promise((resolve, reject) => installedChunkData = installedChunks[chunkId] = [resolve, reject]); + promises.push(installedChunkData[2] = promise); + + // start chunk loading + const url = REQ_.p + REQ_.u(chunkId); + // create error before stack unwound to get useful stacktrace later + const error = new Error(); + const loadingEnded = (event) => { + if(REQ_.o(installedChunks, chunkId)) { + installedChunkData = installedChunks[chunkId]; + if(installedChunkData !== 0) installedChunks[chunkId] = undefined; + if(installedChunkData) { + const errorType = event && (event.type === 'load' ? 'missing' : event.type); + const realSrc = event && event.target && event.target.src; + error.message = `Loading chunk ${ chunkId } failed.\n(${ errorType }: ${ realSrc })`; + error.name = 'ChunkLoadError'; + error.type = errorType; + error.request = realSrc; + installedChunkData[1](error); + } + } + }; + REQ_.l(url, loadingEnded, `chunk-${ chunkId}`, chunkId); + } + } + } + }; + + // no prefetching + + // no preloaded + + // no HMR + + // no HMR manifest + + // no on chunks loaded + + // install a JSONP callback for chunk loading + const webpackJsonpCallback = (parentChunkLoadingFunction, data) => { + const [chunkIds, moreModules, runtime] = data; + // add "moreModules" to the modules object, + // then flag all "chunkIds" as loaded and fire callback + let moduleId, chunkId, i = 0; + if(chunkIds.some((id) => installedChunks[id] !== 0)) { + for(moduleId in moreModules) { + if(REQ_.o(moreModules, moduleId)) { + REQ_.m[moduleId] = moreModules[moduleId]; + } + } + if(runtime) var result = runtime(REQ_); + } + if(parentChunkLoadingFunction) parentChunkLoadingFunction(data); + for(;i < chunkIds.length; i++) { + chunkId = chunkIds[i]; + if(REQ_.o(installedChunks, chunkId) && installedChunks[chunkId]) { + installedChunks[chunkId][0](); + } + installedChunks[chunkId] = 0; + } + + } + + const chunkLoadingGlobal = self.webpackChunk_meganz_webclient = self.webpackChunk_meganz_webclient || []; + chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0)); + chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal)); + })(); + + +const EXP_ = {}; +// This entry needs to be wrapped in an IIFE because it needs to be isolated against other entry modules. +(() => { +/** + * `MegaChunkLoader` + * --------------------------------------------------------------------------------------------------------------------- + * Overrides the default `webpack` chunk loading mechanism (`REQ_.l`) to integrate + * `React.lazy`/`React.Suspense` with the `secureboot` architecture of the `webclient`, which ensures lazy-loaded chunks + * go through MEGA's XHR + hash verification pipeline. + * + * 1) What + * --------------------------------------------------------------------------------------------------------------------- + * By default `webpack` loads chunks by injecting `script` tags with standard URLs. MEGA's security model requires all + * code to be i) loaded via XHR (as to create blob URL entities) and ii) verified against a pre-computed SHA-256 hash. + * + * The default `webpack` loader behavior bypasses these integrity checks, which is not optimal. Additionally, the + * `webpack` Hot Module Replacement (HMR) relies on injecting unverified code at runtime, which is incompatible with + * the `webclient` integrity model. + * + * 2) How + * --------------------------------------------------------------------------------------------------------------------- + * This loader intercepts the `import()` calls generated by `React.lazy()`: + * + * i) `webpack` calls `REQ_.l(url, done)` to load a chunk; we capture this call and derive the `url` + * from `chunkFilename` in `webpack.config.js` (ex.: `js/chat/bundle.call.js`) + * ii) we parse the URL as to extract the logical chunk name (ex.: `js/chat/bundle.call.js` -> `call` -> `chat:call_js`) + * iii) we request the resource via `M.require('chat:call_js')` call, as `secureboot.js` maintains the respective + * registry (jsl2) by mapping this logical key to the physical, hashed filename in production. + * iv) `M.require` handles the XHR download and performs the SHA-256 hash check; if the hash does not match the + * build-time manifest, the load is blocked to prevent tampering. + * v) we call the `done()` callback on success, which allows the `React.Suspense` boundary to render the component. + * + * 3) Constraints + * --------------------------------------------------------------------------------------------------------------------- + * - the original `REQ_.l` loader is intentionally not preserved as a fallback -- if a chunk fails the + * integrity check (no `jsl2` entry) chunk loading fails securely rather than bypassing hash verification via the + * default `script` tag injection. + * - `splitChunks` is disabled; we want deterministic, manually defined chunks (via `webpackChunkName`) to + * ensure 1:1 mapping with the `secureboot` registry. + * - `hot` (HMR) is disabled; development can rely on standard page reloads in favor of ensuring the dev environment + * mirrors the production security pipeline. + */ + +const webpackRequire = true ? REQ_ : 0; +if (webpackRequire && webpackRequire.l) { + const CHUNK_LOADER_DEBUG = d && localStorage.chunkLoaderDebug; + const logger = MegaLogger.getLogger('MegaChunkLoader', { + levelColors: { + 'DEBUG': 'olive', + 'ERROR': 'tomato' + } + }); + webpackRequire.l = (url, done) => { + const startTime = CHUNK_LOADER_DEBUG ? performance.now() : 0; + const match = url.match(/bundle\.([^.]+)\.js$/); + const chunkName = match == null ? void 0 : match[1]; + const jslName = chunkName ? `chat:${chunkName.replace(/-/g, '_')}_js` : null; + const hasJslEntry = jslName && jsl2[jslName]; + if (hasJslEntry) { + M.require(jslName).then(() => { + if (CHUNK_LOADER_DEBUG) { + const duration = (performance.now() - startTime).toFixed(1); + logger.debug(`Loaded '${chunkName}' chunk in ${duration}ms`, { + chunkName, + jslName, + url + }); + } + done({ + type: 'load' + }); + }).catch(ex => { + logger.error(`Failed to load ${jslName}`, { + error: ex, + chunkName, + jslName, + url + }); + done({ + type: 'error', + target: { + src: url + } + }); + }); + } else { + logger.error(`Blocked insecure chunk load ('jsl2' entry missing)`, { + chunkName, + jslName, + url + }); + done({ + type: 'error', + target: { + src: url + } + }); + } + }; +} +})(); +// This entry needs to be wrapped in an IIFE because it needs to be in strict mode. +(() => { +"use strict"; +// UNUSED EXPORTS: default -class EmojiAutocomplete extends mixins.w9 { - constructor(props) { - super(props); - this.domRef = REaCt().createRef(); - this.state = { - 'selected': 0 +// EXTERNAL MODULE: external "React" +const external_React_ = REQ_(1594); +const REaCt = REQ_.n(external_React_); +// EXTERNAL MODULE: external "ReactDOM" +const external_ReactDOM_ = REQ_(5206); +// EXTERNAL MODULE: ./js/chat/ui/conversations.jsx + 2 modules +const conversations = REQ_(4904); +;// ./js/chat/chatRouting.jsx +let _ChatRouting; +class ChatRouting { + constructor(megaChatInstance) { + this.megaChat = megaChatInstance; + } + openCustomView(sectionName) { + const {megaChat} = this; + megaChat.routingSection = sectionName; + megaChat.hideAllChats(); + delete megaChat.lastOpenedChat; + } + route(resolve, reject, location, event, isLandingPage) { + if (!M.chat) { + console.error('This function is meant to navigate within the chat...'); + return; + } + const args = String(location || '').split('/').map(String.trim).filter(String); + if (args[0] === 'fm') { + args.shift(); + } + if (args[0] === 'chat') { + args.shift(); + } + if (args[0] && args[0].length > 8 && args[0].substring(0, 8) === 'contacts') { + location = location.replace(args[0], 'contacts'); + args[0] = 'contacts'; + } + const [section] = args; + const { + megaChat + } = this; + if (d) { + megaChat.logger.warn('navigate(%s)', location, args); + } + args.route = { + location, + section, + args }; - this.loading = false; - this.data_emojis = []; - } - preload_emojis() { - if (this.loading === false) { - this.loading = true; - megaChat.getEmojiDataSet('emojis').then(emojis => { - this.loading = 0; - this.data_emojis = emojis; - this.safeForceUpdate(); - }); + if (isLandingPage) { + megaChat.eventuallyInitMeetingUI(); } - } - unbindKeyEvents() { - $(document).off(`keydown.emojiAutocomplete${ this.getUniqueId()}`); - } - bindKeyEvents() { - const self = this; - $(document).rebind(`keydown.emojiAutocomplete${ self.getUniqueId()}`, (e) => { - if (!self.props.emojiSearchQuery) { - self.unbindKeyEvents(); - return; - } - let key = e.keyCode || e.which; - if (!$(e.target).is("textarea")) { - console.error("this should never happen."); - return; - } - if (e.altKey || e.metaKey) { - return; - } - let selected = $.isNumeric(self.state.selected) ? self.state.selected : 0; - if (document.body.classList.contains('rtl') && (key === 37 || key === 39)) { - key = key === 37 ? 39 : 37; + megaChat.routingSection = 'chat'; + megaChat.routingSubSection = null; + megaChat.routingParams = null; + const handler = ChatRouting.gPageHandlers[section || 'start']; + if (handler) { + handler.call(this, args.route).then(resolve).catch(reject); + resolve = null; + } else { + let roomId = String(args[(section === 'c' || section === 'g' || section === 'p') | 0] || ''); + if (roomId.includes('#')) { + let key = roomId.split('#'); + roomId = key[0]; + key = key[1]; + megaChat.publicChatKeys[roomId] = key; + roomId = megaChat.handleToId[roomId] || roomId; } - let handled = false; - if (!e.shiftKey && (key === 37 || key === 38)) { - selected = selected - 1; - selected = selected < 0 ? self.maxFound - 1 : selected; - if (self.found[selected] && self.state.selected !== selected) { - self.setState({ - selected, - 'prefilled': true - }); - handled = true; - self.props.onPrefill(false, `:${ self.found[selected].n }:`); - } - } else if (!e.shiftKey && (key === 39 || key === 40 || key === 9)) { - selected = selected + (key === 9 ? e.shiftKey ? -1 : 1 : 1); - selected = selected < 0 ? Object.keys(self.found).length - 1 : selected; - selected = selected >= self.props.maxEmojis || selected >= Object.keys(self.found).length ? 0 : selected; - if (self.found[selected] && (key === 9 || self.state.selected !== selected)) { - self.setState({ - selected, - 'prefilled': true - }); - self.props.onPrefill(false, `:${ self.found[selected].n }:`); - handled = true; - } - } else if (key === 13) { - self.unbindKeyEvents(); - if (selected === -1) { - if (self.found.length > 0) { - for (let i = 0; i < self.found.length; i++) { - if (`:${ self.found[i].n }:` === `${self.props.emojiSearchQuery }:`) { - self.props.onSelect(false, `:${ self.found[0].n }:`); - handled = true; - } - } + const room = megaChat.getChatById(roomId); + if (room) { + room.show(); + args.route.location = room.getRoomUrl(); + } else if (!roomId || roomId === u_handle || roomId.length !== 11 && !is_chatlink) { + ChatRouting.gPageHandlers.redirect(args.route, 'fm/chat').then(resolve).catch(reject); + resolve = null; + } else if (section === 'p') { + megaChat.smartOpenChat([u_handle, roomId], 'private', undefined, undefined, undefined, true).then(resolve).catch(reject); + resolve = null; + } else { + megaChat.plugins.chatdIntegration.openChat(roomId).then(chatId => { + megaChat.getChatById(chatId).show(); + return chatId; + }).catch(ex => { + if (d && ex !== ENOENT) { + console.warn('If "%s" is a chat, something went wrong..', roomId, ex); } - if (!handled && key === 13) { - self.props.onCancel(); + if (page !== location) { + return EEXPIRED; } - return; - } else if (self.found.length > 0 && self.found[selected]) { - self.props.onSelect(false, `:${ self.found[selected].n }:`); - handled = true; - } else { - self.props.onCancel(); - } - } else if (key === 27) { - self.unbindKeyEvents(); - self.props.onCancel(); - handled = true; - } - if (handled) { - e.preventDefault(); - e.stopPropagation(); - return false; - } else { - if (self.isMounted()) { - self.setState({ - 'prefilled': false - }); - } + megaChat.cleanup(true); + if (ex === ENOENT || ex === EBLOCKED && megaChat.publicChatKeys[roomId]) { + msgDialog('warninga', '', l[20641], l[20642], () => { + loadSubPage(is_chatlink ? 'start' : 'fm/chat', event); + }); + } else { + if (String(location).startsWith('chat')) { + location = 'fm/chat'; + } + loadSubPage(location, location.includes('chat') ? 'override' : event); + } + return EACCESS; + }).then(resolve).catch(reject); + resolve = null; } - }); - } - componentDidUpdate() { - if (!this.props.emojiSearchQuery) { - this.unbindKeyEvents(); - } else { - this.bindKeyEvents(); } - } - componentWillUnmount() { - super.componentWillUnmount(); - this.unbindKeyEvents(); - } - render() { - const self = this; - if (!self.props.emojiSearchQuery) { - return null; + if (resolve) { + onIdle(resolve); } - self.preload_emojis(); - if (self.loading) { - return REaCt().createElement("div", { - className: "textarea-autofill-bl" - }, REaCt().createElement("div", { - className: "textarea-autofill-info" - }, l[5533])); - } - const q = self.props.emojiSearchQuery.substr(1, self.props.emojiSearchQuery.length); - let exactMatch = []; - let partialMatch = []; - const emojis = self.data_emojis || []; - for (var i = 0; i < emojis.length; i++) { - const emoji = emojis[i]; - const match = emoji.n.indexOf(q); - if (match !== -1) { - if (match === 0) { - exactMatch.push(emoji); - } else if (partialMatch.length < self.props.maxEmojis - exactMatch.length) { - partialMatch.push(emoji); - } - } - if (exactMatch.length >= self.props.maxEmojis) { - break; - } + if (args.route.location !== location) { + location = args.route.location; } - exactMatch.sort((a, b) => { - if (a.n === q) { - return -1; - } else if (b.n === q) { - return 1; - } else { - return 0; - } - }); - const found = exactMatch.concat(partialMatch).slice(0, self.props.maxEmojis); - exactMatch = partialMatch = null; - this.maxFound = found.length; - this.found = found; - if (!found || found.length === 0) { - queueMicrotask(() => { - self.props.onCancel(); - }); - return null; + const method = page === 'chat' || page === 'fm/chat' || page === location || event && event.type === 'popstate' ? 'replaceState' : 'pushState'; + mBroadcaster.sendMessage('beforepagechange', location); + M.currentdirid = String(page = location).replace('fm/', ''); + if (location.substr(0, 13) === "chat/contacts") { + location = `fm/${ location}`; } - const emojisDomList = []; - for (var i = 0; i < found.length; i++) { - const meta = found[i]; - const filename = twemoji.convert.toCodePoint(meta.u); - emojisDomList.push(REaCt().createElement("div", { - className: `emoji-preview shadow ${ this.state.selected === i ? "active" : ""}`, - key: `${meta.n }_${ this.state.selected === i ? "selected" : "inselected"}`, - title: `:${ meta.n }:`, - onClick (e) { - self.props.onSelect(e, e.target.title); - self.unbindKeyEvents(); - } - }, REaCt().createElement("img", { - width: "20", - height: "20", - className: "emoji emoji-loading", - draggable: "false", - alt: meta.u, - onLoad: e => { - e.target.classList.remove('emoji-loading'); - }, - onError: e => { - e.target.classList.remove('emoji-loading'); - e.target.classList.add('emoji-loading-error'); - }, - src: `${staticpath }images/mega/twemojis/2_v2/72x72/${ filename }.png` - }), REaCt().createElement("div", { - className: "emoji title" - }, `:${ meta.n }:`))); + if (location === 'chat') { + location = 'fm/chat'; } - return REaCt().createElement("div", { - ref: this.domRef, - className: "textarea-autofill-bl" - }, REaCt().createElement(utils.P9, { - tag: "div", - className: "textarea-autofill-info" - }, l.emoji_suggestion_instruction), REaCt().createElement("div", { - className: "textarea-autofill-emoji" - }, emojisDomList)); + history[method]({ + subpage: location + }, "", (hashLogic ? '#' : '/') + location); + mBroadcaster.sendMessage('pagechange', page); + } + initFmAndChat(targetChatId) { + assert(!fminitialized); + return new Promise((res, rej) => { + M.currentdirid = targetChatId ? `fm/chat/${ targetChatId}` : undefined; + loadSubPage('fm'); + mBroadcaster.once('chat_initialized', () => { + authring.onAuthringReady().then(res, rej); + }); + }); + } + reinitAndOpenExistingChat(chatId, publicChatHandle = false, cbBeforeOpen = undefined) { + const chatUrl = `fm/chat/c/${ chatId}`; + publicChatHandle = publicChatHandle || megaChat.initialPubChatHandle; + megaChat.destroy(); + is_chatlink = false; + loadingDialog.pshow(); + return new Promise((resolve, reject) => { + this.initFmAndChat(chatId).always(() => { + megaChat.initialPubChatHandle = publicChatHandle; + megaChat.initialChatId = chatId; + const next = () => { + mBroadcaster.once('pagechange', () => { + onIdle(() => { + loadingDialog.phide(); + megaChat.renderListing(chatUrl, true).catch(ex => { + console.error("Failed to megaChat.renderListing:", ex); + reject(ex); + }).always(() => { + megaChat.updateKeysInProtocolHandlers(); + const chatRoom = megaChat.getChatById(chatId); + assert(chatRoom); + if (chatRoom.state === ChatRoom.STATE.READY) { + resolve(chatRoom); + } else { + chatRoom.rebind('onMessagesHistoryDone.reinitAndOpenExistingChat', () => { + if (chatRoom.state === ChatRoom.STATE.READY) { + resolve(chatRoom); + chatRoom.unbind('onMessagesHistoryDone.reinitAndOpenExistingChat'); + } + }); + } + }); + }); + }); + loadSubPage(chatUrl); + }; + if (cbBeforeOpen) { + cbBeforeOpen().then(next, ex => { + console.error("Failed to execute `cbBeforeOpen`, got a reject of the returned promise:", ex); + }); + } else { + next(); + } + }).catch(ex => reject(ex)); + }); + } + reinitAndJoinPublicChat(chatId, initialPubChatHandle, publicChatKey) { + initialPubChatHandle = initialPubChatHandle || megaChat.initialPubChatHandle; + megaChat.destroy(); + is_chatlink = false; + loadingDialog.pshow(); + return new Promise((res, rej) => { + this.initFmAndChat(chatId).then(() => { + megaChat.initialPubChatHandle = initialPubChatHandle; + megaChat.initialChatId = chatId; + const mciphReq = megaChat.plugins.chatdIntegration.getMciphReqFromHandleAndKey(initialPubChatHandle, publicChatKey); + const isReady = chatRoom => { + if (chatRoom.state === ChatRoom.STATE.READY) { + res(chatRoom); + loadingDialog.phide(); + } else { + chatRoom.rebind('onMessagesHistoryDone.reinitAndOpenExistingChat', () => { + if (chatRoom.state === ChatRoom.STATE.READY) { + res(chatRoom); + loadingDialog.phide(); + chatRoom.unbind('onMessagesHistoryDone.reinitAndOpenExistingChat'); + } + }); + } + }; + const join = () => { + const existingRoom = megaChat.getChatById(chatId); + if (!existingRoom) { + megaChat.rebind('onRoomInitialized.reinitAndJoinPublicChat', (e, megaRoom) => { + if (megaRoom.chatId === chatId) { + megaRoom.setActive(); + isReady(megaRoom); + megaChat.unbind('onRoomInitialized.reinitAndJoinPublicChat'); + } + }); + } else { + existingRoom.setActive(); + isReady(existingRoom); + } + }; + join(); + asyncApiReq(mciphReq).then(join).catch(ex => { + if (ex === EEXIST) { + join(); + } else { + loadingDialog.phide(); + console.error("Bad response for mciphReq:", mciphReq, ex); + rej(ex); + } + }); + }); + }); } } -EmojiAutocomplete.defaultProps = { - 'requiresUpdateOnResize': true, - 'emojiSearchQuery': false, - 'disableCheckingVisibility': true, - 'maxEmojis': 12 +_ChatRouting = ChatRouting; +ChatRouting.gPageHandlers = { + async start({ + location + }) { + return megaChat.onChatsHistoryReady(15e3).then(() => { + return page === location ? megaChat.renderListing() : EACCESS; + }); + }, + async redirect(target, path = 'fm/chat') { + target.location = path; + return _ChatRouting.gPageHandlers.start(target); + }, + async new_meeting(target) { + megaChat.trigger('onStartNewMeeting'); + return _ChatRouting.gPageHandlers.redirect(target); + }, + async contacts({ + section, + args + }) { + this.openCustomView(section); + const [, target = ''] = args; + if (target.length === 11) { + megaChat.routingSubSection = "contact"; + megaChat.routingParams = target; + } else if (target === "received" || target === "sent") { + megaChat.routingSubSection = target; + } + } }; -// EXTERNAL MODULE: ./js/chat/ui/gifPanel/gifPanel.jsx + 3 modules -const gifPanel = REQ_(691); -// EXTERNAL MODULE: ./js/ui/perfectScrollbar.jsx -const perfectScrollbar = REQ_(486); -;// ./js/chat/ui/typingArea.jsx - -let _dec, _class; - - - - - +// EXTERNAL MODULE: ./js/chat/ui/messages/scheduleMetaChange.jsx +const scheduleMetaChange = REQ_(5470); +// EXTERNAL MODULE: ./js/chat/chatRoom.jsx +const chat_chatRoom = REQ_(7057); +// EXTERNAL MODULE: ./js/chat/ui/meetings/schedule/helpers.jsx +const helpers = REQ_(6521); +;// ./js/chat/meetingsManager.jsx -const TypingArea = (_dec = (0,mixins.hG)(54, true), _class = class TypingArea extends mixins.w9 { - constructor(props) { - super(props); - this.domRef = REaCt().createRef(); - this.state = { - emojiSearchQuery: false, - textareaHeight: 20, - gifPanelActive: false - }; - this.getTextareaMaxHeight = () => { - const { - containerRef - } = this.props; - if (containerRef && containerRef.current) { - return this.isMounted() ? containerRef.current.offsetHeight * 0.4 : 100; - } - return 100; - }; +class Occurrence { + constructor(megaChat, occurrence) { const { - chatRoom - } = props; - this.logger = d && MegaLogger.getLogger("TypingArea", {}, chatRoom && chatRoom.logger || megaChat.logger); - this.onEmojiClicked = this.onEmojiClicked.bind(this); - this.onTypeAreaKeyUp = this.onTypeAreaKeyUp.bind(this); - this.onTypeAreaKeyDown = this.onTypeAreaKeyDown.bind(this); - this.onTypeAreaBlur = this.onTypeAreaBlur.bind(this); - this.onTypeAreaChange = this.onTypeAreaChange.bind(this); - this.onCopyCapture = this.onCopyCapture.bind(this); - this.onPasteCapture = this.onPasteCapture.bind(this); - this.onCutCapture = this.onCutCapture.bind(this); - this.state.typedMessage = this.props.initialText || ''; - } - onEmojiClicked(e, slug) { - if (this.props.disabled) { - e.preventDefault(); - e.stopPropagation(); - return; - } - slug = slug[0] === ':' || slug.substr(-1) === ':' ? slug : `:${slug}:`; - const textarea = $('.messages-textarea', this.domRef.current)[0]; - const cursorPosition = this.getCursorPosition(textarea); + decodeData + } = megaChat.plugins.meetingsManager; + this.megaChat = megaChat; + this.id = occurrence.id; + this.uid = `${occurrence.cid}-${occurrence.o || occurrence.s}`; + this.chatId = occurrence.cid; + this.parentId = occurrence.p; + this.start = occurrence.s * 1000; + this.startInitial = parseInt(occurrence.o) * 1000 || undefined; + this.end = occurrence.e * 1000; + this.timezone = decodeData(occurrence.tz); + this.title = decodeData(occurrence.t); + this.description = decodeData(occurrence.d); + this.ownerHandle = occurrence.u; + this.flags = occurrence.f; + this.canceled = occurrence.c; + this.scheduledMeeting = occurrence.scheduledMeeting; + } + get isUpcoming() { + return !this.canceled && this.end > Date.now(); + } + cancel() { const { - text, - onValueChanged - } = this.props; - const val = text.slice(0, cursorPosition) + slug + text.slice(cursorPosition); - onValueChanged(val); - textarea.selectionEnd = cursorPosition + slug.length; - this.onTypeAreaChange(e, val); + encodeData + } = this.megaChat.plugins.meetingsManager; + const req = { + a: 'mcsmp', + p: this.parentId || this.id, + ...this.parentId && { + id: this.id + }, + cid: this.chatId, + o: this.start / 1000, + s: this.start / 1000, + e: this.end / 1000, + tz: encodeData(this.timezone), + t: encodeData(this.title), + d: encodeData(this.description) || '', + f: this.scheduledMeeting.flags, + c: 1 + }; + asyncApiReq(req).catch(ex => console.error('Occurrence > cancel ->', ex)); } - stoppedTyping() { - if (this.props.disabled || !this.props.chatRoom) { - return; - } - this.iAmTyping = false; - this.props.chatRoom.trigger('stoppedTyping'); + update(startDateTime, endDateTime) { + const { + encodeData + } = this.megaChat.plugins.meetingsManager; + const req = { + a: 'mcsmp', + cid: this.chatId, + p: this.parentId || this.id, + ...this.parentId && { + id: this.id + }, + o: this.start / 1000, + s: startDateTime / 1000, + e: endDateTime / 1000, + tz: encodeData(this.timezone), + t: encodeData(this.title), + d: encodeData(this.description) || '', + f: this.scheduledMeeting.flags + }; + asyncApiReq(req).catch(ex => console.error('Occurrence > update ->', ex)); } - typing() { - if (this.props.disabled || !this.props.chatRoom) { - return; - } - const self = this; - const now = Date.now(); - delay(this.getReactId(), () => self.iAmTyping && self.stoppedTyping(), 4e3); - if (!self.iAmTyping || now - self.lastTypingStamp > 4e3) { - self.iAmTyping = true; - self.lastTypingStamp = now; - self.props.chatRoom.trigger('typing'); +} +class ScheduledMeeting { + constructor(megaChat, meetingInfo, fromActionPacket) { + const { + decodeData + } = megaChat.plugins.meetingsManager; + this.megaChat = megaChat; + this.id = meetingInfo.id; + this.chatId = meetingInfo.cid; + this.parentId = meetingInfo.p; + this.start = meetingInfo.s * 1000; + this.startInitial = parseInt(meetingInfo.o) * 1000 || undefined; + this.end = meetingInfo.e * 1000; + this.timezone = decodeData(meetingInfo.tz); + this.title = decodeData(meetingInfo.t); + this.description = decodeData(meetingInfo.d); + this.flags = meetingInfo.f; + this.canceled = meetingInfo.c; + this.recurring = meetingInfo.r && { + frequency: meetingInfo.r.f || undefined, + interval: meetingInfo.r.i || 0, + end: meetingInfo.r.u * 1000 || undefined, + weekDays: meetingInfo.r.wd || [], + monthDays: meetingInfo.r.md || [], + offset: meetingInfo.r.mwd && meetingInfo.r.mwd.length ? { + value: meetingInfo.r.mwd[0][0], + weekDay: meetingInfo.r.mwd[0][1] + } : [] + }; + this.occurrences = new MegaDataMap(); + this.nextOccurrenceStart = this.start; + this.nextOccurrenceEnd = this.end; + this.isCompleted = false; + this.ownerHandle = meetingInfo.u; + this.chatRoom = meetingInfo.chatRoom; + this.chatRoom.scheduledMeeting = this.isRoot ? this : this.parent; + if (fromActionPacket) { + this.initializeFromActionPacket(); } } - triggerOnUpdate(forced) { - const self = this; - if (!self.props.onUpdate || !self.isMounted()) { - return; - } - let shouldTriggerUpdate = forced ? forced : false; - if (!shouldTriggerUpdate && self.props.text !== self.lastTypedMessage) { - self.lastTypedMessage = self.props.text; - shouldTriggerUpdate = true; - } - if (!shouldTriggerUpdate) { - const $textarea = $('.chat-textarea:visible textarea:visible', self.domRef.current); - if (!self._lastTextareaHeight || self._lastTextareaHeight !== $textarea.height()) { - self._lastTextareaHeight = $textarea.height(); - shouldTriggerUpdate = true; - if (self.props.onResized) { - self.props.onResized(); - } - } - } - if (shouldTriggerUpdate) { - self.props.onUpdate(); - } + get isRoot() { + return !this.parentId; } - onCancelClicked() { - const self = this; - self.props.onValueChanged(''); - if (self.props.chatRoom && self.iAmTyping) { - self.stoppedTyping(); - } - self.onConfirmTrigger(false); - self.triggerOnUpdate(); + get isCanceled() { + return !!this.canceled; } - onSaveClicked() { - const self = this; - if (self.props.disabled || !self.isMounted()) { - return; - } - const val = $.trim($('.chat-textarea:visible textarea:visible', this.domRef.current).val()); - if (self.onConfirmTrigger(val) !== true) { - self.props.onValueChanged(''); - } - if (self.props.chatRoom && self.iAmTyping) { - self.stoppedTyping(); - } - self.triggerOnUpdate(); + get isPast() { + return (this.isRecurring ? this.recurring.end : this.end) < Date.now(); } - onConfirmTrigger(val) { - const { - onConfirm, - persist, - chatRoom - } = this.props; - const result = onConfirm(val); - if (val !== false && result !== false) { - $('.textarea-scroll', this.domRef.current).scrollTop(0); - } - if (persist) { - const { - persistedTypeArea - } = chatRoom.megaChat.plugins; - if (persistedTypeArea) { - if (d > 2) { - this.logger.info('Removing persisted-typed value...'); - } - persistedTypeArea.removePersistedTypedValue(chatRoom); - } - } - return result; + get isUpcoming() { + return !(this.isCanceled || this.isPast || this.isCompleted); } - onTypeAreaKeyDown(e) { - if (this.props.disabled) { - e.preventDefault(); - e.stopPropagation(); - return; - } - const self = this; - const key = e.keyCode || e.which; - const element = e.target; - const val = $.trim(element.value); - if (self.state.emojiSearchQuery) { - return; - } - if (key === 13 && !e.shiftKey && !e.ctrlKey && !e.altKey) { - if (e.isPropagationStopped() || e.isDefaultPrevented()) { - return; - } - if (self.onConfirmTrigger(val) !== true) { - self.props.onValueChanged(''); - $(document).trigger('closeDropdowns'); - } - e.preventDefault(); - e.stopPropagation(); - if (self.props.chatRoom && self.iAmTyping) { - self.stoppedTyping(); - } - } + get isRecurring() { + return !!this.recurring; } - onTypeAreaKeyUp(e) { - if (this.props.disabled) { - e.preventDefault(); - e.stopPropagation(); - return; - } - const self = this; - const key = e.keyCode || e.which; - const element = e.target; - const val = $.trim(element.value); - if (key === 13 && !e.shiftKey && !e.ctrlKey && !e.altKey) { - e.preventDefault(); - e.stopPropagation(); - } else if (key === 13) { - if (self.state.emojiSearchQuery) { - return; - } - if (e.altKey) { - let content = element.value; - const cursorPos = self.getCursorPosition(element); - content = `${content.substring(0, cursorPos) }\n${ content.substring(cursorPos, content.length)}`; - self.props.onValueChanged(content); - self.onUpdateCursorPosition = cursorPos + 1; - e.preventDefault(); - } else if ($.trim(val).length === 0) { - e.preventDefault(); - } - } else if (key === 38) { - if (self.state.emojiSearchQuery) { - return; - } - if ($.trim(val).length === 0) { - if (self.props.onUpEditPressed && self.props.onUpEditPressed() === true) { - e.preventDefault(); - } - } - } else if (key === 27) { - if (self.state.emojiSearchQuery) { - return; - } - if (self.props.showButtons === true) { - e.preventDefault(); - self.onCancelClicked(e); - } - } else { - if (self.prefillMode && (key === 8 || key === 32 || key === 186 || key === 13)) { - self.prefillMode = false; - } - const currentContent = element.value; - const currentCursorPos = self.getCursorPosition(element) - 1; - if (self.prefillMode && (currentCursorPos > self.state.emojiEndPos || currentCursorPos < self.state.emojiStartPos)) { - self.prefillMode = false; - self.setState({ - 'emojiSearchQuery': false, - 'emojiStartPos': false, - 'emojiEndPos': false - }); - return; - } - if (self.prefillMode) { - return; - } - const char = String.fromCharCode(key); - if (key === 16 || key === 17 || key === 18 || key === 91 || key === 8 || key === 37 || key === 39 || key === 40 || key === 38 || key === 9 || /[\w:-]/.test(char)) { - const parsedResult = mega.utils.emojiCodeParser(currentContent, currentCursorPos); - self.setState({ - 'emojiSearchQuery': parsedResult[0], - 'emojiStartPos': parsedResult[1], - 'emojiEndPos': parsedResult[2] - }); - return; - } - if (self.state.emojiSearchQuery) { - self.setState({ - 'emojiSearchQuery': false - }); - } + get isNear() { + return this.start - Date.now() < ChatRoom.SCHEDULED_MEETINGS_INTERVAL; + } + get iAmOwner() { + if (this.ownerHandle) { + return this.ownerHandle === u_handle; } + return null; } - onTypeAreaBlur(e) { - if (this.props.disabled) { - e.preventDefault(); - e.stopPropagation(); - return; - } - const self = this; - if (self.state.emojiSearchQuery) { - setTimeout(() => { - if (self.isMounted()) { - self.setState({ - 'emojiSearchQuery': false, - 'emojiStartPos': false, - 'emojiEndPos': false - }); - } - }, 300); - } + get parent() { + return this.isRoot ? null : this.megaChat.plugins.meetingsManager.getMeetingById(this.parentId); } - onTypeAreaChange(e, value) { - if (this.props.disabled) { - e.preventDefault(); - e.stopPropagation(); + setNextOccurrence() { + const upcomingOccurrences = Object.values(this.occurrences).filter(o => o.isUpcoming); + if (!upcomingOccurrences || !upcomingOccurrences.length) { + this.isCompleted = this.isRecurring; return; } - const self = this; - value = String(value || e.target.value || '').replace(/^\s+/, ''); - if (self.props.text !== value) { - self.props.onValueChanged(value); - self.forceUpdate(); - } - if (value.length) { - self.typing(); - } else { - self.stoppedTyping(); - } - if (this.props.persist) { - const { - chatRoom - } = this.props; - const { - megaChat - } = chatRoom; - const { - persistedTypeArea - } = megaChat.plugins; - if (persistedTypeArea) { - if (d > 2) { - this.logger.debug('%s persisted-typed value...', value.length ? 'Updating' : 'Removing'); - } - if (value.length) { - persistedTypeArea.updatePersistedTypedValue(chatRoom, value); - } else { - persistedTypeArea.removePersistedTypedValue(chatRoom); - } - } - } - self.updateScroll(); + const sortedOccurrences = upcomingOccurrences.sort((a, b) => a.start - b.start); + this.nextOccurrenceStart = sortedOccurrences[0].start; + this.nextOccurrenceEnd = sortedOccurrences[0].end; } - focusTypeArea() { - if (this.props.disabled) { - return; - } - if ($('.chat-textarea:visible textarea:visible', this.domRef.current).length > 0 && !$('.chat-textarea:visible textarea:visible:first', this.domRef.current).is(":focus")) { - moveCursortoToEnd($('.chat-textarea:visible:first textarea', this.domRef.current)[0]); + async getOccurrences(options) { + const { + from, + to, + count + } = options || {}; + const { + meetingsManager + } = this.megaChat.plugins; + const req = { + a: 'mcsmfo', + cid: this.chatId, + ...from && { + cf: Math.round(from / 1000) + }, + ...to && { + ct: Math.round(to / 1000) + }, + ...count && { + cc: count + } + }; + if (is_chatlink) { + req.ph = is_chatlink.ph; + delete req.cid; } - } - componentDidMount() { - super.componentDidMount(); - this._lastTextareaHeight = 20; - this.lastTypedMessage = this.props.initialText || this.lastTypedMessage; - chatGlobalEventManager.addEventListener('resize', `typingArea${this.getUniqueId()}`, () => this.handleWindowResize()); - this.triggerOnUpdate(true); - this.updateScroll(); - megaChat.rebind(`viewstateChange.gifpanel${this.getUniqueId()}`, e => { - const { - gifPanelActive - } = this.state; - const { - state - } = e.data; - if (state === 'active' && !gifPanelActive && this.gifResume) { - this.setState({ - gifPanelActive: true - }); - delete this.gifResume; - } else if (state !== 'active' && gifPanelActive && !this.gifResume) { - this.gifResume = true; - this.setState({ - gifPanelActive: false + const occurrences = await asyncApiReq(req); + if (Array.isArray(occurrences)) { + if (!options) { + this.occurrences.clear(); + } + for (let i = 0; i < occurrences.length; i++) { + const occurrence = new Occurrence(this.megaChat, { + scheduledMeeting: this, + ...occurrences[i] }); + this.occurrences.set(occurrence.uid, occurrence); } - }); + this.isCompleted = false; + this.setNextOccurrence(); + this.megaChat.trigger(meetingsManager.EVENTS.OCCURRENCES_UPDATE, this); + } + return this.occurrences; } - UNSAFE_componentWillMount() { - const { - chatRoom, - initialText, - persist, - onValueChanged - } = this.props; + getOccurrencesById(occurrenceId) { + const occurrences = Object.values(this.occurrences.toJS()).filter(o => o.id === occurrenceId); + return occurrences.length ? occurrences : false; + } + initializeFromActionPacket() { const { megaChat, - roomId - } = chatRoom; - const { - persistedTypeArea - } = megaChat.plugins; - if (persist && persistedTypeArea) { - if (!initialText) { - persistedTypeArea.getPersistedTypedValue(chatRoom).then(res => { - if (res && this.isMounted() && !this.props.text) { - onValueChanged(res); - } - }).catch(ex => { - if (this.logger && ex !== undefined) { - this.logger.warn(`Failed to retrieve persistedTypeArea for ${roomId}: ${ex}`, [ex]); + isUpcoming, + isCanceled, + isRecurring, + parent + } = this; + if (isUpcoming && isRecurring || parent) { + return parent ? (() => { + const occurrences = Object.values(parent.occurrences); + if (occurrences.length <= 20) { + return parent.getOccurrences().catch(nop); + } + occurrences.sort((a, b) => a.start - b.start); + const { + chatId, + start, + startInitial + } = this; + const currentIndex = occurrences.findIndex(o => o.uid === `${chatId}-${(startInitial || start) / 1000}`); + const previous = occurrences[currentIndex - 1]; + if (!previous) { + return parent.getOccurrences().catch(nop); + } + const movedBack = start <= previous.start; + let tmp = 0; + let newStart = movedBack ? Date.now() : previous.end; + const maxIdx = movedBack ? currentIndex + 1 : occurrences.length; + const startIdx = movedBack ? 0 : currentIndex; + for (let i = startIdx; i < maxIdx; i++) { + if (++tmp % 20 === 0) { + parent.getOccurrences({ + from: newStart, + to: occurrences[i].end, + count: 20 + }).catch(dump); + newStart = occurrences[i].end; + tmp = 0; } - }); - } - persistedTypeArea.addChangeListener(this.getUniqueId(), (e, k, v) => { - if (roomId === k) { - onValueChanged(v || ''); - this.triggerOnUpdate(true); + parent.occurrences.remove(occurrences[i].uid); } - }); + if (tmp) { + parent.getOccurrences({ + from: newStart, + count: tmp, + to: movedBack ? occurrences[currentIndex].end : occurrences[occurrences.length - 1].end + }).catch(dump); + } + })() : this.getOccurrences().catch(nop); } + megaChat.trigger(megaChat.plugins.meetingsManager.EVENTS[isCanceled ? 'CANCEL' : 'INITIALIZE'], this); } - componentWillUnmount() { - super.componentWillUnmount(); - const self = this; - self.triggerOnUpdate(); - if (megaChat.plugins.persistedTypeArea) { - megaChat.plugins.persistedTypeArea.removeChangeListener(self.getUniqueId()); + isSameAsOpts(opts) { + const { + timezone, + startDateTime, + endDateTime, + topic, + description, + f, + recurring + } = opts; + if (this.timezone !== timezone || this.start !== startDateTime || this.end !== endDateTime) { + return false; } - chatGlobalEventManager.removeEventListener('resize', `typingArea${ self.getUniqueId()}`); - megaChat.off(`viewstateChange.gifpanel${this.getUniqueId()}`); - } - componentDidUpdate() { - if (this.isComponentEventuallyVisible() && !window.getSelection().toString() && $('textarea:focus,select:focus,input:focus').filter(":visible").length === 0) { - this.focusTypeArea(); + if (this.title !== topic) { + return false; } - this.updateScroll(); - if (this.onUpdateCursorPosition) { - const el = $('.chat-textarea:visible:first textarea:visible', this.domRef.current)[0]; - el.selectionStart = el.selectionEnd = this.onUpdateCursorPosition; - this.onUpdateCursorPosition = false; + if (this.description !== description) { + return false; } - } - updateScroll() { - if (!this.isComponentEventuallyVisible() || !this.$node && !this.domRef && !this.domRef.current) { - return; + if (this.flags !== f) { + return false; } - const $node = this.$node = this.$node || this.domRef.current; - const $textarea = this.$textarea = this.$textarea || $('textarea:first', $node); - const $scrollBlock = this.$scrollBlock = this.$scrollBlock || $textarea.closest('.textarea-scroll'); - const $preview = $('.message-preview', $scrollBlock).safeHTML(`${escapeHTML(this.props.text).replace(/\n/g, '
')}
`); - const textareaHeight = $preview.height(); - $scrollBlock.height(Math.min(textareaHeight, this.getTextareaMaxHeight())); - if (textareaHeight !== this._lastTextareaHeight) { - this._lastTextareaHeight = textareaHeight; - this.setState({ - textareaHeight - }); - if (this.props.onResized) { - this.props.onResized(); + if (!!this.recurring ^ !!recurring) { + return false; + } + if (this.recurring) { + if (this.recurring.frequency !== recurring.frequency || this.recurring.interval !== (recurring.interval || 0)) { + return false; + } + if (this.recurring.end !== recurring.end) { + return false; + } + let diff = array.diff(this.recurring.weekDays, recurring.weekDays || []); + if (diff.removed.length + diff.added.length) { + return false; + } + diff = array.diff(this.recurring.monthDays, recurring.monthDays || []); + if (diff.removed.length + diff.added.length) { + return false; + } + if (Array.isArray(this.recurring.offset) && !Array.isArray(recurring.offset) || !Array.isArray(this.recurring.offset) && Array.isArray(recurring.offset)) { + return false; + } + if ((this.recurring.offset.value || 0) !== (recurring.offset.value || 0) || (this.recurring.offset.weekDay || 0) !== (recurring.offset.weekDay || 0)) { + return false; + } + } + return true; + } +} +class MeetingsManager { + constructor(megaChat) { + this.EVENTS = { + INITIALIZE: 'onMeetingInitialize', + EDIT: 'onMeetingEdit', + CANCEL: 'onMeetingCancel', + LEAVE: 'onMeetingLeave', + OCCURRENCES_UPDATE: 'onOccurrencesUpdate' + }; + this.startDayStrings = [l.schedule_occur_sun, l.schedule_occur_mon, l.schedule_occur_tue, l.schedule_occur_wed, l.schedule_occur_thu, l.schedule_occur_fri, l.schedule_occur_sat]; + this.midDayStrings = [l.schedule_occur_sun_mid, l.schedule_occur_mon_mid, l.schedule_occur_tue_mid, l.schedule_occur_wed_mid, l.schedule_occur_thu_mid, l.schedule_occur_fri_mid, l.schedule_occur_sat_mid]; + this.NOTIF_TITLES = { + recur: { + desc: { + update: l.schedule_notif_update_desc + }, + name: { + update: l.schedule_mgmt_title + }, + time: { + occur: l.schedule_mgmt_update_occur, + all: l.schedule_mgmt_update_recur + }, + convert: l.schedule_mgmt_update_convert_recur, + inv: l.schedule_notif_invite_recur, + multi: l.schedule_notif_update_multi, + cancel: { + occur: l.schedule_mgmt_cancel_occur, + all: l.schedule_mgmt_cancel_recur + } + }, + once: { + desc: { + update: l.schedule_notif_update_desc + }, + name: { + update: l.schedule_mgmt_title + }, + time: { + occur: '', + all: l.schedule_mgmt_update + }, + convert: l.schedule_mgmt_update_convert, + inv: l.schedule_notif_invite, + multi: l.schedule_notif_update_multi, + cancel: { + occur: '', + all: l.schedule_mgmt_cancel + } } - $textarea.height(textareaHeight); - } - if (this.textareaScroll) { - this.textareaScroll.reinitialise(); - } - } - getCursorPosition(el) { - let pos = 0; - if ('selectionStart' in el) { - pos = el.selectionStart; - } else if ('selection' in document) { - el.focus(); - const sel = document.selection.createRange(), - selLength = document.selection.createRange().text.length; - sel.moveStart('character', -el.value.length); - pos = sel.text.length - selLength; - } - return pos; - } - customIsEventuallyVisible() { - return this.props.chatRoom.isCurrentlyActive; - } - handleWindowResize(e) { - if (!this.isComponentEventuallyVisible()) { - return; - } - if (e) { - this.updateScroll(); - } - this.triggerOnUpdate(); - } - isActive() { - return document.hasFocus() && this.$messages && this.$messages.is(":visible"); - } - resetPrefillMode() { - this.prefillMode = false; - } - onCopyCapture() { - this.resetPrefillMode(); - } - onCutCapture() { - this.resetPrefillMode(); - } - onPasteCapture() { - this.resetPrefillMode(); - } - render() { - const self = this; - const room = this.props.chatRoom; - let buttons = null; - if (self.props.showButtons === true) { - buttons = [REaCt().createElement(ui_buttons.$, { - key: "save", - className: `${"mega-button right"} positive`, - label: l[776], - onClick: self.onSaveClicked.bind(self) - }), REaCt().createElement(ui_buttons.$, { - key: "cancel", - className: "mega-button right", - label: l.msg_dlg_cancel, - onClick: self.onCancelClicked.bind(self) - })]; - } - const textareaStyles = { - height: self.state.textareaHeight }; - const textareaScrollBlockStyles = {}; - const newHeight = Math.min(self.state.textareaHeight, self.getTextareaMaxHeight()); - if (newHeight > 0) { - textareaScrollBlockStyles.height = newHeight; - } - let emojiAutocomplete = null; - if (self.state.emojiSearchQuery) { - emojiAutocomplete = REaCt().createElement(EmojiAutocomplete, { - emojiSearchQuery: self.state.emojiSearchQuery, - emojiStartPos: self.state.emojiStartPos, - emojiEndPos: self.state.emojiEndPos, - typedMessage: self.props.text, - onPrefill (e, emojiAlias) { - if ($.isNumeric(self.state.emojiStartPos) && $.isNumeric(self.state.emojiEndPos)) { - const msg = self.props.text; - const pre = msg.substr(0, self.state.emojiStartPos); - let post = msg.substr(self.state.emojiEndPos + 1, msg.length); - const startPos = self.state.emojiStartPos; - const fwdPos = startPos + emojiAlias.length; - let endPos = fwdPos; - self.onUpdateCursorPosition = fwdPos; - self.prefillMode = true; - if (post.substr(0, 2) == "::" && emojiAlias.substr(-1) == ":") { - emojiAlias = emojiAlias.substr(0, emojiAlias.length - 1); - endPos -= 1; - } else { - post = post ? post.substr(0, 1) !== " " ? ` ${ post}` : post : " "; - self.onUpdateCursorPosition++; - } - self.setState({ - 'emojiEndPos': endPos - }); - self.props.onValueChanged(pre + emojiAlias + post); + this.OCCUR_STRINGS = { + recur: { + daily: { + continuous: { + occur: l.schedule_recur_time_daily_cont, + skip: l.scheduled_recur_time_daily_skip_cont + }, + limited: { + occur: l.schedule_recur_time_daily, + skip: l.scheduled_recur_time_daily_skip } }, - onSelect (e, emojiAlias, forceSend) { - if ($.isNumeric(self.state.emojiStartPos) && $.isNumeric(self.state.emojiEndPos)) { - const msg = self.props.text; - const pre = msg.substr(0, self.state.emojiStartPos); - let post = msg.substr(self.state.emojiEndPos + 1, msg.length); - if (post.substr(0, 2) == "::" && emojiAlias.substr(-1) == ":") { - emojiAlias = emojiAlias.substr(0, emojiAlias.length - 1); - } else { - post = post ? post.substr(0, 1) !== " " ? ` ${ post}` : post : " "; - } - const val = pre + emojiAlias + post; - self.prefillMode = false; - self.setState({ - 'emojiSearchQuery': false, - 'emojiStartPos': false, - 'emojiEndPos': false - }); - self.props.onValueChanged(val); - if (forceSend) { - if (self.onConfirmTrigger($.trim(val)) !== true) { - self.props.onValueChanged(''); - } - } + weekly: { + continuous: { + list: l.schedule_recur_time_week_cont_list, + spec: l.schedule_recur_time_week_cont + }, + limited: { + list: l.schedule_recur_time_week_list, + spec: l.schedule_recur_time_week } }, - onCancel () { - self.prefillMode = false; - self.setState({ - 'emojiSearchQuery': false, - 'emojiStartPos': false, - 'emojiEndPos': false - }); + monthly: { + continuous: { + num: l.schedule_recur_time_num_day_month_cont, + pos: [[l.schedule_recur_time_first_day_month_6_cont, l.schedule_recur_time_first_day_month_0_cont, l.schedule_recur_time_first_day_month_1_cont, l.schedule_recur_time_first_day_month_2_cont, l.schedule_recur_time_first_day_month_3_cont, l.schedule_recur_time_first_day_month_4_cont, l.schedule_recur_time_first_day_month_5_cont], [l.schedule_recur_time_second_day_month_6_cont, l.schedule_recur_time_second_day_month_0_cont, l.schedule_recur_time_second_day_month_1_cont, l.schedule_recur_time_second_day_month_2_cont, l.schedule_recur_time_second_day_month_3_cont, l.schedule_recur_time_second_day_month_4_cont, l.schedule_recur_time_second_day_month_5_cont], [l.schedule_recur_time_third_day_month_6_cont, l.schedule_recur_time_third_day_month_0_cont, l.schedule_recur_time_third_day_month_1_cont, l.schedule_recur_time_third_day_month_2_cont, l.schedule_recur_time_third_day_month_3_cont, l.schedule_recur_time_third_day_month_4_cont, l.schedule_recur_time_third_day_month_5_cont], [l.schedule_recur_time_fourth_day_month_6_cont, l.schedule_recur_time_fourth_day_month_0_cont, l.schedule_recur_time_fourth_day_month_1_cont, l.schedule_recur_time_fourth_day_month_2_cont, l.schedule_recur_time_fourth_day_month_3_cont, l.schedule_recur_time_fourth_day_month_4_cont, l.schedule_recur_time_fourth_day_month_5_cont], [l.schedule_recur_time_fifth_day_month_6_cont, l.schedule_recur_time_fifth_day_month_0_cont, l.schedule_recur_time_fifth_day_month_1_cont, l.schedule_recur_time_fifth_day_month_2_cont, l.schedule_recur_time_fifth_day_month_3_cont, l.schedule_recur_time_fifth_day_month_4_cont, l.schedule_recur_time_fifth_day_month_5_cont]], + last: [l.schedule_recur_time_fifth_day_month_6_cont, l.schedule_recur_time_fifth_day_month_0_cont, l.schedule_recur_time_fifth_day_month_1_cont, l.schedule_recur_time_fifth_day_month_2_cont, l.schedule_recur_time_fifth_day_month_3_cont, l.schedule_recur_time_fifth_day_month_4_cont, l.schedule_recur_time_fifth_day_month_5_cont] + }, + limited: { + num: l.schedule_recur_time_num_day_month, + pos: [[l.schedule_recur_time_first_day_month_6, l.schedule_recur_time_first_day_month_0, l.schedule_recur_time_first_day_month_1, l.schedule_recur_time_first_day_month_2, l.schedule_recur_time_first_day_month_3, l.schedule_recur_time_first_day_month_4, l.schedule_recur_time_first_day_month_5], [l.schedule_recur_time_second_day_month_6, l.schedule_recur_time_second_day_month_0, l.schedule_recur_time_second_day_month_1, l.schedule_recur_time_second_day_month_2, l.schedule_recur_time_second_day_month_3, l.schedule_recur_time_second_day_month_4, l.schedule_recur_time_second_day_month_5], [l.schedule_recur_time_third_day_month_6, l.schedule_recur_time_third_day_month_0, l.schedule_recur_time_third_day_month_1, l.schedule_recur_time_third_day_month_2, l.schedule_recur_time_third_day_month_3, l.schedule_recur_time_third_day_month_4, l.schedule_recur_time_third_day_month_5], [l.schedule_recur_time_fourth_day_month_6, l.schedule_recur_time_fourth_day_month_0, l.schedule_recur_time_fourth_day_month_1, l.schedule_recur_time_fourth_day_month_2, l.schedule_recur_time_fourth_day_month_3, l.schedule_recur_time_fourth_day_month_4, l.schedule_recur_time_fourth_day_month_5], [l.schedule_recur_time_fifth_day_month_6, l.schedule_recur_time_fifth_day_month_0, l.schedule_recur_time_fifth_day_month_1, l.schedule_recur_time_fifth_day_month_2, l.schedule_recur_time_fifth_day_month_3, l.schedule_recur_time_fifth_day_month_4, l.schedule_recur_time_fifth_day_month_5]], + last: [l.schedule_recur_time_last_day_month_6, l.schedule_recur_time_last_day_month_0, l.schedule_recur_time_last_day_month_1, l.schedule_recur_time_last_day_month_2, l.schedule_recur_time_last_day_month_3, l.schedule_recur_time_last_day_month_4, l.schedule_recur_time_last_day_month_5] + } + }, + [scheduleMetaChange.A.MODE.CANCELLED]: { + occur: l.schedule_occurrence_time, + all: '' } - }); - } - const disabledTextarea = !!(room.pubCu25519KeyIsMissing === true || this.props.disabled); - return REaCt().createElement("div", { - ref: this.domRef, - className: ` - typingarea-component - ${this.props.className} - ` - }, this.state.gifPanelActive && REaCt().createElement(gifPanel.Ay, { - chatRoom: this.props.chatRoom, - onToggle: () => { - this.setState({ - gifPanelActive: false - }); - delete this.gifResume; - } - }), REaCt().createElement("div", { - className: ` - chat-textarea - ${this.props.className} - ` - }, emojiAutocomplete, self.props.children, self.props.editing ? null : REaCt().createElement(ui_buttons.$, { - className: ` - popup-button - gif-button - ${this.state.gifPanelActive ? 'active' : ''} - `, - icon: "small-icon gif", - disabled: this.props.disabled, - onClick: () => this.setState(state => { - delete this.gifResume; - return { - gifPanelActive: !state.gifPanelActive - }; - }) - }), REaCt().createElement(ui_buttons.$, { - className: "popup-button emoji-button", - icon: "sprite-fm-theme icon-emoji", - iconHovered: "sprite-fm-theme icon-emoji-active", - disabled: this.props.disabled - }, REaCt().createElement(emojiDropdown.L, { - className: "popup emoji", - vertOffset: 17, - onClick: this.onEmojiClicked - })), REaCt().createElement("hr", null), REaCt().createElement(perfectScrollbar.O, { - chatRoom: self.props.chatRoom, - className: "chat-textarea-scroll textarea-scroll", - options: { - 'suppressScrollX': true }, - style: textareaScrollBlockStyles, - ref: ref => { - self.textareaScroll = ref; - } - }, REaCt().createElement("div", { - className: "messages-textarea-placeholder" - }, self.props.text ? null : REaCt().createElement(utils.zT, null, (l[18763] || `Write message to \u201c%s\u201d\u2026`).replace('%s', room.getRoomTitle()))), REaCt().createElement("textarea", { - className: ` - ${"messages-textarea"} - ${disabledTextarea ? 'disabled' : ''} - `, - onKeyUp: this.onTypeAreaKeyUp, - onKeyDown: this.onTypeAreaKeyDown, - onBlur: this.onTypeAreaBlur, - onChange: this.onTypeAreaChange, - onCopyCapture: this.onCopyCapture, - onPasteCapture: this.onPasteCapture, - onCutCapture: this.onCutCapture, - value: self.props.text, - style: textareaStyles, - disabled: disabledTextarea, - readOnly: disabledTextarea - }), REaCt().createElement("div", { - className: "message-preview" - }))), buttons); - } -}, (0,applyDecoratedDescriptor.A)(_class.prototype, "handleWindowResize", [_dec], Object.getOwnPropertyDescriptor(_class.prototype, "handleWindowResize"), _class.prototype), _class); - -}, - -501 -(_, EXP_, REQ_) { - -"use strict"; -REQ_.d(EXP_, { -Y: () => withUpdateObserver -}); -const _extends0__ = REQ_(168); -const react1__ = REQ_(594); -const react1 = REQ_.n(react1__); -const _mixins_js2__ = REQ_(137); - - - -const withUpdateObserver = Component => class extends _mixins_js2__.w9 { - constructor(...args) { - super(...args); - this.updateInterval = 600000; - this.instanceRef = react1().createRef(); - this.intervalRef = undefined; - this.state = { - updated: 0 - }; - this.updateListener = () => { - return this.isComponentVisible() && document.visibilityState === 'visible' && this.setState(state => ({ - updated: ++state.updated - }), () => this.safeForceUpdate()); - }; - } - componentWillUnmount() { - super.componentWillUnmount(); - document.removeEventListener('visibilitychange', this.updateListener); - clearInterval(this.intervalRef); - } - componentDidMount() { - super.componentDidMount(); - document.addEventListener('visibilitychange', this.updateListener); - this.intervalRef = setInterval(this.instanceRef.current[Component.updateListener] || this.updateListener, Component.updateInterval || this.updateInterval); - } - render() { - return react1().createElement(Component, (0,_extends0__.A)({ - ref: this.instanceRef - }, this.state, this.props)); - } -}; - -}, - -994 -(_, EXP_, REQ_) { - -"use strict"; -REQ_.d(EXP_, { -$: () => Button -}); -const _extends0__ = REQ_(168); -const react1__ = REQ_(594); -const react1 = REQ_.n(react1__); -const _chat_mixins_js2__ = REQ_(137); - - - -const BLURRABLE_CLASSES = '.conversationsApp, .join-meeting, .main-blur-block'; -class Button extends _chat_mixins_js2__.w9 { - constructor(props) { - super(props); - this.domRef = react1().createRef(); - this.buttonClass = `.button`; - this.state = { - focused: false, - hovered: false, - iconHovered: '' - }; - this.onBlur = e => { - let _this$domRef; - if (!this.isMounted()) { - return; - } - if (!e || !$(e.target).closest(this.buttonClass).is((_this$domRef = this.domRef) == null ? void 0 : _this$domRef.current)) { - this.setState({ - focused: false - }, () => { - this.unbindEvents(); - this.safeForceUpdate(); - }); + once: { + [scheduleMetaChange.A.MODE.CREATED]: { + occur: l.schedule_occurrence_time + }, + [scheduleMetaChange.A.MODE.EDITED]: { + occur: l.schedule_occurrence_time_recur + }, + [scheduleMetaChange.A.MODE.CANCELLED]: { + occur: '' + } } }; - this.onClick = e => { - let _this$domRef2; - if (this.props.disabled === true) { - e.preventDefault(); - e.stopPropagation(); - return; - } - if ($(e.target).closest('.popup').closest(this.buttonClass).is((_this$domRef2 = this.domRef) == null ? void 0 : _this$domRef2.current) && this.state.focused === true) { - e.preventDefault(); - e.stopPropagation(); - return; - } - if ($(e.target).is('input, textarea, select')) { + this.megaChat = megaChat; + this.scheduledMeetings = megaChat.scheduledMeetings || new MegaDataMap(); + this._goneOccurrences = {}; + this.megaChat.rebind(this.EVENTS.CANCEL, ({ + data + }) => this.archiveMeeting(data)); + this.megaChat.rebind(this.EVENTS.LEAVE, ({ + data + }) => this.detachMeeting(data)); + this.megaChat.rebind(`${this.EVENTS.OCCURRENCES_UPDATE}.tracker`, ({ + data + }) => { + if (!this._goneOccurrences[data.chatId]) { return; } - if (this.state.focused === false) { - if (this.props.onClick) { - this.props.onClick(this, e); - } else if (react1().Children.count(this.props.children) > 0) { - this.setState({ - focused: true - }, () => this.safeForceUpdate()); + const { + chatId + } = data; + for (const scheduledId of Object.keys(this._goneOccurrences[chatId])) { + if (this._goneOccurrences[chatId][scheduledId] === -1) { + this._goneOccurrences[chatId][scheduledId] = this.scheduledMeetings[scheduledId] ? 0 : 1; } - } else if (this.state.focused === true) { - this.setState({ - focused: false - }); - this.unbindEvents(); - } - }; - this.state.iconHovered = this.props.iconHovered || ''; - } - UNSAFE_componentWillUpdate(nextProps, nextState) { - if (nextProps.disabled === true && nextState.focused === true) { - nextState.focused = false; - } - if (this.state.focused !== nextState.focused && nextState.focused === true) { - this.bindEvents(); - if (this._pageChangeListener) { - mBroadcaster.removeListener(this._pageChangeListener); } - this._pageChangeListener = mBroadcaster.addListener('pagechange', () => { - if (this.state.focused === true) { - this.onBlur(); - } - }); - } - } - componentWillUnmount() { - super.componentWillUnmount(); - this.unbindEvents(); + }); } - renderChildren() { - return this.props.children && react1().Children.map(this.props.children, child => child && (typeof child.type === 'string' || child.type === undefined ? child : react1().cloneElement(child, { - active: this.state.focused, - closeDropdown: () => this.setState({ - focused: false - }, () => this.unbindEvents()), - onActiveChange: active => { - let _this$domRef3; - const $element = $(((_this$domRef3 = this.domRef) == null ? void 0 : _this$domRef3.current) || this.domNode); - const $scrollables = $element.parents('.ps'); - if ($scrollables.length > 0) { - $scrollables.map((k, element) => Ps[active ? 'disable' : 'enable'](element)); + checkForNotifications() { + const time = Date.now(); + const upcomingMeetings = Object.values(this.scheduledMeetings.toJS()).filter(c => c.isUpcoming); + for (const meeting of upcomingMeetings) { + if (pushNotificationSettings.isAllowedForChatId(meeting.chatId)) { + if (meeting.nextOccurrenceStart >= time + 9e5 && meeting.nextOccurrenceStart <= time + 96e4) { + const ss = Math.floor(meeting.nextOccurrenceStart / 1000); + const ns = Math.floor(time / 1000) + 900; + if (ss - ns <= 10) { + this.megaChat.trigger('onScheduleUpcoming', meeting); + } else { + tSleep(ss - ns).always(() => { + this.megaChat.trigger('onScheduleUpcoming', meeting); + }); + } + } else if (meeting.nextOccurrenceStart >= time && meeting.nextOccurrenceStart < time + 6e4) { + const ss = Math.floor(meeting.nextOccurrenceStart / 1000); + const ns = Math.floor(time / 1000); + tSleep(ss - ns).always(() => { + this.megaChat.trigger('onScheduleStarting', meeting); + }); } - child.props.onActiveChange == null || child.props.onActiveChange(active); - return this[active ? 'bindEvents' : 'unbindEvents'](); } - }))); + } } - bindEvents() { - $(BLURRABLE_CLASSES).rebind(`mousedown.button--${this.getUniqueId()}`, this.onBlur); - $(document).rebind(`keyup.button--${this.getUniqueId()}`, ev => this.state.focused === true && ev.keyCode === 27 && this.onBlur()); - $(document).rebind(`closeDropdowns.${this.getUniqueId()}`, this.onBlur); + encodeData(data) { + return data && base64urlencode(to8(data)); } - unbindEvents() { - $(BLURRABLE_CLASSES).unbind(`mousedown.button--${this.getUniqueId()}`); - $(document).off(`keyup.button--${this.getUniqueId()}`); - $(document).off(`closeDropdowns.${this.getUniqueId()}`); - mBroadcaster.removeListener(this._pageChangeListener); + decodeData(data) { + return data && from8(base64urldecode(data)); } - render() { + getMeetingById(meetingId) { + return this.scheduledMeetings[meetingId]; + } + getMeetingOrOccurrenceParent(meetingId) { + const meeting = this.scheduledMeetings[meetingId]; + if (!meeting) { + return false; + } + if (meeting.parentId) { + return this.getMeetingOrOccurrenceParent(meeting.parentId); + } + return meeting; + } + getRoomByMeetingId() {} + async createMeeting(meetingInfo) { + await this.megaChat.createAndShowGroupRoomFor(meetingInfo.participants, meetingInfo.topic, { + keyRotation: false, + createChatLink: meetingInfo.link, + isMeeting: true, + openInvite: meetingInfo.openInvite, + waitingRoom: meetingInfo.waitingRoom, + scheduledMeeting: { + a: 'mcsmp', + s: meetingInfo.startDateTime / 1000, + e: meetingInfo.endDateTime / 1000, + tz: this.encodeData(meetingInfo.timezone), + t: this.encodeData(meetingInfo.topic), + d: this.encodeData(meetingInfo.description), + f: meetingInfo.sendInvite ? 0x01 : 0x00, + ...meetingInfo.recurring && { + r: { + f: meetingInfo.recurring.frequency, + wd: meetingInfo.recurring.weekDays, + md: meetingInfo.recurring.monthDays, + mwd: meetingInfo.recurring.offset, + ...meetingInfo.recurring.end && { + u: meetingInfo.recurring.end / 1000 + }, + ...meetingInfo.recurring.interval && { + i: meetingInfo.recurring.interval + } + } + } + } + }); + } + async updateMeeting(meetingInfo, chatRoom) { const { - className, - disabled, - style, - icon, - iconHovered, - label, - attrs, - toggle, - secondLabel, - secondLabelClass - } = this.props; - const isMegaButton = className && className.indexOf('mega-button') > -1; - const TagName = isMegaButton ? 'button' : 'div'; - return react1().createElement(TagName, (0,_extends0__.A)({ - ref: this.domRef, - className: ` - button - ${className || ''} - ${disabled ? 'disabled' : ''} - ${this.state.focused ? 'active active-dropdown' : ''} - `, - style, - onClick: this.onClick, - onMouseEnter: () => iconHovered && this.setState({ - hovered: true - }), - onMouseLeave: () => iconHovered && this.setState({ - hovered: false - }) - }, attrs), icon && !isMegaButton && react1().createElement("div", null, react1().createElement("i", { - className: this.state.hovered ? this.state.iconHovered : icon - })), icon && isMegaButton && react1().createElement("div", null, react1().createElement("i", { - className: this.state.hovered ? this.state.iconHovered : icon - })), label && react1().createElement("span", null, label), secondLabel && react1().createElement("span", { - className: secondLabelClass ? secondLabelClass : '' - }, secondLabel), toggle && react1().createElement("div", { - className: ` - mega-switch - ${toggle.className ? toggle.className : ''} - ${toggle.enabled ? 'toggle-on' : ''} - `, - role: "switch", - "aria-checked": !!toggle.enabled, - onClick: ev => { - ev.stopPropagation(); - if (this.props.toggle.onClick) { - this.props.toggle.onClick(); + scheduledMeeting, + chatId, + publicLink, + options + } = chatRoom; + await megaChat.plugins.chatdIntegration.updateScheduledMeeting(meetingInfo, scheduledMeeting.id, chatId); + const nextParticipants = meetingInfo.participants; + const prevParticipants = chatRoom.getParticipantsExceptMe(); + const participantsDiff = JSON.stringify(nextParticipants) !== JSON.stringify(prevParticipants); + if (participantsDiff) { + const removed = prevParticipants.filter(h => !nextParticipants.includes(h)); + const added = nextParticipants.filter(h => !prevParticipants.includes(h)); + if (removed.length) { + for (let i = removed.length; i--;) { + chatRoom.trigger('onRemoveUserRequest', [removed[i]]); } } - }, react1().createElement("div", { - className: `mega-feature-switch sprite-fm-mono-after - ${toggle.enabled ? 'icon-check-after' : 'icon-minimise-after'}` - })), this.renderChildren()); + if (added.length) { + chatRoom.trigger('onAddUserRequest', [added]); + } + } + if (!!meetingInfo.link !== !!publicLink) { + chatRoom.updatePublicHandle(!meetingInfo.link, meetingInfo.link); + } + if (meetingInfo.waitingRoom !== options[chat_chatRoom.U_.WAITING_ROOM]) { + chatRoom.toggleWaitingRoom(); + } + if (meetingInfo.openInvite !== options[chat_chatRoom.U_.OPEN_INVITE]) { + chatRoom.toggleOpenInvite(); + } } -} - -}, - -911 -(_, EXP_, REQ_) { - -"use strict"; -REQ_.r(EXP_); -REQ_.d(EXP_, { -Dropdown: () => Dropdown, -DropdownContactsSelector: () => DropdownContactsSelector, -DropdownItem: () => DropdownItem -}); -const _utils_jsx0__ = REQ_(314); -const _chat_mixins1__ = REQ_(137); -const _chat_ui_contacts_jsx2__ = REQ_(251); -const React = REQ_(594); - - - -class Dropdown extends _chat_mixins1__.w9 { - constructor(props) { - super(props); - this.domRef = React.createRef(); - this.onActiveChange = this.onActiveChange.bind(this); - this.onResized = this.onResized.bind(this); + cancelMeeting(scheduledMeeting, chatId) { + return this.megaChat.plugins.chatdIntegration.cancelScheduledMeeting(scheduledMeeting, chatId); } - UNSAFE_componentWillUpdate(nextProps) { - if (this.props.active != nextProps.active) { - this.onActiveChange(nextProps.active); + deleteMeeting(scheduledMeetingId, chatId) { + return this.megaChat.plugins.chatdIntegration.deleteScheduledMeeting(scheduledMeetingId, chatId); + } + attachMeeting(meetingInfo, fromActionPacket) { + const chatRoom = meetingInfo.chatRoom || this.megaChat.getChatById(meetingInfo.cid); + if (chatRoom) { + const scheduledMeeting = new ScheduledMeeting(this.megaChat, { + chatRoom, + ...meetingInfo + }, fromActionPacket); + this.scheduledMeetings.set(meetingInfo.id, scheduledMeeting); + return scheduledMeeting; } } - specShouldComponentUpdate(nextProps, nextState) { - if (this.props.active != nextProps.active) { - if (this.props.onBeforeActiveChange) { - this.props.onBeforeActiveChange(nextProps.active); + detachMeeting(scheduledMeeting) { + if (scheduledMeeting) { + this.archiveMeeting(scheduledMeeting); + scheduledMeeting.chatRoom.scheduledMeeting = null; + this.scheduledMeetings.remove(scheduledMeeting.id); + if (fmdb) { + fmdb.del('mcsm', scheduledMeeting.id); } - return true; - } else if (this.props.focused != nextProps.focused) { - return true; - } else if (this.state && this.state.active != nextState.active) { - return true; } - return undefined; } - onActiveChange(newVal) { - if (this.props.onActiveChange) { - this.props.onActiveChange(newVal); - } + archiveMeeting(scheduledMeeting) { + const { + chatRoom + } = scheduledMeeting; + tSleep(2).then(() => chatRoom.hasMessages(true) ? null : chatRoom.archive()); } - reposElementUsing(element, obj, info) { - let $element; - if (this.popupElement) { - $element = $(this.popupElement); + filterUpcomingMeetings(conversations) { + const upcomingMeetings = Object.values(conversations || {}).filter(c => { + return c.isDisplayable() && c.isMeeting && c.scheduledMeeting && c.scheduledMeeting.isUpcoming && c.iAmInRoom() && !c.havePendingCall(); + }).sort((a, b) => a.scheduledMeeting.nextOccurrenceStart - b.scheduledMeeting.nextOccurrenceStart || a.ctime - b.ctime); + const nextOccurrences = upcomingMeetings.reduce((nextOccurrences, chatRoom) => { + const { + nextOccurrenceStart + } = chatRoom.scheduledMeeting; + if ((0,helpers.cK)(nextOccurrenceStart)) { + nextOccurrences.today.push(chatRoom); + } else if ((0,helpers.ef)(nextOccurrenceStart)) { + nextOccurrences.tomorrow.push(chatRoom); + } else { + const date = time2date(nextOccurrenceStart / 1000, 19); + if (!nextOccurrences.rest[date]) { + nextOccurrences.rest[date] = []; + } + nextOccurrences.rest[date].push(chatRoom); + } + return nextOccurrences; + }, { + today: [], + tomorrow: [], + rest: {} + }); + return { + upcomingMeetings, + nextOccurrences + }; + } + getOccurrenceStrings(meta) { + const res = []; + const { + prevTiming, + timeRules, + mode, + occurrence, + recurring, + converted + } = meta; + const { + MODE + } = scheduleMetaChange.A; + if (!mode) { + return res; + } + const { + OCCUR_STRINGS + } = this; + let string; + if (recurring) { + res.push(this._parseOccurrence(timeRules, mode, occurrence)); + if (prevTiming && !(occurrence && mode === MODE.CANCELLED)) { + res.push(this._parseOccurrence(prevTiming, mode, occurrence)); + } } else { - return; - } - const self = this; - let vertOffset = 0; - let horizOffset = 0; - if (!self.props.noArrow) { - const $arrow = $('.dropdown-white-arrow', $element); - let arrowHeight; - if (self.props.arrowHeight) { - arrowHeight = self.props.arrowHeight; - if (info.vertical === "top") { - arrowHeight = 0; + const { + startTime, + endTime + } = timeRules; + string = OCCUR_STRINGS.once[mode].occur; + res.push(string.replace('%1', toLocaleTime(startTime)).replace('%2', toLocaleTime(endTime)).replace('%6', time2date(startTime, 20)).replace('%s', time2date(startTime, 11))); + if (prevTiming) { + const { + startTime: pStartTime, + endTime: pEndTime + } = prevTiming; + if (converted) { + res.push(this._parseOccurrence(prevTiming, mode, occurrence)); } else { - arrowHeight *= -1; + res.push(string.replace('%1', toLocaleTime(pStartTime)).replace('%2', toLocaleTime(pEndTime)).replace('%6', time2date(pStartTime, 20)).replace('%s', time2date(pStartTime, 11))); } - } else { - arrowHeight = $arrow.outerHeight(); } - if (info.vertical === "top") { - $(element).removeClass("down-arrow").addClass("up-arrow"); + } + return res; + } + _parseOccurrence(timeRules, mode, occurrence) { + const { + startTime, + endTime, + days, + dayInt, + interval, + month, + recurEnd, + skipDay + } = timeRules; + const { + recur, + once + } = this.OCCUR_STRINGS; + const occurrenceEnd = recurEnd ? 'limited' : 'continuous'; + let string = ''; + if (recur[mode]) { + return occurrence ? recur[mode].occur.replace('%1', toLocaleTime(startTime)).replace('%2', toLocaleTime(endTime)).replace('%s', time2date(startTime, 11)) : recur[mode].all; + } else if (month) { + const { + count, + occur + } = month; + string = count < 0 ? mega.icu.format(recur.monthly[occurrenceEnd].last[occur], interval) : mega.icu.format(recur.monthly[occurrenceEnd].pos[count][occur], interval); + return string.replace('%1', toLocaleTime(startTime)).replace('%2', toLocaleTime(endTime)).replace('%3', time2date(startTime, 2)).replace('%4', time2date(recurEnd, 2)); + } else if (days) { + if (days.length > 1) { + if (days.length === 7) { + return recur.daily[occurrenceEnd].occur.replace('%1', toLocaleTime(startTime)).replace('%2', toLocaleTime(endTime)).replace('%3', time2date(startTime, 2)).replace('%4', time2date(recurEnd, 2)); + } + const weekDays = days.map((day, idx) => { + if (idx) { + return this.midDayStrings[day]; + } + return this.startDayStrings[day]; + }); + string = mega.icu.format(recur.weekly[occurrenceEnd].list, interval); + string = mega.utils.trans.listToString(weekDays, string); } else { - $(element).removeClass("up-arrow").addClass("down-arrow"); + string = mega.icu.format(recur.weekly[occurrenceEnd].spec, interval).replace('%s', this.startDayStrings[days[0]]); } - vertOffset += info.vertical === "top" ? arrowHeight : 0; - } - if (self.props.vertOffset) { - vertOffset += self.props.vertOffset * (info.vertical === "top" ? 1 : -1); - } - if (self.props.horizOffset) { - horizOffset += self.props.horizOffset; - } - $(element).css({ - left: `${obj.left + 0 + horizOffset }px`, - top: `${obj.top + vertOffset }px` - }); - if (this.props.positionLeft) { - $(element).css({ - left: this.props.positionLeft - }); + return string.replace('%1', toLocaleTime(startTime)).replace('%2', toLocaleTime(endTime)).replace('%3', time2date(startTime, 2)).replace('%4', time2date(recurEnd, 2)); + } else if (dayInt) { + string = mega.icu.format(recur.monthly[occurrenceEnd].num, interval); + return string.replace('%1', toLocaleTime(startTime)).replace('%2', toLocaleTime(endTime)).replace('%3', time2date(startTime, 2)).replace('%4', time2date(recurEnd, 2)).replace('%5', dayInt); + } else if (skipDay) { + string = mega.icu.format(recur.daily[occurrenceEnd].skip, interval); + return string.replace('%1', toLocaleTime(startTime)).replace('%2', toLocaleTime(endTime)).replace('%3', time2date(startTime, 2)).replace('%4', time2date(recurEnd, 2)); } + string = once[mode].occur; + return string.replace('%1', toLocaleTime(startTime)).replace('%2', toLocaleTime(endTime)).replace('%6', time2date(startTime, 20)).replace('%s', time2date(startTime, 11)); } - onResized() { - const self = this; - if (this.props.active === true && this.popupElement) { - const $element = $(this.popupElement); - const $positionToElement = $('.button.active-dropdown:visible'); - if ($positionToElement.length === 0) { - return; + getFormattingMeta(scheduledId, data, chatRoom) { + const { + MODE + } = scheduleMetaChange.A; + const meta = { + userId: data.sender || false, + timeRules: {}, + mode: MODE.EDITED, + handle: scheduledId, + cid: chatRoom.chatId + }; + const changeSet = data.schedChange || data.cs || false; + if (changeSet) { + const { + s, + e, + c, + r, + t, + d: desc + } = changeSet; + let onlyTitle = typeof t !== 'undefined'; + if (Array.isArray(c) && c[1]) { + meta.mode = MODE.CANCELLED; } - let $container = $positionToElement.closest('.messages.scroll-area'); - if ($container.length === 0) { - $container = $(document.body); + if (Array.isArray(s)) { + meta.prevTiming = { + startTime: s[0] + }; + meta.timeRules.startTime = s[1] || s[0]; } - $element.css('margin-left', ''); - $element.position({ - of: $positionToElement, - my: self.props.positionMy ? self.props.positionMy : "center top", - at: self.props.positionAt ? self.props.positionAt : "center bottom", - collision: this.props.collision || 'flipfit', - within: self.props.wrapper || $container, - using (obj, info) { - self.reposElementUsing(this, obj, info); + const meeting = this.getMeetingOrOccurrenceParent(scheduledId); + if (Array.isArray(e)) { + if (!meta.prevTiming) { + meta.prevTiming = { + startTime: meeting ? Math.floor(meeting.start / 1000) : 0 + }; + meta.timeRules.startTime = meta.prevTiming.startTime; } - }); - } - } - componentDidMount() { - super.componentDidMount(); - chatGlobalEventManager.addEventListener('resize', `drpdwn${ this.getUniqueId()}`, this.onResized.bind(this)); - this.onResized(); - const self = this; - $(document.body).rebind(`closeAllDropdownsExcept.drpdwn${ this.getUniqueId()}`, (e, target) => { - if (self.props.active && target !== self) { - if (self.props && self.props.closeDropdown) { - self.props.closeDropdown(); + meta.prevTiming.endTime = e[0]; + meta.timeRules.endTime = e[1] || e[0]; + onlyTitle = false; + } + if (desc) { + meta.description = true; + onlyTitle = false; + } + if (Array.isArray(r)) { + const parseR = r => r ? typeof r === 'string' ? JSON.parse(r) : r : false; + const prev = parseR(r[0]); + const next = parseR(r[1]); + if (r.length === 1) { + meta.converted = false; + meta.timeRules = this._recurringTimings(prev, meta.timeRules || {}); + meta.prevTiming = this._recurringTimings(prev, meta.prevTiming); + meta.recurring = r[0] !== ''; + } else { + meta.converted = !!(!!prev ^ !!next); + if (prev) { + meta.prevTiming = this._recurringTimings(prev, meta.prevTiming || {}); + } + meta.timeRules = this._recurringTimings(next, meta.timeRules || {}); + meta.recurring = next !== false; } + onlyTitle = false; } - }); - } - componentDidUpdate() { - this.onResized(); - } - componentWillUnmount() { - super.componentWillUnmount(); - $(document.body).unbind(`closeAllDropdownsExcept.drpdwn${ this.getUniqueId()}`); - if (this.props.active) { - this.onActiveChange(false); - } - chatGlobalEventManager.removeEventListener('resize', `drpdwn${ this.getUniqueId()}`); - } - doRerender() { - const self = this; - setTimeout(() => { - self.safeForceUpdate(); - }, 100); - setTimeout(() => { - self.onResized(); - }, 200); - } - renderChildren() { - const self = this; - return React.Children.map(this.props.children, (child) => { - if (child) { - let activeVal = self.props.active || self.state.active; - activeVal = String(activeVal); - return React.cloneElement(child, { - active: activeVal - }); + if (!meeting || meeting.id !== scheduledId) { + meta.occurrence = true; + meta.recurring = true; } - return null; - }); + if (Array.isArray(t)) { + meta.topicChange = true; + meta.onlyTitle = onlyTitle; + meta.topic = this.decodeData(t[1]); + meta.oldTopic = this.decodeData(t[0]); + } + return meta; + } + return this.noCsMeta(scheduledId, data, chatRoom); } - render() { - if (this.props.active !== true) { - return null; + _recurringTimings(meta, obj) { + if (!meta) { + return obj; + } + obj.recurEnd = meta.u || false; + obj.interval = meta.i || 1; + if (meta.wd) { + obj.days = meta.wd.sort((a, b) => a - b).map(wd => wd === 7 ? 0 : wd); + } + if (meta.md) { + obj.dayInt = meta.md[0]; + } + if (meta.mwd) { + obj.month = meta.mwd.map(oc => { + return { + count: (oc[0] || 1) - 1, + occur: oc[1] ? oc[1] === 7 ? 0 : oc[1] : 1 + }; + })[0]; } - const self = this; - let child = null; - if (this.props.children) { - child = React.createElement("div", { - ref: this.domRef - }, self.renderChildren()); - } else if (this.props.dropdownItemGenerator) { - child = this.props.dropdownItemGenerator(this); + if (meta.f === 'd' && meta.i > 1) { + obj.skipDay = true; + } else if (meta.f === 'd' && meta.i === 1) { + obj.days = [1, 2, 3, 4, 5, 6, 0]; } - if (!child && !this.props.forceShowWhenEmpty) { - if (this.props.active !== false) { - queueMicrotask(() => { - self.onActiveChange(false); - }); + return obj; + } + noCsMeta(scheduledId, data, chatRoom) { + const meta = { + timeRules: {}, + userId: data.sender || false, + ap: data, + handle: scheduledId, + cid: chatRoom.chatId + }; + if (!this.getMeetingOrOccurrenceParent(scheduledId) && !chatRoom.scheduledMeeting) { + const res = this._checkOccurrenceAwait(chatRoom, scheduledId, meta); + if (res) { + return res; } - return null; } - return React.createElement(_utils_jsx0__.Ay.RenderTo, { - element: document.body, - className: ` - dropdown - body - ${this.props.noArrow ? '' : 'dropdown-arrow up-arrow'} - ${this.props.className || ''} - `, - style: this.popupElement && { - zIndex: 123, - position: 'absolute', - width: this.props.styles ? this.props.styles.width : undefined - }, - popupDidMount: popupElement => { - this.popupElement = popupElement; - this.onResized(); - }, - popupWillUnmount: () => { - delete this.popupElement; + const meeting = this.getMeetingOrOccurrenceParent(scheduledId) || chatRoom.scheduledMeeting; + assert(meeting, `Invalid scheduled meeting state for ${scheduledId} msg`); + const toS = ms => Math.floor(ms / 1000); + const { + MODE + } = scheduleMetaChange.A; + meta.timeRules.startTime = toS(meeting.start); + meta.timeRules.endTime = toS(meeting.end); + meta.topic = meeting.title; + meta.recurring = !!meeting.recurring; + meta.mode = meeting.canceled ? MODE.CANCELLED : MODE.CREATED; + meta.occurrence = meta.recurring && meeting.id !== scheduledId; + if (!meta.occurrence && !meeting.canceled) { + meta.mode = MODE.CREATED; + } + const cal = ms => { + const date = new Date(ms); + return { + date: date.getDate(), + month: time2date(toS(ms), 12) + }; + }; + if (meta.occurrence) { + const occurrences = meeting.getOccurrencesById(scheduledId); + if (!occurrences) { + meta.mode = MODE.EDITED; + const res = this._checkOccurrenceAwait(chatRoom, scheduledId, meta); + if (res) { + return res; + } + meta.ap = data; + return meta; } - }, React.createElement("div", { - ref: this.domRef, - onClick: () => { - $(document.body).trigger('closeAllDropdownsExcept', this); + meta.mode = occurrences.some(o => o.canceled) ? MODE.CANCELLED : MODE.EDITED; + meta.calendar = cal(occurrences[0].start); + const timeDiff = meta.timeRules.endTime - meta.timeRules.startTime; + meta.timeRules.startTime = toS(occurrences[0].start); + meta.timeRules.endTime = toS(occurrences[0].end); + if (occurrences.length === 1 && occurrences[0].startInitial) { + meta.prevTiming = { + startTime: toS(occurrences[0].startInitial) + }; + meta.prevTiming.endTime = meta.prevTiming.startTime + timeDiff; } - }, this.props.noArrow ? null : React.createElement("i", { - className: "dropdown-white-arrow" - }), child)); - } -} -Dropdown.defaultProps = { - 'requiresUpdateOnResize': true -}; -class DropdownContactsSelector extends _chat_mixins1__.w9 { - constructor(props) { - super(props); - this.state = { - 'selected': this.props.selected ? this.props.selected : [] - }; - this.onSelectClicked = this.onSelectClicked.bind(this); - this.onSelected = this.onSelected.bind(this); - } - specShouldComponentUpdate(nextProps, nextState) { - if (this.props.active != nextProps.active) { - return true; - } else if (this.props.focused != nextProps.focused) { - return true; - } else if (this.state && this.state.active != nextState.active) { - return true; - } else if (this.state && JSON.stringify(this.state.selected) != JSON.stringify(nextState.selected)) { - return true; + } else if (meta.recurring) { + const { + end, + weekDays = [], + interval, + monthDays = [], + offset, + frequency + } = meeting.recurring; + meta.recurring = true; + meta.timeRules.recurEnd = end ? toS(end) : false; + meta.timeRules.interval = interval || 1; + if (frequency === 'd' && interval > 1) { + meta.timeRules.skipDay = true; + } else if (frequency === 'd' && interval === 1) { + meta.timeRules.days = [1, 2, 3, 4, 5, 6, 0]; + } + if (weekDays.length) { + meta.timeRules.days = weekDays.sort((a, b) => a - b).map(wd => wd === 7 ? 0 : wd); + } + if (monthDays.length) { + meta.timeRules.dayInt = monthDays[0]; + } + if (!Array.isArray(offset)) { + meta.timeRules.month = { + count: (offset.value || 1) - 1, + occur: offset.weekDay ? offset.weekDay === 7 ? 0 : offset.weekDay : 1 + }; + } + meta.calendar = cal(meeting.start); } else { - return undefined; + meta.calendar = cal(meeting.start); } - } - onSelected(nodes) { - this.setState({ - 'selected': nodes - }); - if (this.props.onSelected) { - this.props.onSelected(nodes); + if (!meta.occurrence && meeting.canceled && $.len(meta.timeRules)) { + meta.mode = MODE.CREATED; } - this.forceUpdate(); - } - onSelectClicked() { - this.props.onSelectClicked(); - } - render() { - return React.createElement(Dropdown, { - className: ` - popup contacts-search - ${this.props.className} - tooltip-blur - `, - active: this.props.active, - closeDropdown: this.props.closeDropdown, - ref: ref => { - this.dropdownRef = ref; - }, - positionMy: this.props.positionMy, - positionAt: this.props.positionAt, - arrowHeight: this.props.arrowHeight, - horizOffset: this.props.horizOffset, - vertOffset: this.props.vertOffset, - noArrow: true - }, React.createElement(_chat_ui_contacts_jsx2__.ContactPickerWidget, { - onClose: this.props.closeDropdown, - onEventuallyUpdated: () => { - let _this$dropdownRef; - return (_this$dropdownRef = this.dropdownRef) == null ? void 0 : _this$dropdownRef.doRerender(); - }, - active: this.props.active, - className: "popup contacts-search tooltip-blur small-footer", - contacts: M.u, - selectFooter: this.props.selectFooter, - megaChat: this.props.megaChat, - exclude: this.props.exclude, - allowEmpty: this.props.allowEmpty, - multiple: this.props.multiple, - topButtons: this.props.topButtons, - showAddContact: this.props.showAddContact, - onAddContact: () => eventlog(500237), - onSelected: () => eventlog(500238), - onSelectDone: this.props.onSelectDone, - multipleSelectedButtonLabel: this.props.multipleSelectedButtonLabel, - singleSelectedButtonLabel: this.props.singleSelectedButtonLabel, - nothingSelectedButtonLabel: this.props.nothingSelectedButtonLabel - })); - } -} -DropdownContactsSelector.defaultProps = { - requiresUpdateOnResize: true -}; -class DropdownItem extends _chat_mixins1__.w9 { - constructor(props) { - super(props); - this.domRef = React.createRef(); - this.state = { - 'isClicked': false - }; - this.onClick = this.onClick.bind(this); - this.onMouseOver = this.onMouseOver.bind(this); - } - renderChildren() { - const self = this; - return React.Children.map(this.props.children, (child) => { - const props = { - active: self.state.isClicked, - closeDropdown () { - self.setState({ - 'isClicked': false - }); - } - }; - return React.cloneElement(child, props); - }); + delete meta.ap; + return meta; } - onClick(ev) { - const { - children, - persistent, - onClick - } = this.props; - if (children) { - ev.stopPropagation(); - ev.preventDefault(); - this.setState({ - isClicked: !this.state.isClicked - }); + _checkOccurrenceAwait(chatRoom, scheduledId, meta) { + if (!this._goneOccurrences[chatRoom.chatId]) { + this._goneOccurrences[chatRoom.chatId] = {}; } - if (!persistent) { - $(document).trigger('closeDropdowns'); + if (typeof this._goneOccurrences[chatRoom.chatId][scheduledId] === 'undefined') { + this._goneOccurrences[chatRoom.chatId][scheduledId] = -1; + return meta; + } + const datum = this._goneOccurrences[chatRoom.chatId]; + if (datum[scheduledId] === -1) { + return meta; + } else if (datum[scheduledId] === 1) { + meta.gone = true; + return meta; } - return onClick && onClick(ev); + return false; } - onMouseOver(e) { - if (this.props.submenu) { - const $contextItem = $(e.target).closest(".contains-submenu"); - const $subMenu = $contextItem.next('.submenu'); - const contextTopPos = $contextItem.position().top; - let contextleftPos = 0; - $contextItem.addClass("opened"); - $subMenu.addClass("active"); - contextleftPos = $contextItem.offset().left + $contextItem.outerWidth() + $subMenu.outerWidth() + 10; - if (contextleftPos > $(document.body).width()) { - $subMenu.addClass("left-position"); + areMetaObjectsSame(obj1, obj2) { + if (obj1 && !obj2 || !obj1 && obj2) { + return false; + } + const keys = Object.keys(obj1); + if (keys.length !== $.len(obj2)) { + return false; + } + const diff = array.diff(keys, Object.keys(obj2)); + if (diff.removed.length + diff.added.length) { + return false; + } + for (const key of keys) { + if (!obj2.hasOwnProperty(key)) { + return false; + } + if (obj1[key] instanceof Object && obj2[key] instanceof Object) { + if (!this.areMetaObjectsSame(obj1[key], obj2[key])) { + return false; + } + } else if (Array.isArray(obj1[key]) && Array.isArray(obj2[key])) { + const keyDiff = array.diff(obj1[key], obj2[key]); + if (keyDiff.removed.length + keyDiff.added.length) { + return false; + } + } else if (obj1[key] !== obj2[key]) { + return false; } - $subMenu.css({ - "top": contextTopPos - }); - } else if (!$(e.target).parent('.submenu').length) { - const $dropdown = $(e.target).closest(".dropdown.body"); - $dropdown.find(".contains-submenu").removeClass("opened"); - $dropdown.find(".submenu").removeClass("active"); } - } - render() { - const { - className, - disabled, - label, - icon, - submenu - } = this.props; - return React.createElement("div", { - ref: this.domRef, - className: ` - dropdown-item - ${className ? className : ''} - ${submenu ? 'contains-submenu' : ''} - ${disabled ? 'disabled' : ''} - `, - onClick: disabled ? undefined : ev => this.onClick(ev), - onMouseOver: this.onMouseOver - }, icon && React.createElement("i", { - className: icon - }), label && React.createElement("span", null, label), submenu ? React.createElement("i", { - className: "sprite-fm-mono icon-arrow-right submenu-icon" - }) : '', React.createElement("div", null, this.renderChildren())); + return true; } } -DropdownItem.defaultProps = { - requiresUpdateOnResize: true -}; - -}, - -844 -(_, EXP_, REQ_) { - -"use strict"; -REQ_.d(EXP_, { -L: () => DropdownEmojiSelector -}); -const _extends0__ = REQ_(168); -const _chat_mixins1__ = REQ_(137); + const meetingsManager = MeetingsManager; +window.MeetingsManager = MeetingsManager; +// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/applyDecoratedDescriptor.js +const applyDecoratedDescriptor = REQ_(793); +// EXTERNAL MODULE: ./js/chat/mixins.js +const mixins = REQ_(8264); +;// ./js/chat/chatOnboarding.jsx -const React = REQ_(594); +let _dec, _class; -const DropdownsUI = REQ_(911); -const PerfectScrollbar = REQ_(486).O; -class DropdownEmojiSelector extends _chat_mixins1__.w9 { - constructor(props) { - super(props); - this.domRef = React.createRef(); - this.emojiSearchRef = React.createRef(); - this.data_categories = null; - this.data_emojis = null; - this.data_emojiByCategory = null; - this.customCategoriesOrder = ["frequently_used", "people", "nature", "food", "activity", "travel", "objects", "symbols", "flags"]; - this.frequentlyUsedEmojis = ['slight_smile', 'grinning', 'smile', 'rofl', 'wink', 'yum', 'rolling_eyes', 'stuck_out_tongue', 'smiling_face_with_3_hearts', 'heart_eyes', 'kissing_heart', 'sob', 'pleading_face', 'thumbsup', 'pray', 'wave', 'fire', 'sparkles']; - this.heightDefs = { - 'categoryTitleHeight': 55, - 'emojiRowHeight': 35, - 'containerHeight': 302, - 'totalScrollHeight': 302, - 'numberOfEmojisPerRow': 9 - }; - this.categoryLabels = { - 'frequently_used': l[17737], - 'people': l[8016], - 'objects': l[17735], - 'activity': l[8020], - 'nature': l[8017], - 'travel': l[8021], - 'symbols': l[17736], - 'food': l[8018], - 'flags': l[17703] - }; - this.state = this.getInitialState(); - this.onSearchChange = this.onSearchChange.bind(this); - this.onUserScroll = this.onUserScroll.bind(this); - this._onScrollChanged = this._onScrollChanged.bind(this); - } - getInitialState() { - return clone({ - 'previewEmoji': null, - 'searchValue': '', - 'browsingCategory': false, - 'isActive': false, - 'isLoading': true, - 'loadFailed': false, - 'visibleCategories': "0" - }); - } - _generateEmoji(meta) { - const filename = twemoji.convert.toCodePoint(meta.u); - return React.createElement("img", { - width: "20", - height: "20", - className: "emoji emoji-loading", - draggable: "false", - alt: meta.u, - title: `:${ meta.n }:`, - onLoad: e => { - e.target.classList.remove('emoji-loading'); - }, - onError: e => { - e.target.classList.remove('emoji-loading'); - e.target.classList.add('emoji-loading-error'); - }, - src: `${staticpath }images/mega/twemojis/2_v2/72x72/${ filename }.png` - }); - } - _generateEmojiElement(emoji, cat) { - const self = this; - const categoryName = self.data_categories[cat]; - return React.createElement("div", { - "data-emoji": emoji.n, - className: "button square-button emoji", - key: `${categoryName }_${ emoji.n}`, - onMouseEnter: e => { - if (self.mouseEnterTimer) { - clearTimeout(self.mouseEnterTimer); - } - e.stopPropagation(); - e.preventDefault(); - self.mouseEnterTimer = setTimeout(() => { - self.setState({ - 'previewEmoji': emoji - }); - }, 250); - }, - onMouseLeave: e => { - if (self.mouseEnterTimer) { - clearTimeout(self.mouseEnterTimer); - } - e.stopPropagation(); - e.preventDefault(); - self.setState({ - 'previewEmoji': null - }); - }, - onClick: e => { - if (self.props.onClick) { - self.props.onClick(e, emoji.n, emoji); - $(document).trigger('closeDropdowns'); - } - } - }, self._generateEmoji(emoji)); - } - UNSAFE_componentWillUpdate(nextProps, nextState) { - if (nextState.searchValue !== this.state.searchValue || nextState.browsingCategories !== this.state.browsingCategories) { - this._cachedNodes = {}; - if (this.scrollableArea) { - this.scrollableArea.scrollToY(0); - } - this._onScrollChanged(0, nextState); - } - if (nextState.isActive === true) { - const self = this; - if (nextState.isLoading === true || !self.loadingPromise && (!self.data_categories || !self.data_emojis)) { - const p = [megaChat.getEmojiDataSet('categories'), megaChat.getEmojiDataSet('emojis')]; - this.loadingPromise = Promise.all(p).then(([categories, emojis]) => { - this.data_emojis = emojis; - this.data_categories = categories; - self.data_categories.push('frequently_used'); - self.data_categoriesWithCustomOrder = []; - self.customCategoriesOrder.forEach((catName) => { - self.data_categoriesWithCustomOrder.push(self.data_categories.indexOf(catName)); - }); - self.data_emojiByCategory = {}; - const frequentlyUsedEmojisMeta = {}; - self.data_emojis.forEach((emoji) => { - const cat = emoji.c; - if (!self.data_emojiByCategory[cat]) { - self.data_emojiByCategory[cat] = []; - } - if (self.frequentlyUsedEmojis.indexOf(emoji.n) > -1) { - frequentlyUsedEmojisMeta[emoji.n] = emoji.u; - } - emoji.element = self._generateEmojiElement(emoji, cat); - self.data_emojiByCategory[cat].push(emoji); - }); - self.data_emojiByCategory[8] = []; - self.frequentlyUsedEmojis.forEach((slug) => { - const emoji = { - 'n': slug, - 'u': frequentlyUsedEmojisMeta[slug] - }; - emoji.element = self._generateEmojiElement(emoji, 99); - self.data_emojiByCategory[8].push(emoji); - }); - self._onScrollChanged(0); - self.setState({ - 'isLoading': false - }); - }).catch(ex => { - if (d) { - console.error("Emoji loading failed.", ex); - } - this.setState({ - 'loadFailed': true, - 'isLoading': false - }); - }); - } - } else if (nextState.isActive === false) { - if (this.data_emojis) { - for (let i = this.data_emojis.length; i--;) { - delete this.data_emojis[i].element; +const ChatOnboarding = (_dec = (0,mixins.hG)(1000), _class = class ChatOnboarding { + constructor(megaChat) { + this.finished = false; + if (u_type === 3 && !is_mobile) { + this.state = { + [OBV4_FLAGS.CHAT]: -1 + }; + this.megaChat = megaChat; + this.flagMap = attribCache.bitMapsManager.exists('obv4') ? attribCache.bitMapsManager.get('obv4') : new MegaDataBitMap('obv4', false, Object.values(OBV4_FLAGS)); + const keys = Object.keys(this.state); + const promises = keys.map(key => this.flagMap.get(key)); + Promise.allSettled(promises).then(res => { + for (let i = 0; i < res.length; ++i) { + const v = res[i]; + if (v.status === 'fulfilled') { + this.handleFlagChange(null, null, keys[i], v.value); + } } - } - this.data_emojis = null; - this.data_categories = null; - this.data_emojiByCategory = null; - this.loadingPromise = null; + }); + this.interval = setInterval(() => { + if (!$.dialog) { + this._checkAndShowStep(); + } + }, 10000); + this.initListeners(); } } - onSearchChange(e) { - const self = this; - self.setState({ - searchValue: e.target.value, - browsingCategory: false + initListeners() { + this.flagMap.addChangeListener((...args) => this.handleFlagChange(...args)); + this.megaChat.chatUIFlags.addChangeListener(SoonFc(200, () => { + if (this.megaChat.chatUIFlags.convPanelCollapse && $.dialog === 'onboardingDialog') { + closeDialog(); + } + this._checkAndShowStep(); + })); + this.megaChat.addChangeListener(() => { + const room = this.megaChat.getCurrentRoom(); + if (!room) { + return; + } + this.checkAndShowStep(); }); } - onUserScroll($ps) { - if (this.state.browsingCategory) { - const $cat = $(`.emoji-category-container[data-category-name="${ this.state.browsingCategory }"]`); - if (!elementInViewport($cat)) { - this.setState({ - 'browsingCategory': false - }); - } - } - this._onScrollChanged($ps.getScrollPositionY()); + checkAndShowStep() { + this._checkAndShowStep(); } - generateEmojiElementsByCategory(categoryId, posTop, stateObj) { - const self = this; - if (!self._cachedNodes) { - self._cachedNodes = {}; + _shouldSkipShow() { + if (!M.chat || !mega.ui.onboarding || $.dialog || loadingDialog.active || u_type < 3 || is_mobile || $.msgDialog) { + return true; } - if (!stateObj) { - stateObj = self.state; + this.$topRightMenu = this.$topRightMenu || $('.top-menu-popup', '#topmenu'); + if (!this.$topRightMenu.hasClass('o-hidden')) { + return true; } - if (typeof self._cachedNodes[categoryId] !== 'undefined') { - return self._cachedNodes[categoryId]; + this.$topAccDropdown = this.$topAccDropdown || $('.js-dropdown-account', '#topmenu'); + if (this.$topAccDropdown.hasClass('show')) { + return true; } - const categoryName = self.data_categories[categoryId]; - const emojis = []; - const {searchValue} = stateObj; - let totalEmojis = 0; - self.data_emojiByCategory[categoryId].forEach((meta) => { - const slug = meta.n; - if (searchValue.length > 0) { - if (`:${ slug }:`.toLowerCase().indexOf(searchValue.toLowerCase()) < 0) { - return; - } - } - totalEmojis++; - emojis.push(meta.element); - }); - if (emojis.length > 0) { - const totalHeight = self.heightDefs.categoryTitleHeight + Math.ceil(totalEmojis / self.heightDefs.numberOfEmojisPerRow) * self.heightDefs.emojiRowHeight; - return self._cachedNodes[categoryId] = [totalHeight, React.createElement("div", { - key: categoryName, - "data-category-name": categoryName, - className: "emoji-category-container", - style: { - 'position': 'absolute', - 'top': posTop - } - }, emojis.length > 0 ? React.createElement("div", { - className: "clear" - }) : null, React.createElement("div", { - className: "emoji-type-txt" - }, self.categoryLabels[categoryName] ? self.categoryLabels[categoryName] : categoryName), React.createElement("div", { - className: "clear" - }), emojis, React.createElement("div", { - className: "clear" - }))]; - } else { - return self._cachedNodes[categoryId] = undefined; + this.$topNotifDropdown = this.$topNotifDropdown || $('.js-dropdown-notification', '#topmenu'); + if (this.$topNotifDropdown.hasClass('show')) { + return true; } + this.$searchPanel = this.$searchPanel || $('.search-panel', '.conversationsApp'); + return this.$searchPanel.hasClass('expanded'); } - _isVisible(scrollTop, scrollBottom, elTop, elBottom) { - const visibleTop = elTop < scrollTop ? scrollTop : elTop; - const visibleBottom = elBottom > scrollBottom ? scrollBottom : elBottom; - return visibleBottom - visibleTop > 0; - } - _onScrollChanged(scrollPositionY, stateObj) { - const self = this; - if (!self.data_categoriesWithCustomOrder) { + _checkAndShowStep() { + if (this._shouldSkipShow()) { return; } - if (scrollPositionY === false) { - scrollPositionY = self.scrollableArea.getScrollPositionY(); + const { + sections + } = mega.ui.onboarding; + if (!sections) { + return; } - if (!stateObj) { - stateObj = self.state; + const { + chat: obChat + } = sections; + if (!obChat) { + return; } - const visibleStart = scrollPositionY; - const visibleEnd = visibleStart + self.heightDefs.containerHeight; - let currentPos = 0; - let visibleCategories = []; - self._emojiReactElements = []; - self.data_categoryPositions = {}; - self.data_categoriesWithCustomOrder.forEach((k) => { - const categoryDivMeta = self.generateEmojiElementsByCategory(k, currentPos, stateObj); - if (categoryDivMeta) { - const startPos = currentPos; - currentPos += categoryDivMeta[0]; - const endPos = currentPos; - self.data_categoryPositions[k] = startPos; - if (self._isVisible(visibleStart, visibleEnd, startPos, endPos)) { - visibleCategories.push(k); - self._emojiReactElements.push(categoryDivMeta[1]); - } - } - }); - if (self._emojiReactElements.length === 0) { - const emojisNotFound = React.createElement("span", { - className: "emojis-not-found", - key: 'emojis-not-found' - }, l[20920]); - self._emojiReactElements.push(emojisNotFound); + if (this.state[OBV4_FLAGS.CHAT]) { + return; } - visibleCategories = visibleCategories.join(','); - self.setState({ - 'totalScrollHeight': currentPos, - visibleCategories - }); + this.showDefaultNextStep(obChat); } - _renderEmojiPickerPopup() { - const self = this; - let preview; - if (self.state.previewEmoji) { - const meta = self.state.previewEmoji; - preview = React.createElement("div", { - className: "emoji-preview" - }, self._generateEmoji(meta), React.createElement("div", { - className: "emoji title" - }, `:${ meta.n }:`)); + showDefaultNextStep(obChat) { + const nextIdx = obChat.searchNextOpenStep(); + if (nextIdx !== false && (!this.$obDialog || !this.$obDialog.is(':visible')) && (this.obToggleDrawn || $('.conversations-category', '.conversationsApp').length)) { + this.obToggleDrawn = true; + if (obChat.steps && obChat.steps[nextIdx] && obChat.steps[nextIdx].isComplete) { + return; + } + obChat.startNextOpenSteps(nextIdx); + this.$obDialog = this.$obDialog || $('#ob-dialog'); } - const categoryIcons = { - "frequently_used": "icon-emoji-type-frequent", - "people": "icon-emoji-type-people", - "nature": "icon-emoji-type-nature", - "food": "icon-emoji-type-food", - "activity": "icon-emoji-type-activity", - "travel": "icon-emoji-type-travel", - "objects": "icon-emoji-type-objects", - "symbols": "icon-emoji-type-symbol", - "flags": "icon-emoji-type-flag" - }; - const categoryButtons = []; - let activeCategoryName = false; - if (!self.state.searchValue) { - const firstActive = self.state.visibleCategories.split(",")[0]; - if (firstActive) { - activeCategoryName = self.data_categories[firstActive]; + } + handleFlagChange(...args) { + if (args.length >= 4 && typeof args[2] === 'string' && typeof args[3] === 'number' && this.state.hasOwnProperty(args[2])) { + if (d) { + console.debug(`Chat onboarding flag ${args[2]}: ${this.state[args[2]]} -> ${args[3]}`); + } + this.state[args[2]] = args[3]; + if (args[2] === OBV4_FLAGS.CHAT && args[3] === 1 && this.interval) { + clearInterval(this.interval); + delete this.interval; } } - self.customCategoriesOrder.forEach(categoryName => { - categoryButtons.push(React.createElement("div", { - visiblecategories: this.state.visibleCategories, - className: ` - button square-button emoji - ${activeCategoryName === categoryName ? 'active' : ''} - `, - key: categoryIcons[categoryName], - onClick: e => { - e.stopPropagation(); - e.preventDefault(); - this.setState({ - browsingCategory: categoryName, - searchValue: '' - }); - this._cachedNodes = {}; - const categoryPosition = this.data_categoryPositions[this.data_categories.indexOf(categoryName)] + 10; - this.scrollableArea.scrollToY(categoryPosition); - this._onScrollChanged(categoryPosition); - const { - current - } = this.emojiSearchRef || !1; - current == null || current.focus(); + } + destroy() { + if (this.interval) { + clearInterval(this.interval); + delete this.interval; + } + } +}, (0,applyDecoratedDescriptor.A)(_class.prototype, "checkAndShowStep", [_dec], Object.getOwnPropertyDescriptor(_class.prototype, "checkAndShowStep"), _class.prototype), _class); + +// EXTERNAL MODULE: ./js/chat/ui/meetings/utils.jsx +const utils = REQ_(3901); +// EXTERNAL MODULE: ./js/chat/chatGlobalEventManager.jsx +const chatGlobalEventManager = REQ_(8676); +// EXTERNAL MODULE: ./js/chat/ui/messages/utils.jsx +const messages_utils = REQ_(187); +// EXTERNAL MODULE: ./js/chat/utils.jsx +const chat_utils = REQ_(5779); +// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/extends.js +const esm_extends = REQ_(8168); +// EXTERNAL MODULE: ./js/chat/ui/contacts.jsx +const contacts = REQ_(8022); +// EXTERNAL MODULE: ./js/ui/modalDialogs.jsx + 1 modules +const modalDialogs = REQ_(8120); +// EXTERNAL MODULE: ./js/chat/ui/meetings/button.jsx +const meetings_button = REQ_(6740); +// EXTERNAL MODULE: ./js/ui/utils.jsx +const ui_utils = REQ_(6411); +;// ./js/chat/ui/meetings/workflow/incoming.jsx + + + + + + + +class Incoming extends REaCt().Component { + constructor(props) { + super(props); + this.state = { + video: false, + unsupported: undefined, + hoveredSwitch: true, + hideOverlay: false + }; + this.renderSwitchControls = () => { + const className = `mega-button large round switch ${this.state.hoveredSwitch ? 'hovered' : ''}`; + const toggleHover = () => this.setState(state => ({ + hoveredSwitch: !state.hoveredSwitch + })); + return JSX_("div", { + className: "switch-button" + }, JSX_("div", { + className: "switch-button-container simpletip", + "data-simpletip": l.end_and_answer, + "data-simpletipposition": "top", + onMouseEnter: toggleHover, + onMouseLeave: toggleHover, + onClick: ev => { + ev.stopPropagation(); + this.props.onSwitch(); } - }, React.createElement("i", { - className: `sprite-fm-mono ${categoryIcons[categoryName]}` + }, JSX_(meetings_button.A, { + className: `${className} negative`, + icon: "icon-end-call" + }), JSX_(meetings_button.A, { + className: `${className} positive`, + icon: "icon-phone" }))); - }); - return React.createElement(React.Fragment, null, React.createElement("div", { - className: "popup-header emoji" - }, preview || React.createElement("div", { - className: "search-block emoji" - }, React.createElement("i", { - className: "sprite-fm-mono icon-preview-reveal" - }), React.createElement("input", { - ref: this.emojiSearchRef, - type: "search", - placeholder: l[102], - onChange: this.onSearchChange, - autoFocus: true, - value: this.state.searchValue - }))), React.createElement(PerfectScrollbar, { - className: "popup-scroll-area emoji perfectScrollbarContainer", - searchValue: this.state.searchValue, - onUserScroll: this.onUserScroll, - visibleCategories: this.state.visibleCategories, - ref: ref => { - this.scrollableArea = ref; - } - }, React.createElement("div", { - className: "popup-scroll-content emoji" - }, React.createElement("div", { - style: { - height: this.state.totalScrollHeight - } - }, this._emojiReactElements))), React.createElement("div", { - className: "popup-footer emoji" - }, categoryButtons)); + }; + this.renderAnswerControls = () => { + const { + video, + unsupported + } = this.state; + const { + onAnswer, + onToggleVideo + } = this.props; + return JSX_(REaCt().Fragment, null, JSX_(meetings_button.A, { + className: ` + mega-button + positive + answer + ${unsupported ? 'disabled' : ''} + `, + icon: "icon-phone", + simpletip: unsupported ? null : { + position: 'top', + label: l[7205] + }, + onClick: unsupported ? null : onAnswer + }, JSX_("span", null, l[7205])), JSX_(meetings_button.A, { + className: ` + mega-button + large + round + video + ${video ? '' : 'negative'} + ${unsupported ? 'disabled' : ''} + `, + icon: video ? 'icon-video-call-filled' : 'icon-video-off', + simpletip: unsupported ? null : { + position: 'top', + label: video ? l[22894] : l[22893] + }, + onClick: () => unsupported ? null : this.setState({ + video: !video + }, () => onToggleVideo(video)) + }, JSX_("span", null, video ? l[22894] : l[22893]))); + }; + this.state.unsupported = !megaChat.hasSupportForCalls; + this.state.hideOverlay = document.body.classList.contains('overlayed') && !$.msgDialog; + } + componentDidMount() { + this._old$dialog = $.dialog; + $.dialog = "chat-incoming-call"; + } + componentWillUnmount() { + $.dialog = this._old$dialog; } render() { - const self = this; - let popupContents = null; - if (self.state.isActive === true) { - if (self.state.loadFailed === true) { - popupContents = React.createElement("div", { - className: "loading" - }, l[1514]); - } else if (this.state.isLoading || !this.data_emojiByCategory || !this.data_categories) { - popupContents = React.createElement("div", { - className: "loading" - }, l[5533]); - } else { - popupContents = self._renderEmojiPickerPopup(); - } - } else { - popupContents = null; + const { + chatRoom + } = this.props; + if (chatRoom) { + const { + NAMESPACE + } = Incoming; + const { + callerId, + onClose, + onReject + } = this.props; + const { + unsupported + } = this.state; + const CALL_IN_PROGRESS = window.sfuClient; + const isPrivateRoom = chatRoom.type === 'private'; + const rejectLabel = isPrivateRoom ? l[20981] : l.msg_dlg_cancel; + return JSX_(modalDialogs.A.ModalDialog, (0,esm_extends.A)({}, this.state, { + name: NAMESPACE, + className: NAMESPACE, + roomName: chatRoom.getRoomTitle(), + onClose: () => onClose() + }), JSX_("div", { + className: "fm-dialog-body" + }, JSX_("div", { + className: `${NAMESPACE}-avatar` + }, JSX_(contacts.eu, { + contact: M.u[callerId] + })), JSX_("div", { + className: `${NAMESPACE}-info` + }, JSX_("h1", null, JSX_(ui_utils.zT, null, chatRoom.getRoomTitle())), JSX_("span", null, isPrivateRoom ? l[17878] : l[19995])), JSX_("div", { + className: ` + ${NAMESPACE}-controls + ${CALL_IN_PROGRESS ? 'call-in-progress' : ''} + ` + }, JSX_(meetings_button.A, { + className: ` + mega-button + large + round + negative + `, + icon: "icon-end-call", + simpletip: { + position: 'top', + label: rejectLabel + }, + onClick: onReject + }, JSX_("span", null, rejectLabel)), CALL_IN_PROGRESS ? this.renderSwitchControls() : this.renderAnswerControls()), unsupported && JSX_("div", { + className: `${NAMESPACE}-unsupported` + }, JSX_("div", { + className: "unsupported-message" + }, (0,utils.HV)())))); } - return React.createElement(DropdownsUI.Dropdown, (0,_extends0__.A)({ - className: "popup emoji" - }, self.props, { - isLoading: self.state.isLoading, - loadFailed: self.state.loadFailed, - visibleCategories: this.state.visibleCategories, - forceShowWhenEmpty: true, - onActiveChange: newValue => { - if (newValue === false) { - self.setState(self.getInitialState()); - self._cachedNodes = {}; - self._onScrollChanged(0); - } else { - self.setState({ - 'isActive': true - }); - } - if (self.props.onActiveChange) { - self.props.onActiveChange(newValue); + console.error('Incoming dialog received missing chatRoom prop.'); + return null; + } +} +Incoming.NAMESPACE = 'incoming-dialog'; +;// ./js/chat/chat.jsx + + + + + + + + + +window.chatGlobalEventManager = chatGlobalEventManager.r; + +mega.ui = mega.ui || {}; +mega.ui.chat = mega.ui.chat || {}; +mega.ui.chat.getMessageString = messages_utils.d; + + +const ScheduleMeeting = (0,external_React_.lazy)(() => REQ_.e( 716).then(REQ_.bind(REQ_, 8389))); +const StartMeeting = (0,external_React_.lazy)(() => REQ_.e( 543).then(REQ_.bind(REQ_, 7190))); +const ContactSelectorDialog = (0,external_React_.lazy)(() => REQ_.e( 543).then(REQ_.bind(REQ_, 2678))); +const StartGroupChatWizard = (0,external_React_.lazy)(() => REQ_.e( 543).then(REQ_.bind(REQ_, 5199))); +const CloudBrowserDialog = (0,external_React_.lazy)(() => REQ_.e( 313).then(REQ_.bind(REQ_, 6961))); +window.ChatCallIncomingDialog = (0,chat_utils.li)(Incoming); +window.ScheduleMeetingDialogUI = { + Schedule: (0,chat_utils.li)(ScheduleMeeting) +}; +window.StartMeetingDialogUI = { + Start: (0,chat_utils.li)(StartMeeting) +}; +window.ContactSelectorDialogUI = { + ContactSelectorDialog: (0,chat_utils.li)(ContactSelectorDialog) +}; +window.StartGroupChatDialogUI = { + StartGroupChatWizard: (0,chat_utils.li)(StartGroupChatWizard) +}; +Object.defineProperty(mega, 'CloudBrowserDialog', { + value: (0,chat_utils.li)(CloudBrowserDialog) +}); +const EMOJI_DATASET_VERSION = 5; +const CHAT_ONHISTDECR_RECNT = "onHistoryDecrypted.recent"; +const LOAD_ORIGINALS = { + 'image/gif': 25e6, + 'image/png': 2e5, + 'image/webp': 2e5 +}; +const CHATUIFLAGS_MAPPING = { + 'convPanelCollapse': 'cPC' +}; +function Chat() { + const self = this; + this.is_initialized = false; + this.logger = MegaLogger.getLogger("chat"); + this.mbListeners = []; + this.chats = new MegaDataMap(); + this.scheduledMeetings = new MegaDataMap(); + this.chatUIFlags = new MegaDataMap(); + this.$chatTreePanePs = []; + this.initChatUIFlagsManagement(); + this.currentlyOpenedChat = null; + this.currentlyOpenedView = null; + this.lastOpenedChat = null; + this.archivedChatsCount = 0; + this.FORCE_EMAIL_LOADING = localStorage.fel; + this.WITH_SELF_NOTE = mega.flags.ff_n2s || localStorage.withSelfNote; + this._imageLoadCache = Object.create(null); + this._imagesToBeLoaded = Object.create(null); + this._imageAttributeCache = Object.create(null); + this._queuedMccPackets = []; + this._queuedMcsmPackets = {}; + this._queuedMessageUpdates = []; + this._queuedChatRoomEvents = Object.create(null); + this.handleToId = Object.create(null); + this.publicChatKeys = Object.create(null); + this.SOUNDS = { + ALERT: 'alert_info_message', + INCOMING_MSG: 'incoming_chat_message', + INCOMING_CALL: 'incoming_voice_video_call', + CALL_JOIN: 'user_join_call', + CALL_LEFT: 'user_left_call', + CALL_END: 'end_call', + CALL_JOIN_WAITING: 'user_join_waiting', + RECONNECT: 'reconnecting', + SPEAKER_TEST: 'test_speaker' + }; + this.options = { + 'delaySendMessageIfRoomNotAvailableTimeout': 3000, + 'plugins': { + 'chatdIntegration': ChatdIntegration, + 'callManager2': CallManager2, + 'urlFilter': UrlFilter, + 'emoticonShortcutsFilter': EmoticonShortcutsFilter, + 'emoticonsFilter': EmoticonsFilter, + 'callFeedback': CallFeedback, + 'presencedIntegration': PresencedIntegration, + 'persistedTypeArea': PersistedTypeArea, + 'btRtfFilter': BacktickRtfFilter, + 'rtfFilter': RtfFilter, + 'richpreviewsFilter': RichpreviewsFilter, + 'chatToastIntegration': ChatToastIntegration, + 'chatStats': ChatStats, + 'geoLocationLinks': GeoLocationLinks, + meetingsManager, + 'chatOnboarding': ChatOnboarding, + 'userHelper': ChatUserHelper + }, + 'chatNotificationOptions': { + 'textMessages': { + 'incoming-chat-message': { + title: l.notif_title_incoming_msg, + 'icon' (notificationObj) { + return notificationObj.options.icon; + }, + 'body' (notificationObj, params) { + if (params.type === 'private') { + return l.notif_body_incoming_msg.replace('%s', params.from); + } + return l.notif_body_incoming_msg_group.replace('%1', params.from).replace('%2', params.roomTitle); + } + }, + 'incoming-voice-video-call': { + 'title': l[17878] || "Incoming call", + 'icon' (notificationObj) { + return notificationObj.options.icon; + }, + 'body' (notificationObj, params) { + return l[5893].replace('[X]', params.from); + } + }, + 'screen-share-error': { + title: l.screenshare_failed_notif || 'You are no longer sharing your screen', + icon: notificationObj => { + return notificationObj.options.icon; + }, + body: '' + }, + 'upcoming-scheduled-occurrence': { + title: ({ + options + }) => { + return options.meeting.title; + }, + icon: `${staticpath}/images/mega/mega-icon.svg`, + body: l.notif_body_scheduled_upcoming + }, + 'starting-scheduled-occurrence': { + title: ({ + options + }) => { + return options.meeting.title; + }, + icon: `${staticpath}/images/mega/mega-icon.svg`, + body: l.notif_body_scheduled_starting } }, - searchValue: self.state.searchValue, - browsingCategory: self.state.browsingCategory, - previewEmoji: self.state.previewEmoji - }), React.createElement("div", { - ref: this.domRef - }, popupContents)); - } + sounds: Object.values(this.SOUNDS) + }, + 'chatStoreOptions': { + 'autoPurgeMaxMessagesPerRoom': 1024 + } + }; + this.SOUNDS.buffers = Object.create(null); + this.plugins = {}; + self.filePicker = null; + self._chatsAwaitingAps = {}; + MegaDataObject.call(this, { + "currentlyOpenedChat": null, + "activeCall": null, + 'routingSection': null, + 'routingSubSection': null, + 'routingParams': null + }); + this.routing = new ChatRouting(this); + Object.defineProperty(this, 'hasSupportForCalls', { + get () { + return typeof SfuClient !== 'undefined' && typeof TransformStream !== 'undefined' && window.RTCRtpSender && !!RTCRtpSender.prototype.createEncodedStreams; + } + }); + this.minuteClockInterval = setInterval(() => this._syncChats(), 6e4); + return this; } -DropdownEmojiSelector.defaultProps = { - 'requiresUpdateOnResize': true, - 'hideable': true -}; - -}, - -701 -(_, EXP_, REQ_) { - -"use strict"; - -// EXPORTS -REQ_.d(EXP_, { - A: () => FMView +inherits(Chat, MegaDataObject); +Object.defineProperty(Chat, 'mcf', { + value: Object.create(null) }); - -// EXTERNAL MODULE: external "React" -const React_ = REQ_(594); -const REaCt = REQ_.n(React_); -// EXTERNAL MODULE: ./js/chat/mixins.js -const mixins = REQ_(137); -// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/extends.js -const esm_extends = REQ_(168); -// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/applyDecoratedDescriptor.js -const applyDecoratedDescriptor = REQ_(793); -// EXTERNAL MODULE: ./js/ui/perfectScrollbar.jsx -const perfectScrollbar = REQ_(486); -;// ./js/ui/jsx/megaList/megaList2.jsx - - -let _dec, _class; - - - -const MegaList2 = (_dec = (0,mixins.hG)(30, true), _class = class MegaList2 extends mixins.w9 { - constructor(props) { - super(props); - this._calculated = false; - this._firstRender = true; - this.customIsEventuallyVisible = true; - this.requiresUpdateOnResize = true; - this.adapterChangedDoRepaint = false; - assert(props.listAdapter, 'missing `listAdapter` for MegaList2'); - assert(props.nodeAdapter, 'missing `nodeAdapter` for MegaList2'); - assert(props.entries, 'missing `entries` for MegaList2'); - this.options = { - extraRows: 8, - batchPages: 0, - perfectScrollOptions: { - 'handlers': ['click-rail', 'drag-thumb', 'wheel', 'touch'], - 'minScrollbarLength': 20 +Object.defineProperty(Chat, 'mcsm', { + value: Object.create(null) +}); +Chat.prototype.init = promisify(function (resolve, reject) { + const self = this; + if (self.is_initialized) { + self.destroy(); + } + if (d) { + console.time('megachat:plugins:init'); + } + self.plugins = Object.create(null); + self.plugins.chatNotifications = new ChatNotifications(self, self.options.chatNotificationOptions); + self.plugins.chatNotifications.notifications.rebind('onAfterNotificationCreated.megaChat', () => { + self.updateSectionUnreadCount(); + }); + Object.keys(self.options.plugins).forEach(plugin => { + self.plugins[plugin] = new self.options.plugins[plugin](self); + }); + if (d) { + console.timeEnd('megachat:plugins:init'); + } + $(document.body); + if (!is_chatlink) { + $(mega.ui.header.setStatus).rebind('mousedown.megachat', '.sub-menu.status button', function () { + const presence = $(this).data("presence"); + self._myPresence = presence; + const targetPresence = PresencedIntegration.cssClassToPresence(presence); + self.plugins.presencedIntegration.setPresence(targetPresence); + if (targetPresence !== UserPresence.PRESENCE.OFFLINE) { + Object.keys(self.plugins.chatdIntegration.chatd.shards).forEach(k => { + const v = self.plugins.chatdIntegration.chatd.shards[k]; + v.connectionRetryManager.requiresConnection(); + }); } - }; - this.onPsUserScroll = this.onPsUserScroll.bind(this); - this.thumbsLoadingHandlers = new MapSet(); - this.thumbsThatRequireLoading = new MapSet(); - this.requestThumbnailCb = this.requestThumbnailCb.bind(this); + }); } - specShouldComponentUpdate(nextProps) { - let invalidate = false; - if (nextProps.listAdapter.prototype.constructor.name !== this.props.listAdapter.prototype.constructor.name || nextProps.entries !== this.props.entries || nextProps.viewMode !== this.props.viewMode) { - invalidate = true; - } - if (nextProps.sortBy !== this.props.sortBy || nextProps.currentlyViewedEntry !== this.props.currentlyViewedEntry) { - invalidate = true; - this.domRef.scrollToY(0); - } - if (invalidate) { - this._calculated = false; - this.adapterChangedDoRepaint = true; - return true; + self.$container = $('.fm-chat-block'); + if (M.chat && !is_chatlink) { + $('.activity-status-block, .activity-status').removeClass('hidden'); + $('.js-dropdown-account .status-dropdown').removeClass('hidden'); + } + if (is_chatlink) { + const { + ph, + key + } = is_chatlink; + Chat.mcf[ph] = key; + this.publicChatKeys[ph] = key; + } + const promises = []; + const rooms = Object.keys(Chat.mcf); + for (let i = rooms.length; i--;) { + const roomId = rooms[i]; + const room = Chat.mcf[roomId]; + if (!this.publicChatKeys[rooms[i]]) { + promises.push(self.plugins.chatdIntegration.openChat(room, true)); } - return null; + delete Chat.mcf[rooms[i]]; } - _recalculate() { - if (this._calculated) { - return this._calculated; + Promise.allSettled(promises).then(res => { + const pub = Object.keys(this.publicChatKeys); + return Promise.allSettled([res].concat(pub.map(pch => { + return this.plugins.chatdIntegration.openChat(pch, true); + }))); + }).then(res => { + res = res[0].value.concat(res.slice(1)); + this.logger.info('chats settled...', res); + if (is_mobile) { + return; } - const calculated = this._calculated = Object.create(null); - lazy(calculated, 'scrollWidth', () => { - return this.domRef.getClientWidth(); - }); - lazy(calculated, 'scrollHeight', () => this.domRef.getClientHeight() - calculated.headerHeight); - lazy(calculated, 'itemWidth', () => { - if (this.props.listAdapter.itemWidth === false) { - return calculated.scrollWidth; - } - return this.props.listAdapter.itemWidth; - }); - lazy(calculated, 'itemHeight', () => { - return this.props.itemHeight || this.props.listAdapter.itemHeight; - }); - lazy(calculated, 'headerHeight', () => this.props.headerHeight || 0); - lazy(calculated, 'contentWidth', () => { - const contentWidth = this.domRef.getContentWidth(); - if (contentWidth) { - return contentWidth; - } - return calculated.itemWidth; - }); - lazy(calculated, 'itemsPerRow', () => { - if (this.props.listAdapter.itemsPerRow) { - return this.props.listAdapter.itemsPerRow; + if (is_chatlink) { + const start = document.getElementById('startholder'); + this.flyoutStartHolder = start.querySelector('.flyout-holder') || mCreateElement('div', { + class: 'flyout-holder' + }, start); + } + const selector = is_chatlink ? '.chat-links-preview > .chat-app-container' : '.section.conversations'; + const rootDOMNode = this.rootDOMNode = document.querySelector(selector); + const $$root = this.$$root = (0,external_ReactDOM_.createRoot)(rootDOMNode); + $$root.render(JSX_(conversations.Ay, { + megaChat: this, + routingSection: this.routingSection, + routingSubSection: this.routingSubSection, + routingParams: this.routingParams + })); + this.onChatsHistoryReady().then(() => { + const room = this.getCurrentRoom(); + if (room) { + room.scrollToChat(); } - return Math.max(1, Math.floor(calculated.contentWidth / calculated.itemWidth)); - }); - lazy(calculated, 'contentHeight', () => { - return Math.ceil(this.props.entries.length / calculated.itemsPerRow) * calculated.itemHeight; - }); - lazy(calculated, 'scrollLeft', () => { - return this.domRef.getScrollPositionX(); - }); - lazy(calculated, 'scrollTop', () => { - if (this.adapterChangedDoRepaint) { - return 0; + return room; + }).dump('on-chat-history-loaded'); + this.is_initialized = true; + this.registerUploadListeners(); + this.trigger('onInit'); + mBroadcaster.sendMessage('chat_initialized'); + setInterval(this.removeMessagesByRetentionTime.bind(this, null), 2e4); + this.autoJoinIfNeeded(); + const scheduledMeetings = Object.values(Chat.mcsm); + if (scheduledMeetings && scheduledMeetings.length) { + for (let i = scheduledMeetings.length; i--;) { + const scheduledMeeting = scheduledMeetings[i]; + this.plugins.meetingsManager.attachMeeting(scheduledMeeting); + delete Chat.mcsm[scheduledMeeting.id]; } - return this.domRef.getScrollPositionY(); - }); - lazy(calculated, 'scrolledPercentX', () => { - return 100 / calculated.scrollWidth * calculated.scrollLeft; - }); - lazy(calculated, 'scrolledPercentY', () => { - return 100 / calculated.scrollHeight * calculated.scrollTop; - }); - lazy(calculated, 'isAtTop', () => { - return calculated.scrollTop === 0; - }); - lazy(calculated, 'isAtBottom', () => { - return calculated.scrollTop === calculated.scrollHeight; - }); - lazy(calculated, 'itemsPerPage', () => { - return Math.ceil(calculated.scrollHeight / calculated.itemHeight) * calculated.itemsPerRow; - }); - lazy(calculated, 'visibleFirstItemNum', () => { - let value = 0; - value = Math.floor(Math.floor(calculated.scrollTop / calculated.itemHeight) * calculated.itemsPerRow); - if (value > 0) { - value = Math.max(0, value - this.options.extraRows * calculated.itemsPerRow); + } + if (notify) { + notify.countAndShowNewNotifications(); + } + return true; + }).then(resolve).catch(reject); +}); +Chat.prototype.showUpgradeDialog = function () { + return is_extension ? msgDialog('warningb', l[1900], l[8841]) : msgDialog('confirmation', l[1900], l[8840], '', cb => cb && location.reload()); +}; +Chat.prototype._syncChats = function () { + if (!this.is_initialized) { + return; + } + this.plugins.meetingsManager.checkForNotifications(); + const { + chats, + logger + } = this; + if (chats && chats.length) { + chats.forEach(({ + chatId, + scheduledMeeting + }) => { + const dnd = pushNotificationSettings.getDnd(chatId); + if (dnd && dnd < unixtime()) { + pushNotificationSettings.disableDnd(chatId); + if (logger) { + logger.debug(`Chat.prototype._syncDnd chatId=${chatId}`); + } } - return value; - }); - lazy(calculated, 'visibleLastItemNum', () => { - let value = Math.min(this.props.entries.length, Math.ceil(Math.ceil(calculated.scrollTop / calculated.itemHeight) * calculated.itemsPerRow + calculated.itemsPerPage)); - if (value < this.props.entries.length) { - value = Math.min(this.props.entries.length, value + this.options.extraRows * calculated.itemsPerRow); + const { + isUpcoming, + chatRoom, + id + } = scheduledMeeting || {}; + if (isUpcoming) { + scheduledMeeting.setNextOccurrence(); + chatRoom.trackDataChange(); + if (logger) { + logger.debug(`Chat.prototype.__syncScheduledMeetings id=${id} chatId=${chatId}`); + } } - return value; - }); - if (this.options.batchPages > 0) { - const perPage = calculated.itemsPerPage; - const visibleF = calculated.visibleFirstItemNum; - calculated.visibleFirstItemNum = Math.max(0, ((visibleF - visibleF % perPage) / perPage - 1 - this.options.batchPages) * perPage); - const visibleL = calculated.visibleLastItemNum; - calculated.visibleLastItemNum = Math.min(this.props.entries.length, ((visibleL - visibleL % perPage) / perPage + 1 + this.options.batchPages) * perPage); - } - Object.defineProperty(M, 'rmItemsInView', { - get: () => { - const c = this.domRef && this._calculated || !1; - return c.itemsPerPage + c.itemsPerRow | 0; - }, - configurable: true }); } - _contentUpdated() { - this._calculated = false; - this._recalculate(); - if (this.listContent && this._lastContentHeight !== this._calculated.contentHeight) { - this._lastContentHeight = this._calculated.contentHeight; - this.listContent.style.height = `${this._calculated.contentHeight }px`; - } - if (this.domRef && this._calculated.scrollHeight + this._calculated.scrollTop > this._calculated.contentHeight) { - this.domRef.scrollToY(this._calculated.contentHeight - this._calculated.scrollHeight); - } - if (this.listAdapterInstance && this.listAdapterInstance.onContentUpdated) { - this.listAdapterInstance.onContentUpdated(); +}; +Chat.prototype.loadChatUIFlagsFromConfig = function (val) { + let hadChanged = false; + let flags = val || mega.config.get("cUIF"); + if (flags) { + if (typeof flags !== 'object') { + flags = {}; } + Object.keys(CHATUIFLAGS_MAPPING).forEach(k => { + const v = flags[CHATUIFLAGS_MAPPING[k]]; + hadChanged = v !== undefined && this.chatUIFlags.set(k, v) !== false || hadChanged; + }); } - _getCalcsThatTriggerChange() { - return [this.props.entries.length, this._calculated.scrollHeight, this._calculated.itemWidth, this._calculated.itemHeight, this._calculated.contentWidth, this._calculated.itemsPerRow, this._calculated.contentHeight, this._calculated.visibleFirstItemNum, this._calculated.visibleLastItemNum]; + return hadChanged; +}; +Chat.prototype.cleanup = function (clean) { + const room = this.getCurrentRoom(); + if (room) { + room.hide(); } - indexOfEntry(nodeHandle, prop) { - prop = prop || 'h'; - for (let i = 0; i < this.props.entries.length; i++) { - const entry = this.props.entries[i]; - if (entry[prop] === nodeHandle) { - return i; - } - } - return -1; + M.chat = false; + this.routingParams = null; + this.routingSection = null; + this.routingSubSection = null; + if (clean) { + M.currentdirid = page = false; } - scrollToItem(nodeHandle) { - const elementIndex = this.indexOfEntry(nodeHandle); - if (elementIndex === -1) { - return false; - } - let shouldScroll = false; - const itemOffsetTop = Math.floor(elementIndex / this._calculated.itemsPerRow) * this._calculated.itemHeight; - const itemOffsetTopPlusHeight = itemOffsetTop + this._calculated.itemHeight; - if (itemOffsetTop < this._calculated.scrollTop || itemOffsetTopPlusHeight > this._calculated.scrollTop + this._calculated.scrollHeight) { - shouldScroll = true; - } - if (shouldScroll) { - this.domRef.scrollToY(itemOffsetTop); - onIdle(() => { - this.safeForceUpdate(); - }); - return true; +}; +Chat.prototype.initChatUIFlagsManagement = function () { + const self = this; + self.loadChatUIFlagsFromConfig(); + this.chatUIFlags.addChangeListener((hashmap, extraArg) => { + const flags = mega.config.get("cUIF") || {}; + let hadChanged = false; + let hadLocalChanged = false; + Object.keys(CHATUIFLAGS_MAPPING).forEach((k) => { + if (flags[CHATUIFLAGS_MAPPING[k]] !== self.chatUIFlags[k]) { + if (extraArg === 0xDEAD) { + self.chatUIFlags._data[k] = flags[CHATUIFLAGS_MAPPING[k]]; + hadLocalChanged = true; + } else { + flags[CHATUIFLAGS_MAPPING[k]] = self.chatUIFlags[k]; + hadChanged = true; + } + } + }); + if (hadLocalChanged) { + if (extraArg !== 0xDEAD) { + self.chatUIFlags.trackDataChange(0xDEAD); + } + $.tresizer(); } - return false; - } - onPsUserScroll() { - if (!this.isMounted()) { + if (extraArg === 0xDEAD) { return; } - const oldCalc = JSON.stringify(this._getCalcsThatTriggerChange()); - this._contentUpdated(); - const newCalc = JSON.stringify(this._getCalcsThatTriggerChange()); - if (oldCalc !== newCalc) { - this.forceUpdate(); - } - } - onResizeDoUpdate() { - super.onResizeDoUpdate(); - this._contentUpdated(); - } - componentDidMount() { - super.componentDidMount(); - this._contentUpdated(); - this.forceUpdate(); - } - componentDidUpdate() { - super.componentDidUpdate(); - this._contentUpdated(); - if (this.adapterChangedDoRepaint) { - this.adapterChangedDoRepaint = false; - this._calculated = false; - this._recalculate(); - } - if (this.thumbsThatRequireLoading.size) { - delay('chat:mega-list2:thumb-loader', () => this.enqueueThumbnailRetrieval(), 20); + if (hadChanged) { + mega.config.set("cUIF", flags); } - this._firstRender = this._firstRender || this.props.viewmode !== M.viewmode; - if (this._firstRender && this.domRef) { - let _this$domRef; - this._firstRender = false; - Ps.update((_this$domRef = this.domRef) == null ? void 0 : _this$domRef.$Node); + }); + this.mbListeners.push(mBroadcaster.addListener('fmconfig:cUIF', tryCatch(v => { + if (self.loadChatUIFlagsFromConfig(v)) { + self.chatUIFlags.trackDataChange(0xDEAD); } + })), mBroadcaster.addListener('statechange', state => { + this.trigger('viewstateChange', state); + })); +}; +Chat.prototype.unregisterUploadListeners = function (destroy) { + 'use strict'; + + const self = this; + mBroadcaster.removeListener(self._uplDone); + mBroadcaster.removeListener(self._uplError); + mBroadcaster.removeListener(self._uplAbort); + mBroadcaster.removeListener(self._uplFAError); + mBroadcaster.removeListener(self._uplFAReady); + if (destroy) { + mBroadcaster.removeListener(self._uplStart); } - enqueueThumbnailRetrieval() { - const loaders = new Map(this.thumbsLoadingHandlers); - const nodes = new Map(this.thumbsThatRequireLoading); - const pending = []; - const defaultCallback = (n, src, id) => { - let img = document.getElementById(id || `chat_${n.h}`); - if (img && (img = img.querySelector('img'))) { - let _img$parentNode$paren; - img.src = src; - (_img$parentNode$paren = img.parentNode.parentNode) == null || _img$parentNode$paren.classList.add('thumb'); + delete self._uplError; +}; +Chat.prototype.registerUploadListeners = function () { + const self = this; + const logger = d && MegaLogger.getLogger('chatUploadListener', false, self.logger); + const ufo = Object.create(null); + self.unregisterUploadListeners(true); + const forEachChat = function (chats, callback) { + let result = 0; + if (!Array.isArray(chats)) { + chats = [chats]; + } + for (let i = chats.length; i--;) { + const room = self.getRoomFromUrlHash(chats[i]); + if (room) { + callback(room, ++result); } - }; - const setSource = n => { - if (thumbnails.has(n.fa)) { - const src = thumbnails.get(n.fa); - const batch = [...nodes.get(n.fa)]; - for (let i = batch.length; i--;) { - const n = batch[i]; - const handlers = [...loaders.get(n.h)]; - for (let i = handlers.length; i--;) { - let callback = handlers[i]; - if (typeof callback !== 'function') { - callback = defaultCallback; - } - tryCatch(() => { - const id = callback(n, src); - if (id) { - defaultCallback(n, src, id); - } - })(); - } - } - return true; + } + return result; + }; + const lookupPendingUpload = function (id) { + console.assert((id | 0) > 0 || String(id).length === 8, 'Invalid lookupPendingUpload arguments...'); + for (const uid in ulmanager.ulEventData) { + if (ulmanager.ulEventData[uid].faid === id || ulmanager.ulEventData[uid].h === id) { + return uid; } - }; - for (const [, [n]] of nodes) { - if (!setSource(n)) { - pending.push(n); + } + }; + const unregisterListeners = function () { + if (!$.len(ulmanager.ulEventData)) { + self.unregisterUploadListeners(); + if (d) { + logger.warn('Revoked upload listeners.'); } } - if (pending.length) { - fm_thumbnails('standalone', pending, setSource); + }; + const onUploadComplete = function (ul) { + if (ulmanager.ulEventData[ul && ul.uid]) { + forEachChat(ul.chat, (room) => { + if (d) { + logger.debug('Attaching node[%s] to chat room[%s]...', ul.h, room.chatId, ul.uid, ul, M.d[ul.h]); + } + room.attachNodes([ul.h]).catch(dump); + }); + delete ulmanager.ulEventData[ul.uid]; + unregisterListeners(); + } else if (d) { + logger.warn('could not complete upload...', ul); } - this.thumbsLoadingHandlers.clear(); - this.thumbsThatRequireLoading.clear(); - } - requestThumbnailCb(node, immediate, callback) { - if (node && node.fa) { - if (typeof immediate === 'function') { - callback = immediate; - immediate = 0; + }; + const onUploadCompletion = function (uid, handle, faid, chat) { + if (!chat) { + if (d > 1) { + logger.debug('ignoring upload:completion that is unrelated to chat.', arguments); } - node.seen = node.seen || -7; - this.thumbsLoadingHandlers.set(node.h, callback); - this.thumbsThatRequireLoading.set(node.fa, node); - delay('chat:mega-list2:thumb-loader', () => this.enqueueThumbnailRetrieval(), immediate || 480); + return; } - } - render() { - if (this.isMounted() && !this._calculated) { - this._recalculate(); + const n = M.getNodeByHandle(handle); + const ul = ulmanager.ulEventData[uid] || false; + if (d) { + logger.info('upload:completion', uid, handle, faid, ul, n); } - const { - listAdapter, - listAdapterOpts, - entries, - nodeAdapterProps, - viewMode, - header, - onContextMenu - } = this.props; - const className = `${listAdapter.containerClassName } megaList megaList2`; - const first = this._calculated.visibleFirstItemNum; - const last = this._calculated.visibleLastItemNum; - const nodes = []; - for (let i = first; i < last; i++) { - const node = entries[i]; - nodes.push(REaCt().createElement(this.props.nodeAdapter, (0,esm_extends.A)({ - key: `${i }_${ node[this.props.keyProp]}`, - h: node[this.props.keyProp], - index: i, - megaList: this, - listAdapter, - node, - calculated: this._calculated, - listAdapterOpts, - onContextMenu, - selected: this.props.selected ? this.props.selected.indexOf(node[this.props.keyProp]) > -1 : false, - highlighted: this.props.highlighted ? this.props.highlighted.indexOf(node[this.props.keyProp]) > -1 : false, - requestThumbnailCb: this.requestThumbnailCb, - keyProp: this.props.keyProp || 'h' - }, nodeAdapterProps))); - } - const listAdapterName = listAdapter.prototype.constructor.name; - return REaCt().createElement(REaCt().Fragment, null, REaCt().createElement(perfectScrollbar.O, { - key: `ps_${ listAdapterName }_${ viewMode}`, - options: this.options.perfectScrollOptions, - onUserScroll: this.onPsUserScroll, - className, - style: { - 'position': 'relative' - }, - ref: instance => { - this.domRef = instance; - } - }, REaCt().createElement(this.props.listAdapter, (0,esm_extends.A)({ - containerClassName: this.props.containerClassName, - key: `ps_${ listAdapterName }_${ this.props.viewMode }_la`, - ref: listAdapterInstance => { - this.listAdapterInstance = listAdapterInstance; - }, - listContentRef: listContent => { - this.listContent = listContent; - }, - header, - megaList: this, - calculated: this._calculated - }, listAdapterOpts), nodes))); - } -}, (0,applyDecoratedDescriptor.A)(_class.prototype, "onPsUserScroll", [_dec], Object.getOwnPropertyDescriptor(_class.prototype, "onPsUserScroll"), _class.prototype), _class); -// EXTERNAL MODULE: ./js/ui/jsx/fm/nodes/genericNodePropsComponent.jsx + 1 modules -const genericNodePropsComponent = REQ_(984); -;// ./js/ui/jsx/fm/nodes/genericGrid.jsx - - -class GenericGrid extends genericNodePropsComponent.B { - render() { - const { - node, - calculated, - index, - listAdapter, - className, - keyProp - } = this.props; - const style = {}; - listAdapter.repositionItem(node, calculated, index, style); - const toApplySensitive = !!mega.sensitives.isSensitive(node) && (mega.sensitives.showGlobally ? 1 : 2); - let image = null; - let src = null; - let isThumbClass = ""; - if (node.fa && (is_image2(node) || is_video(node))) { - src = thumbnails.get(node.fa); - if (!src) { - this.props.requestThumbnailCb(node); - src = window.noThumbURI || ''; - } - image = src ? REaCt().createElement("img", { - alt: "", - src - }) : REaCt().createElement("img", { - alt: "" - }); - isThumbClass = " thumb"; + if (!ul) { + if (d) { + logger.error('Upload event data store missing...', uid, n, ul); + } } else { - image = REaCt().createElement("img", null); - } - let fileStatusClass = ""; - if (node.fav) { - fileStatusClass += " icon-favourite-filled"; - } - return REaCt().createElement("a", { - className: `data-block-view megaListItem ui-droppable ui-draggable ui-draggable-handle ${ this.nodeProps.classNames.join(" ") }${className && className(node) || "" }${toApplySensitive ? toApplySensitive === 1 ? ' is-sensitive' : ' hidden-as-sensitive' : ''}`, - id: `chat_${ node[keyProp]}`, - onClick: e => { - this.props.onClick(e, this.props.node); - }, - onDoubleClick: e => { - this.props.onDoubleClick(e, this.props.node); - }, - title: this.nodeProps.title, - style - }, REaCt().createElement("span", { - className: `data-block-bg ${ isThumbClass}` - }, REaCt().createElement("span", { - className: "data-block-indicators" - }, REaCt().createElement("span", { - className: `file-status-icon indicator sprite-fm-mono${ fileStatusClass}` - }), REaCt().createElement("span", { - className: "versioning-indicator" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-versions-previous" - })), REaCt().createElement("i", { - className: "sprite-fm-mono icon-link" - })), REaCt().createElement("span", { - className: `item-type-icon-90 icon-${ this.nodeProps.icon }-90` - }, image), REaCt().createElement("div", { - className: "video-thumb-details" - }, REaCt().createElement("i", { - className: "small-icon small-play-icon" - }), REaCt().createElement("span", null, "00:00"))), REaCt().createElement("span", { - className: "file-block-title" - }, this.nodeProps.title)); - } -} -;// ./js/ui/jsx/fm/nodes/genericTable.jsx - - - -class GenericTableHeader extends mixins.w9 { - constructor(...args) { - super(...args); - this.domRef = REaCt().createRef(); - } - render() { - const { - sortBy, - columns - } = this.props; - const columnsRendered = []; - for (let i = 0; i < columns.length; i++) { - var _colProps; - let col = columns[i]; - let colProps; - if (Array.isArray(col)) { - colProps = col[1]; - col = col[0]; - } - let sortable; - if (col.sortable) { - let classes = ""; - if (sortBy[0] === col.id) { - const ordClass = sortBy[1] === "desc" ? "icon-arrow-down" : "icon-arrow-up"; - classes = `${classes} ${ordClass}`; - } - if (col.id === 'fav') { - classes += ' hidden'; + ul.h = handle; + if (ul.efa && !n) { + if (d) { + logger.error('Invalid state, efa set on deduplication?', ul.efa, ul); } - sortable = REaCt().createElement("i", { - className: `sprite-fm-mono ${col.id} ${classes}` - }); - } - columnsRendered.push(REaCt().createElement("th", { - megatype: col.megatype, - className: col.headerClassName || col.megatype || "", - key: `${col.id }_${ i}`, - onClick: e => { - e.preventDefault(); - if (col.sortable) { - this.props.onClick(col.id); - } + ul.efa = 0; + } else if (ufo[faid]) { + if (d) { + logger.info(`Recovering fa:error state for ${faid}`, ufo[faid], ul.efa); } - }, REaCt().createElement("span", null, ((_colProps = colProps) == null ? void 0 : _colProps.label) || col.label), col.icon && REaCt().createElement("i", { - className: `sprite-fm-mono ${ col.icon}` - }), sortable)); - } - return REaCt().createElement("thead", { - ref: this.domRef - }, REaCt().createElement("tr", null, columnsRendered)); - } -} -class GenericTable extends genericNodePropsComponent.B { - render() { - let _this$nodeProps; - const { - node, - index, - listAdapterOpts, - className, - keyProp - } = this.props; - const toApplySensitive = !!mega.sensitives.isSensitive(node) && (mega.sensitives.showGlobally ? 1 : 2); - const columns = []; - for (let i = 0; i < listAdapterOpts.columns.length; i++) { - const customColumn = listAdapterOpts.columns[i]; - if (Array.isArray(customColumn)) { - columns.push(REaCt().createElement(customColumn[0], { - ...customColumn[1], - 'nodeAdapter': this, - 'h': node[keyProp], - node, - 'key': `${i }_${ customColumn[0].prototype.constructor.name}`, - keyProp - })); - } else { - columns.push(REaCt().createElement(customColumn, { - 'nodeAdapter': this, - 'h': node[keyProp], - node, - 'key': `${i }_${ customColumn.prototype.constructor.name}`, - keyProp - })); + ul.efa = Math.max(0, ul.efa - ufo[faid]) | 0; } - } - const listClassName = listAdapterOpts.className; - return REaCt().createElement("tr", { - className: `node_${ node[keyProp] } ${ className && className(node) || "" } ${ listClassName && listClassName(node) || "" } ${ (_this$nodeProps = this.nodeProps) == null ? void 0 : _this$nodeProps.classNames.join(" ") }${toApplySensitive ? toApplySensitive === 1 ? ' is-sensitive' : ' hidden-as-sensitive' : ''}`, - id: node[keyProp], - onContextMenu: ev => { - if (this.props.onContextMenu) { - this.props.onContextMenu(ev, node[keyProp]); - } - }, - onClick: e => { - this.props.onClick(e, this.props.node); - }, - onDoubleClick: e => { - this.props.onDoubleClick(e, this.props.node); - }, - key: `${index }_${ node[keyProp]}` - }, columns); - } -} -;// ./js/ui/jsx/megaList/adapters.jsx - - -class GenericListAdapter extends mixins.w9 { - constructor(...args) { - super(...args); - this.customIsEventuallyVisible = true; - } -} -class Grid extends GenericListAdapter { - static repositionItem(node, calculated, index, style) { - style.position = "absolute"; - style.top = calculated.itemHeight * Math.floor(index / calculated.itemsPerRow); - if (calculated.itemsPerRow > 1) { - style.left = index % calculated.itemsPerRow * calculated.itemWidth; - } - } - render() { - return REaCt().createElement("div", { - className: "megaList-content", - ref: this.props.listContentRef, - style: { - 'position': 'relative' + if (ul.efa && (!n.fa || String(n.fa).split('/').length < ul.efa)) { + console.assert(!ul.faid || ul.faid === faid, `${faid} != ${ul.faid}`); + ul.faid = faid; + if (d) { + logger.info('Waiting for file attribute to arrive.', handle, ul.efa, n.fa, n.name, ul, [n]); + } + } else { + onUploadComplete(ul); } - }, this.props.children); - } -} -Grid.itemWidth = 212; -Grid.itemHeight = 212; -Grid.containerClassName = "file-block-scrolling megaListContainer"; -class Table extends GenericListAdapter { - onContentUpdated() { - const { - calculated - } = this.props; - const pusherHeight = calculated.visibleFirstItemNum * calculated.itemHeight | 0; - if (this.topPusher) { - this.topPusher.style.height = `${pusherHeight }px`; } - if (this.bottomPusher) { - this.bottomPusher.style.height = `${calculated.contentHeight - pusherHeight - (calculated.visibleLastItemNum - calculated.visibleFirstItemNum) * calculated.itemHeight | 0 }px`; + }; + const onUploadError = function (uid, error) { + const ul = ulmanager.ulEventData[uid]; + if (d) { + logger.debug(error === -0xDEADBEEF ? 'upload:abort' : 'upload.error', uid, error, [ul]); } - } - componentDidUpdate() { - super.componentDidUpdate(); - this.onContentUpdated(); - } - render() { - return REaCt().createElement("table", { - width: "100%", - className: this.props.containerClassName || "grid-table table-hover fm-dialog-table" - }, this.props.header, REaCt().createElement("tbody", { - ref: this.props.listContentRef - }, REaCt().createElement("tr", { - className: "megalist-pusher top", - ref: r => { - this.topPusher = r; - } - }), this.props.children, REaCt().createElement("tr", { - className: "megalist-pusher bottom", - ref: r => { - this.bottomPusher = r; + if (ul) { + delete ulmanager.ulEventData[uid]; + unregisterListeners(); + } else if (d) { + logger.warn(`No upload association for #${uid}`, error); + } + }; + const onAttributeReady = function (handle, fa) { + delay(`chat:fa-ready:${ handle}`, () => { + const uid = lookupPendingUpload(handle); + const ul = ulmanager.ulEventData[uid] || false; + if (d) { + logger.debug('fa:ready', handle, fa, ul.efa, uid, ul); + } + if (ul.h && String(fa).split('/').length >= ul.efa) { + onUploadComplete(ul); + } else if (d) { + logger.debug('Not enough file attributes yet, holding...', handle, fa, ul.efa, ul); + } + }); + }; + const onAttributeError = function (faid, error, onStorageAPIError, nFAiled) { + const uid = lookupPendingUpload(faid); + const ul = ulmanager.ulEventData[uid] || false; + if (d) { + logger.debug('fa:error', faid, error, onStorageAPIError, uid, ul, nFAiled, ul.efa); + } + if (ul) { + ul.efa = Math.max(0, ul.efa - nFAiled) | 0; + if (ul.h) { + const n = M.getNodeByHandle(ul.h); + if (!ul.efa || n.fa && String(n.fa).split('/').length >= ul.efa) { + onUploadComplete(ul); + } } - }))); - } -} -Table.itemHeight = 32; -Table.itemsPerRow = 1; -Table.containerClassName = "grid-scrolling-table megaListContainer"; -// EXTERNAL MODULE: ./js/ui/jsx/fm/nodes/columns/columnFavIcon.jsx -const columnFavIcon = REQ_(161); -;// ./js/ui/tooltips.jsx -const React = REQ_(594); - -class Handler extends React.Component { - render() { - const { - className, - onMouseOver, - onMouseOut, - children - } = this.props; - return React.createElement("span", { - className: ` - tooltip-handler - ${className || ''} - `, - onMouseOver, - onMouseOut - }, children); - } -} -class Contents extends React.Component { - render() { - let className = `tooltip-contents dropdown body tooltip ${ this.props.className ? this.props.className : ""}`; - if (this.props.active) { - className += " visible"; - return React.createElement("div", { - className - }, this.props.withArrow ? React.createElement("i", { - className: "dropdown-white-arrow" - }) : null, this.props.children); } else { - return null; + if (d) { + logger.warn(`No upload association for ${faid} (yet?)`, nFAiled, error); + } + ufo[faid] = (ufo[faid] | 0) + nFAiled; } - } -} -class Tooltip extends mixins.w9 { - constructor(props) { - super(props); - this.domRef = React.createRef(); - this.state = { - 'active': false - }; - } - componentDidUpdate(oldProps, oldState) { - const self = this; - if (oldState.active === true && this.state.active === false) { - chatGlobalEventManager.removeEventListener('resize', `tooltip${ this.getUniqueId()}`); + }; + const registerLocalListeners = function () { + self._uplError = mBroadcaster.addListener('upload:error', onUploadError); + self._uplAbort = mBroadcaster.addListener('upload:abort', onUploadError); + self._uplFAReady = mBroadcaster.addListener('fa:ready', onAttributeReady); + self._uplFAError = mBroadcaster.addListener('fa:error', onAttributeError); + self._uplDone = mBroadcaster.addListener('upload:completion', onUploadCompletion); + }; + self._uplStart = mBroadcaster.addListener('upload:start', (data) => { + if (d) { + logger.info('onUploadStart', [data]); } - if (self.state.active === true) { - self.repositionTooltip(); - chatGlobalEventManager.addEventListener('resize', `tooltip${ this.getUniqueId()}`, () => { - self.repositionTooltip(); - }); - if (this.props.onShown) { - this.props.onShown(); + const notify = function (room) { + room.onUploadStart(data); + }; + for (const k in data) { + const chats = data[k].chat; + if (chats && forEachChat(chats, notify) && !self._uplError) { + registerLocalListeners(); } } + }); +}; +Chat.prototype.getRoomFromUrlHash = function (urlHash) { + if (urlHash.indexOf("#") === 0) { + urlHash = urlHash.subtr(1, urlHash.length); } - repositionTooltip() { - let _this$domRef; - let elLeftPos, elTopPos, elWidth, elHeight; - let tooltipLeftPos, tooltipTopPos, tooltipWidth, tooltipHeight; - let docHeight; - let arrowClass; - if (!this.isMounted()) { + if (urlHash.indexOf("chat/g/") > -1 || urlHash.indexOf("chat/c/") > -1) { + var foundRoom = null; + urlHash = urlHash.replace("chat/g/", "").replace("chat/c/", ""); + megaChat.chats.forEach((room) => { + if (!foundRoom && room.chatId === urlHash) { + foundRoom = room; + } + }); + return foundRoom; + } else if (urlHash.indexOf("chat/p/") > -1) { + const contactHash = urlHash.replace("chat/p/", ""); + if (!contactHash) { return; } - const $container = $((_this$domRef = this.domRef) == null ? void 0 : _this$domRef.current); - const $el = $('.tooltip-handler', $container); - const $tooltip = $('.tooltip-contents', $container); - let {tooltipOffset} = this.props; - const arrow = this.props.withArrow; - if ($el && $tooltip) { - elWidth = $el.outerWidth(); - elHeight = $el.outerHeight(); - elLeftPos = $el.offset().left; - elTopPos = $el.offset().top; - tooltipWidth = $tooltip.outerWidth(); - tooltipHeight = $tooltip.outerHeight(); - docHeight = $(window).height(); - $tooltip.removeClass('dropdown-arrow left-arrow right-arrow up-arrow down-arrow').removeAttr('style'); - if (!tooltipOffset) { - tooltipOffset = 7; - } - if (elTopPos - tooltipHeight - tooltipOffset > 10) { - tooltipLeftPos = elLeftPos + elWidth / 2 - tooltipWidth / 2; - tooltipTopPos = elTopPos - tooltipHeight - tooltipOffset; - arrowClass = arrow ? 'dropdown-arrow down-arrow' : ''; - } else if (docHeight - (elTopPos + elHeight + tooltipHeight + tooltipOffset) > 10) { - tooltipLeftPos = elLeftPos + elWidth / 2 - tooltipWidth / 2; - tooltipTopPos = elTopPos + elHeight + tooltipOffset; - arrowClass = arrow ? 'dropdown-arrow up-arrow' : ''; - } else if (elLeftPos - tooltipWidth - tooltipOffset > 10) { - tooltipLeftPos = elLeftPos - tooltipWidth - tooltipOffset; - tooltipTopPos = elTopPos + elHeight / 2 - tooltipHeight / 2; - arrowClass = arrow ? 'dropdown-arrow right-arrow' : ''; - } else { - tooltipLeftPos = elLeftPos + elWidth + tooltipOffset; - tooltipTopPos = elTopPos + elHeight / 2 - tooltipHeight / 2; - arrowClass = arrow ? 'dropdown-arrow left-arrow' : ''; + const chatRoom = this.getPrivateRoom(contactHash); + return chatRoom; + } else if (urlHash.indexOf("chat/") > -1 && urlHash[13] === "#") { + var foundRoom = null; + const pubHandle = urlHash.replace("chat/", "").split("#")[0]; + urlHash = urlHash.replace("chat/g/", ""); + const chatIds = megaChat.chats.keys(); + for (let i = 0; i < chatIds.length; i++) { + const cid = chatIds[i]; + const room = megaChat.chats[cid]; + if (room.publicChatHandle === pubHandle) { + foundRoom = room; + break; } - $tooltip.css({ - 'left': tooltipLeftPos, - 'top': tooltipTopPos - 5 - }); - $tooltip.addClass(arrowClass); } + return foundRoom; + } else { + return null; } - onHandlerMouseOver() { - this.setState({ - 'active': true - }); - } - onHandlerMouseOut() { - this.setState({ - 'active': false - }); - } - render() { - const self = this; - const others = []; - let handler = null; - let contents = null; - let x = 0; - React.Children.forEach(this.props.children, (child) => { - if (child.type.name === 'Handler') { - handler = React.cloneElement(child, { - onMouseOver () { - self.onHandlerMouseOver(); - }, - onMouseOut () { - self.onHandlerMouseOut(); - } - }); - } else if (child.type.name === 'Contents') { - contents = React.cloneElement(child, { - active: self.state.active, - withArrow: self.props.withArrow - }); - } else { - const tmp = React.cloneElement(child, { - key: x++ - }); - others.push(tmp); - } - }); - return React.createElement("span", { - ref: this.domRef, - className: this.props.className || '' - }, handler, contents, others); - } -} -Tooltip.defaultProps = { - 'hideable': true -}; -const tooltips = { - Tooltip, - Handler, - Contents }; -;// ./js/ui/jsx/fm/nodes/columns/columnNodeName.jsx - - - -class ColumnNodeName extends genericNodePropsComponent.B { - constructor(...args) { - super(...args); - this.state = { - src: null - }; - } - static get label() { - return l[86]; - } - componentDidMount() { - super.componentDidMount(); - } - render() { - const { - nodeAdapter - } = this.props; - const { - node, - requestThumbnailCb - } = nodeAdapter.props; - const src = this.state.src || thumbnails.get(node.fa); - return REaCt().createElement("td", { - megatype: ColumnNodeName.megatype - }, src || is_image2(node) || is_video(node) ? REaCt().createElement(tooltips.Tooltip, { - withArrow: true, - className: "tooltip-handler-container", - onShown: () => { - if (!src) { - requestThumbnailCb(node, true, (n, src) => { - this.setState({ - src - }); - return `preview_${n.h}`; - }); - } - } - }, REaCt().createElement(tooltips.Handler, { - className: `item-type-icon icon-${fileIcon(node)}-24` - }), REaCt().createElement(tooltips.Contents, { - className: "img-preview" - }, REaCt().createElement("div", { - className: "dropdown img-wrapper img-block", - id: `preview_${node.h}` - }, REaCt().createElement("img", { - alt: "", - className: `thumbnail-placeholder ${node.h}`, - src: node.fa || src ? src || `${staticpath}/images/mega/ajax-loader-tiny.gif` : window.noThumbURI - })))) : REaCt().createElement("span", { - className: ` - item-type-icon icon-${fileIcon(node)}-24 - ` - }), REaCt().createElement("span", { - className: "tranfer-filetype-txt" - }, nodeAdapter.nodeProps.title)); - } -} -ColumnNodeName.sortable = true; -ColumnNodeName.id = 'name'; -ColumnNodeName.megatype = 'fname'; -;// ./js/ui/jsx/fm/nodes/columns/columnSize.jsx - - -class ColumnSize extends genericNodePropsComponent.B { - static get label() { - return l[87]; - } - render() { - const { - nodeAdapter - } = this.props; - return REaCt().createElement("td", { - megatype: ColumnSize.megatype, - className: "size" - }, nodeAdapter.nodeProps.size); - } -} -ColumnSize.sortable = true; -ColumnSize.id = "size"; -ColumnSize.megatype = "size"; -;// ./js/ui/jsx/fm/nodes/columns/columnTimeAdded.jsx - - -class ColumnTimeAdded extends genericNodePropsComponent.B { - static get label() { - return l[16169]; - } - render() { - const { - nodeAdapter - } = this.props; - return REaCt().createElement("td", { - megatype: ColumnTimeAdded.megatype, - className: "time ad" - }, nodeAdapter.nodeProps.timestamp); - } -} -ColumnTimeAdded.sortable = true; -ColumnTimeAdded.id = "ts"; -ColumnTimeAdded.megatype = "timeAd"; -;// ./js/ui/jsx/fm/nodes/columns/columnExtras.jsx - - -class ColumnExtras extends genericNodePropsComponent.B { - render() { - return REaCt().createElement("td", { - megatype: ColumnExtras.megatype, - className: "grid-url-field own-data extras-column" - }, REaCt().createElement("span", { - className: "versioning-indicator" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-versions-previous" - })), REaCt().createElement("i", { - className: "sprite-fm-mono icon-link" - })); - } -} -ColumnExtras.sortable = false; -ColumnExtras.id = "extras"; -ColumnExtras.label = ""; -ColumnExtras.megatype = "extras"; -ColumnExtras.headerClassName = "grid-url-header"; -;// ./js/ui/jsx/fm/browserEntries.jsx - - - - - - - - - - - -class BrowserEntries extends mixins.w9 { - constructor(props) { - super(props); - this.state = { - 'sortBy': props.sortBy || ['name', 'asc'] - }; - this.toggleSortBy = this.toggleSortBy.bind(this); - } - UNSAFE_componentWillMount() { - this.lastCharKeyPressed = false; - this.lastCharKeyIndex = -1; - } - componentDidMount() { - super.componentDidMount(); - this.bindEvents(); - } - componentWillUnmount() { - super.componentWillUnmount(); - this.unbindEvents(); +Chat.prototype.updateSectionUnreadCount = SoonFc(function () { + if (!this.is_initialized) { + return; } - componentDidUpdate(oldProps) { - if (oldProps.sortBy && (oldProps.sortBy[0] !== this.props.sortBy[0] || oldProps.sortBy[1] !== this.props.sortBy[1])) { - this.setState({ - 'sortBy': this.props.sortBy - }); + let unreadCount = 0; + const notificationsCount = { + unreadChats: 0, + unreadMeetings: 0, + unreadUpcoming: 0, + chatsCall: false, + meetingCall: false + }; + let havePendingCall = false; + let haveAdHocMessage = false; + this.chats.forEach(chatRoom => { + if (chatRoom.isArchived() || chatRoom.state === ChatRoom.STATE.LEFT) { + return; + } + const unreads = parseInt(chatRoom.messagesBuff.getUnreadCount(), 10); + unreadCount += unreads; + if (unreads) { + notificationsCount[chatRoom.isMeeting ? 'unreadMeetings' : 'unreadChats'] += unreads; + if (chatRoom.scheduledMeeting && chatRoom.scheduledMeeting.isUpcoming) { + notificationsCount.unreadUpcoming += unreads; + } } - } - handleKeyNavigation(selectionManager, shiftKey, keyCode, viewMode) { - let curr; - const { - folderSelectNotAllowed - } = this.props; - if (shiftKey && folderSelectNotAllowed) { - curr = selectionManager.last_selected; - } - const {KEYS} = BrowserEntries; - if (viewMode) { - if (keyCode === KEYS.LEFT) { - selectionManager.select_prev(shiftKey, true); - } else if (keyCode === KEYS.RIGHT) { - selectionManager.select_next(shiftKey, true); - } else if (keyCode === KEYS.UP) { - selectionManager.select_grid_up(shiftKey, true); + if (chatRoom.havePendingCall() && chatRoom.uniqueCallParts && !chatRoom.uniqueCallParts[u_handle]) { + havePendingCall = true; + if (chatRoom.isMeeting) { + notificationsCount.meetingCall = true; + if (!chatRoom.scheduledMeeting && unreads) { + haveAdHocMessage = true; + } } else { - selectionManager.select_grid_down(shiftKey, true); + notificationsCount.chatsCall = true; } - } else if (keyCode === KEYS.UP) { - selectionManager.select_prev(shiftKey, true); + } + }); + unreadCount = unreadCount > 9 ? "9+" : unreadCount; + if (!is_chatlink && mega.ui.header) { + if (notificationsCount.unreadChats || notificationsCount.unreadUpcoming || haveAdHocMessage) { + mega.ui.header.chatsButton.addClass('decorated'); } else { - selectionManager.select_next(shiftKey, true); + mega.ui.header.chatsButton.removeClass('decorated'); } - if (shiftKey && folderSelectNotAllowed && $.selected.length > 1) { - const folderNodes = $.selected.filter(n => !M.isFileNode(M.getNodeByHandle(n))); - if (folderNodes.length > 1) { - if (!M.isFileNode(M.getNodeByHandle(curr))) { - array.remove(folderNodes, curr); - } - if (folderNodes.length) { - const newCurr = selectionManager.last_selected; - for (let i = 0; i < folderNodes.length; i++) { - selectionManager.remove_from_selection(folderNodes[i]); - } - if (M.isFileNode(M.getNodeByHandle(newCurr))) { - selectionManager.set_currently_selected(curr); - } else if (curr && $.selected.includes(curr)) { - selectionManager.set_currently_selected(curr); - } else if ($.selected.length) { - selectionManager.set_currently_selected($.selected[0]); - } - } - } + const phoneIcon = mega.ui.header.domNode.querySelector('.top-chats-call'); + if (phoneIcon) { + phoneIcon.classList[havePendingCall ? 'remove' : 'add']('hidden'); } } - _invalidKeydownTarget(e) { - return e.target && (e.target.tagName === 'INPUT' || e.target.tagName === 'BUTTON' || e.target.tagName === 'TEXTAREA' && !e.target.classList.contains('messages-textarea') || e.target.tagName === 'SELECT'); - } - _isNavigationKeyDown(e, keyCode) { - const { - KEYS - } = BrowserEntries; - const { - viewMode - } = this.props; - return !e.metaKey && (!viewMode && (keyCode === KEYS.UP || keyCode === KEYS.DOWN) || viewMode && (keyCode === KEYS.UP || keyCode === KEYS.DOWN || keyCode === KEYS.LEFT || keyCode === KEYS.RIGHT)); + if (this._lastUnreadCount !== unreadCount) { + this._lastUnreadCount = unreadCount; + notify.updateNotificationIndicator(); } - bindEvents() { - const { - KEYS - } = BrowserEntries; - $(document.body).rebind(`keydown.be${this.getUniqueId()}`, e => { - let charTyped = false; - const keyCode = e.which || e.keyCode; - const $searchField = $('div.fm-files-search input'); - const $typingArea = $('textarea.messages-textarea'); - const { - selectionManager, - viewMode - } = this.props; - if (this._invalidKeydownTarget(e)) { - return; - } - if ($searchField.is(':focus')) { - return; - } - if ($typingArea.is(':focus')) { - $typingArea.trigger('blur'); - } - if (keyCode === KEYS.A && (e.ctrlKey || e.metaKey)) { - this.handleSelectAll(); - e.preventDefault(); - e.stopPropagation(); - } else if (e.metaKey && keyCode === KEYS.UP || keyCode === KEYS.BACKSPACE) { - this.handleKeyBack(); - } else if (this._isNavigationKeyDown(e, keyCode)) { - this.handleKeyNavigation(selectionManager, e.shiftKey, keyCode, viewMode); - } else if (keyCode >= 48 && keyCode <= 57 || keyCode >= 65 && keyCode <= 123 || keyCode > 255) { - charTyped = String.fromCharCode(keyCode).toLowerCase(); - this.handleCharTyped(charTyped); - } else if (keyCode === KEYS.ENTER || e.metaKey && keyCode === KEYS.DOWN) { - this.handleAttach(); - } - mega.ui.mInfoPanel.reRenderIfVisible($.selected); - if (!charTyped) { - this.lastCharKeyPressed = false; - this.lastCharKeyIndex = -1; - } - }); + if (!this._lastNotifications || !shallowEqual(this._lastNotifications, notificationsCount)) { + this._lastNotifications = notificationsCount; + megaChat.trigger('onUnreadCountUpdate', notificationsCount); } - handleSelectAll() { - const { - selectionManager, - folderSelectNotAllowed, - entries - } = this.props; - selectionManager.select_all(); - if (folderSelectNotAllowed) { - const folders = entries.filter(h => !M.isFileNode(M.getNodeByHandle(h))); - for (let i = 0; i < folders.length; i++) { - selectionManager.remove_from_selection(folders[i].h); - } - } +}, 100); +Chat.prototype.dropAllDatabases = promisify(function (resolve, reject) { + const chatd = this.plugins.chatdIntegration.chatd || false; + const promises = []; + if (chatd.chatdPersist) { + promises.push(chatd.chatdPersist.drop()); } - handleKeyBack() { - const { - viewMode, - currentlyViewedEntry - } = this.props; - if (!viewMode) { - const currentFolder = M.getNode(currentlyViewedEntry); - if (currentFolder.p) { - this.expandFolder(currentFolder.p); - } - } + if ('messagesQueueKvStorage' in chatd) { + promises.push(chatd.messagesQueueKvStorage.destroy()); } - handleCharTyped(charTyped) { - const { - entries, - keyProp, - selectionManager - } = this.props; - const foundMatchingNodes = entries.filter(node => { - return node.name && node.name.substring(0, 1).toLowerCase() === charTyped; - }); - if (this.lastCharKeyPressed === charTyped) { - this.lastCharKeyIndex++; - } - this.lastCharKeyPressed = charTyped; - if (foundMatchingNodes.length > 0) { - if (!foundMatchingNodes[this.lastCharKeyIndex]) { - this.lastCharKeyIndex = 0; - } - const foundNode = foundMatchingNodes[this.lastCharKeyIndex]; - selectionManager.clear_selection(); - selectionManager.set_currently_selected(foundNode[keyProp], true); - } + if (Reactions.ready) { + promises.push(Reactions._db.destroy()); } - handleAttach() { - const { - highlighted, - folderSelectNotAllowed, - entries, - keyProp, - onAttachClicked - } = this.props; - let selectedNodes = highlighted; - if (folderSelectNotAllowed) { - selectedNodes = highlighted.filter(h => { - const node = entries.find(e => e[keyProp] === h); - return node && node.t === 0; - }); - if (selectedNodes.length === 0) { - const cursorNode = highlighted[0] && M.getNodeByHandle(highlighted[0]); - if (cursorNode.t === 1) { - this.expandFolder(cursorNode[keyProp]); - return; - } else if (highlighted.length > 0) { - this.expandFolder(highlighted[0]); - return; - } - return; - } - } - onAttachClicked(selectedNodes); + if (PersistedTypeArea.ready) { + promises.push(PersistedTypeArea._db.destroy()); } - unbindEvents() { - $(document.body).off(`keydown.be${ this.getUniqueId()}`); + Promise.allSettled(promises).then(resolve).catch(reject); +}); +Chat.prototype.destroy = function (isLogout) { + if (!this.is_initialized) { + return; } - onEntryClick(e, node) { - const { - selectionManager, - keyProp, - folderSelectNotAllowed, - highlighted = [] - } = this.props; - this.lastCharKeyPressed = false; - this.lastCharKeyIndex = -1; - e.stopPropagation(); - e.preventDefault(); - if (!e.shiftKey && !e.ctrlKey && !e.metaKey) { - selectionManager.clear_selection(); - selectionManager.set_currently_selected(node[keyProp]); - } else if (e.shiftKey) { - if ($.selected && $.selected.length) { - let selFolders; - if (folderSelectNotAllowed) { - selFolders = $.selected.filter(n => !M.isFileNode(M.getNodeByHandle(n))); - } - selectionManager.shift_select_to(node[keyProp], false, true, false); - if (folderSelectNotAllowed && $.selected.length > 1) { - const folderNodes = $.selected.filter(n => !M.isFileNode(M.getNodeByHandle(n))); - if (folderNodes.length > 1) { - array.remove(folderNodes, selFolders[0] || folderNodes[0]); - for (let i = 0; i < folderNodes.length; i++) { - selectionManager.remove_from_selection(folderNodes[i]); - } - } - } - } else { - selectionManager.set_currently_selected(node[keyProp]); - } - } else if (e.ctrlKey || e.metaKey) { - if (!highlighted || !highlighted.includes(node[keyProp])) { - if (folderSelectNotAllowed) { - if (node.t === 1 && highlighted.length > 0) { - return; - } else if (highlighted.some(nodeId => { - const node = M.getNodeByHandle(nodeId); - return node && node.t === 1; - })) { - selectionManager.clear_selection(); - } - } - selectionManager.add_to_selection(node[keyProp]); - } else if (highlighted && highlighted.includes(node[keyProp])) { - if (folderSelectNotAllowed) { - if (node.t === 1) { - return; - } else if (highlighted.some(nodeId => { - const node = M.getNodeByHandle(nodeId); - return node && node.t === 1; - })) { - selectionManager.clear(); - } - } - selectionManager.remove_from_selection(node[keyProp]); - } - } + this.isLoggingOut = isLogout; + for (let i = 0; i < this.mbListeners.length; i++) { + mBroadcaster.removeListener(this.mbListeners[i]); } - expandFolder(nodeId) { - const self = this; - const node = M.getNodeByHandle(nodeId); - if (node) { - self.lastCharKeyPressed = false; - self.lastCharKeyIndex = -1; - self.setState({ - 'selected': [], - 'highlighted': [], - 'cursor': false - }); - self.props.onExpand(node); - self.forceUpdate(); + this.unregisterUploadListeners(true); + this.trigger('onDestroy', [isLogout]); + tryCatch(() => this.$$root.unmount())(); + this.chats.forEach((chatRoom, chatId) => { + if (!isLogout) { + chatRoom.destroy(false, true); } + this.chats.remove(chatId); + }); + this.is_initialized = false; + if (this.plugins.chatdIntegration && this.plugins.chatdIntegration.chatd && this.plugins.chatdIntegration.chatd.shards) { + const { + shards + } = this.plugins.chatdIntegration.chatd; + Object.keys(shards).forEach(shard => shards[shard].connectionRetryManager.options.functions.forceDisconnect()); } - onEntryDoubleClick(e, node) { - const self = this; - self.lastCharKeyPressed = false; - self.lastCharKeyIndex = -1; - e.stopPropagation(); - e.preventDefault(); - const share = M.getNodeShare(node); - if (share && share.down) { - return; - } - if (node.t) { - self.props.onExpand(node); - self.forceUpdate(); - } else { - self.onEntryClick(e, node); - self.props.onAttachClicked(); + for (const pluginName in this.plugins) { + const plugin = this.plugins[pluginName]; + if (plugin.destroy) { + plugin.destroy(); } } - customIsEventuallyVisible() { - return true; - } - toggleSortBy(colId) { - const newState = {}; - if (this.state.sortBy[0] === colId) { - newState.sortBy = [colId, this.state.sortBy[1] === "asc" ? "desc" : "asc"]; - } else { - newState.sortBy = [colId, "asc"]; - } - this.setState(newState); - this.props.onSortByChanged(newState.sortBy); + if (this.minuteClockInterval) { + clearInterval(this.minuteClockInterval); } - render() { - const {viewMode} = this.props; - const listAdapterOpts = this.props.listAdapterOpts || {}; - if (!viewMode) { - listAdapterOpts.columns = [columnFavIcon.$, ColumnNodeName, ColumnSize, ColumnTimeAdded, ColumnExtras]; - } - if (this.props.listAdapterColumns) { - listAdapterOpts.columns = this.props.listAdapterColumns; - } - if (this.props.isLoading) { - return REaCt().createElement("div", { - className: "dialog-empty-block active dialog-fm folder" - }, REaCt().createElement("div", { - className: "dialog-empty-pad" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-cloud-drive" - }), REaCt().createElement("div", { - className: "dialog-empty-header" - }, l[5533]))); - } else if (!this.props.entries.length && this.props.currentlyViewedEntry === 'search') { - return REaCt().createElement("div", { - className: "dialog-empty-block active dialog-fm folder" - }, REaCt().createElement("div", { - className: "dialog-empty-pad" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-preview-reveal" - }), REaCt().createElement("div", { - className: "dialog-empty-header" - }, l[978]))); - } else if (!this.props.entries.length) { - const nilComp = this.props.NilComponent; - return nilComp && (typeof nilComp === "function" ? nilComp() : nilComp) || REaCt().createElement("div", { - className: "dialog-empty-block active dialog-fm folder" - }, this.props.currentlyViewedEntry === 'shares' ? REaCt().createElement("div", { - className: "dialog-empty-pad" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-folder-incoming-share-filled" - }), REaCt().createElement("div", { - className: "dialog-empty-header" - }, l[6871])) : REaCt().createElement("div", { - className: "dialog-empty-pad" - }, REaCt().createElement("i", { - className: "sprite-fm-mono icon-folder-filled" - }), REaCt().createElement("div", { - className: "dialog-empty-header" - }, this.props.currentlyViewedEntry === M.RootID ? l[1343] : M.u[this.props.currentlyViewedEntry] ? l[6787] : l[782]))); - } - return REaCt().createElement(MegaList2, { - viewMode, - sortBy: this.state.sortBy, - currentlyViewedEntry: this.props.currentlyViewedEntry, - selected: this.props.selected, - highlighted: this.props.highlighted, - containerClassName: this.props.containerClassName, - nodeAdapterProps: { - 'onClick': (e, node) => { - this.onEntryClick(e, node); - mega.ui.mInfoPanel.reRenderIfVisible($.selected); - }, - 'onDoubleClick': (e, node) => { - this.onEntryDoubleClick(e, node); - }, - 'className': node => { - return this.props.highlighted.indexOf(node[this.props.keyProp]) > -1 ? " ui-selected" : ""; - } - }, - ref: r => { - this.megaList = r; - }, - listAdapter: viewMode ? Grid : Table, - nodeAdapter: viewMode ? GenericGrid : GenericTable, - listAdapterOpts, - entries: this.props.entries, - itemHeight: this.props.megaListItemHeight, - headerHeight: viewMode ? 0 : 56, - header: !viewMode && REaCt().createElement(GenericTableHeader, { - columns: listAdapterOpts.columns, - sortBy: this.state.sortBy, - onClick: this.toggleSortBy, - headerContainerClassName: this.props.headerContainerClassName - }), - currentdirid: this.props.currentdirid, - onContextMenu: this.props.onContextMenu, - keyProp: this.props.keyProp - }); + if (megaChat.flyoutStartHolder && mega.ui.flyout) { + mega.ui.flyout.reinit(megaChat.flyoutStartHolder); + delete megaChat.flyoutStartHolder; } -} -BrowserEntries.KEYS = { - A: 65, - UP: 38, - DOWN: 40, - LEFT: 37, - RIGHT: 39, - ENTER: 13, - BACKSPACE: 8 -}; -BrowserEntries.defaultProps = { - 'hideable': true, - 'requiresUpdateOnResize': true }; -;// ./js/ui/jsx/fm/fmView.jsx - - - -class FMView extends mixins.w9 { - constructor(props) { - let _this$dataSource; - super(props); - this.domRef = REaCt().createRef(); - let initialSortBy = props.initialSortBy || ['name', 'asc']; - if (props.fmConfigSortEnabled) { - let _fmconfig$sortmodes; - const sortId = props.fmConfigSortId; - assert(sortId, 'missing fmConfigSortId'); - if ((_fmconfig$sortmodes = fmconfig.sortmodes) != null && (_fmconfig$sortmodes = _fmconfig$sortmodes[sortId]) != null && _fmconfig$sortmodes.n) { - let _fmconfig$sortmodes2; - initialSortBy = this._translateFmConfigSortMode((_fmconfig$sortmodes2 = fmconfig.sortmodes) == null ? void 0 : _fmconfig$sortmodes2[sortId]); - } - } - this.state = { - 'sortBy': initialSortBy, - 'selected': [], - 'highlighted': [], - 'entries': null - }; - this.dataSource = this.props.dataSource; - this.state.entries = this.getEntries(); - this.onAttachClicked = this.onAttachClicked.bind(this); - this.onContextMenu = this.onContextMenu.bind(this); - if ((_this$dataSource = this.dataSource) != null && _this$dataSource.addChangeListener) { - this._listener = this.dataSource.addChangeListener(() => { - if (!this.isMounted()) { - return; - } - this.setState({ - 'entries': this.getEntries() - }); - }); - } - this.initSelectionManager(); - } - getDataSourceNode(h) { - return this.dataSource && this.dataSource[h] || M.getNodeByHandle(h); - } - _translateFmConfigSortMode(currentSortModes) { - const sortId = this.props.fmConfigSortId; - assert(sortId, 'missing fmConfigSortId'); - const sortByArr = []; - if (currentSortModes != null && currentSortModes.n) { - sortByArr[0] = currentSortModes.n; - const sortMap = this.props.fmConfigSortMap; - const aliasKeys = sortMap && Object.keys(sortMap) || []; - for (const alias of aliasKeys) { - if (sortByArr[0] === sortMap[alias]) { - sortByArr[0] = alias; - break; - } - } - sortByArr[1] = currentSortModes.d === 1 ? "asc" : "desc"; +Chat.prototype.getContacts = function () { + const results = []; + M.u.forEach((k, v) => { + if (v.c == 1 || v.c == 2) { + results.push(v); } - return sortByArr; + }); + return results; +}; +Chat.prototype.userPresenceToCssClass = function (presence) { + if (presence === UserPresence.PRESENCE.ONLINE) { + return 'online'; + } else if (presence === UserPresence.PRESENCE.AWAY) { + return 'away'; + } else if (presence === UserPresence.PRESENCE.DND) { + return 'busy'; + } else if (presence === UserPresence.PRESENCE.OFFLINE) { + return 'offline'; + } else { + return 'black'; } - initSelectionManager(entries) { - this.selectionManager = new SelectionManager2_React(entries || this.state.entries, this.props.currentdirid || "cloud-drive", () => { - let _this$browserEntries; - return (_this$browserEntries = this.browserEntries) == null || (_this$browserEntries = _this$browserEntries.megaList) == null || (_this$browserEntries = _this$browserEntries._calculated) == null ? void 0 : _this$browserEntries.itemsPerRow; - }, nodeHandle => { - if (this.browserEntries && this.browserEntries.megaList) { - this.browserEntries.megaList.scrollToItem(nodeHandle); - } - }, { - 'onSelectedUpdated': selectedList => { - this.onSelectionUpdated(selectedList); - } - }); +}; +Chat.prototype._renderMyStatus = function () { + const self = this; + if (!self.is_initialized) { + return; } - onSelectionUpdated(selectedList) { - selectedList = [...selectedList]; - const highlighted = selectedList; - if (this.props.folderSelectNotAllowed && !this.props.folderSelectable) { - selectedList = selectedList.filter(nodeId => !this.getDataSourceNode(nodeId).t); - } - this.setState({ - 'selected': selectedList, - highlighted - }); - this.props.onSelected(selectedList); - this.props.onHighlighted(highlighted); - $.selected = highlighted; + if (typeof megaChat.userPresence === 'undefined') { + return; } - getEntries(newState) { - const self = this; - const sortBy = newState && newState.sortBy || self.state.sortBy; - const order = sortBy[1] === "asc" ? 1 : -1; - const entries = []; - let sortFunc, filterFunc, dataSource; - const minSearchLength = self.props.minSearchLength || 3; - const showSen = mega.sensitives.showGlobally; - if (self.props.currentlyViewedEntry === "search" && self.props.searchValue && self.props.searchValue.length >= minSearchLength) { - dataSource = this.dataSource || { - ...M.tnd, - ...M.d - }; - filterFunc = M.getFilterBySearchFn(self.props.searchValue); - } else { - const tmp = M.getChildren(self.props.currentlyViewedEntry) || M.tree[self.props.currentlyViewedEntry] || this.props.dataSource; - dataSource = Object.create(null); - for (const h in tmp) { - const n = this.getDataSourceNode(h); - if (n) { - dataSource[h] = n; - } - } - } - const { - customFilterFn - } = this.props; - for (const h in dataSource) { - const n = dataSource[h]; - const e = n && (!n.h || n.h.length === 8 && crypto_keyok(n) || n.h.length === 11); - const s = e && !n.fv && (showSen || !mega.sensitives.isSensitive(n)); - if (s && (!customFilterFn || customFilterFn(n)) && (!filterFunc || filterFunc(n))) { - entries.push(n); - } - } - if (sortBy[0] === "name") { - sortFunc = M.getSortByNameFn(); - } else if (sortBy[0] === "size") { - sortFunc = M.getSortBySizeFn(); - } else if (sortBy[0] === "ts") { - sortFunc = M.getSortByDateTimeFn(); - } else if (sortBy[0] === "rts") { - sortFunc = M.getSortByRtsFn(); - } else if (sortBy[0] === "status") { - sortFunc = M.getSortByStatusFn(); - } else if (sortBy[0] === "interaction") { - sortFunc = M.getSortByInteractionFn(); - } else if (sortBy[0] === "verification") { - sortFunc = M.getSortByVerificationFn(); - } else if (sortBy[0] === "email") { - sortFunc = M.getSortByEmail(); - } else if (sortBy[0] === 'access') { - sortFunc = (a, b, o) => typeof a.r !== 'undefined' && typeof b.r !== 'undefined' && (a.r < b.r ? -1 : 1) * o; - } else { - sortFunc = M.sortByFavFn(order); - } - const folders = []; - if (this.props.sortFoldersFirst) { - for (let i = entries.length; i--;) { - if (entries[i] && entries[i].t) { - folders.unshift(entries[i]); - entries.splice(i, 1); - } - } - } - folders.sort((a, b) => { - return sortFunc(a, b, order); - }); - entries.sort((a, b) => { - return sortFunc(a, b, order); - }); - return folders.concat(entries); + const $status = $('.activity-status-block .activity-status', 'body'); + $('.top-user-status-popup .dropdown-item').removeClass("active"); + $status.removeClass('online').removeClass('away').removeClass('busy').removeClass('offline').removeClass('black'); + const actualPresence = self.plugins.presencedIntegration.getMyPresenceSetting(); + const userPresenceConRetMan = megaChat.userPresence.connectionRetryManager; + const presence = self.plugins.presencedIntegration.getMyPresence(); + let cssClass = PresencedIntegration.presenceToCssClass(presence); + if (userPresenceConRetMan.getConnectionState() !== ConnectionRetryManager.CONNECTION_STATE.CONNECTED) { + cssClass = "offline"; } - onHighlighted(nodes) { - this.setState({ - 'highlighted': nodes - }); - if (this.props.onHighlighted) { - this.props.onHighlighted(nodes); - } + const $activityStatus = $('.activity-text', '.js-topbar'); + if (actualPresence === UserPresence.PRESENCE.ONLINE) { + $('.top-user-status-popup .dropdown-item[data-presence="chat"]').addClass("active"); + $activityStatus.text(l[5923]); + } else if (actualPresence === UserPresence.PRESENCE.AWAY) { + $('.top-user-status-popup .dropdown-item[data-presence="away"]').addClass("active"); + $activityStatus.text(l[5924]); + } else if (actualPresence === UserPresence.PRESENCE.DND) { + $('.top-user-status-popup .dropdown-item[data-presence="dnd"]').addClass("active"); + $activityStatus.text(l[5925]); + } else if (actualPresence === UserPresence.PRESENCE.OFFLINE) { + $('.top-user-status-popup .dropdown-item[data-presence="unavailable"]').addClass("active"); + $activityStatus.text(l[5926]); + } else { + $('.top-user-status-popup .dropdown-item[data-presence="unavailable"]').addClass("active"); + $activityStatus.text(l[5926]); } - finishedLoading(newState) { - newState.isLoading = false; - newState.entries = this.getEntries(); - this.initSelectionManager(newState.entries); - this.setState(newState); + $status.addClass(cssClass); + if (userPresenceConRetMan.getConnectionState() === ConnectionRetryManager.CONNECTION_STATE.CONNECTING) { + $status.parent().addClass("fadeinout"); + } else { + $status.parent().removeClass("fadeinout"); } - addOrUpdRawListener() { - if (this._rawListener) { - mBroadcaster.removeListener(this._rawListener); +}; +Chat.prototype.renderMyStatus = SoonFc(Chat.prototype._renderMyStatus, 100); +Chat.prototype.openChat = function (userHandles, type, chatId, chatShard, chatdUrl, setAsActive, chatHandle, publicChatKey, ck, isMeeting, mcoFlags, organiser) { + const self = this; + let room = false; + type = type || "private"; + setAsActive = setAsActive === true; + let roomId = chatId; + if (!publicChatKey && chatHandle && self.publicChatKeys[chatHandle]) { + if (type !== "public") { + console.error("this should never happen.", type); + type = "public"; } - this._rawListener = mBroadcaster.addListener(`fmViewUpdate:${ this.props.currentlyViewedEntry}`, () => { - this.setState({ - 'entries': this.getEntries() - }, () => { - if (this.browserEntries.isMounted()) { - this.browserEntries.forceUpdate(); - } - }); - }); + publicChatKey = self.publicChatKeys[chatHandle]; } - componentDidMount() { - let _this$dataSource2; - super.componentDidMount(); - if (!((_this$dataSource2 = this.dataSource) != null && _this$dataSource2.addChangeListener)) { - this.addOrUpdRawListener(); - } - if (this.props.fmConfigSortEnabled) { - this._sortModeListener = mBroadcaster.addListener("fmconfig:sortmodes", sortModes => { - this.onFmConfigSortModeChanged(sortModes); - }); + const $promise = new MegaPromise(); + if (type === "private") { + this.initContacts(userHandles, 2); + roomId = userHandles.length > 1 ? array.one(userHandles, u_handle) : u_handle; + if (self.chats[roomId]) { + $promise.resolve(roomId, self.chats[roomId]); + return [roomId, self.chats[roomId], $promise]; } + } else { + assert(roomId, 'Tried to create a group chat, without passing the chatId.'); + roomId = chatId; } - componentDidUpdate(prevProps) { - const { - currentlyViewedEntry: currEntry, - searchValue: currSearch - } = this.props; - const { - currentlyViewedEntry: prevEntry, - searchValue: prevSearch - } = prevProps; - const dataSourceChanged = this.props.dataSource !== prevProps.dataSource; - if (dataSourceChanged || prevEntry !== currEntry || currSearch !== prevSearch) { - let _this$dataSource3; - this.dataSource = this.props.dataSource; - const newState = { - 'selected': [], - 'highlighted': [] - }; - if (!((_this$dataSource3 = this.dataSource) != null && _this$dataSource3.addChangeListener)) { - this.addOrUpdRawListener(); - } - const handle = currEntry; - if (handle === 'shares') { - newState.isLoading = true; - this.setState(newState); - dbfetch.geta(Object.keys(M.c.shares || {})).always(() => { - this.finishedLoading(newState); - }); - return; + if (type === "group" || type === "public") { + if (d) { + console.time(`openchat:${ chatId }.${ type}`); + } + const newUsers = this.initContacts(userHandles); + if (newUsers.length) { + const chats = self.chats._data; + if (d) { + console.debug('openchat:%s.%s: processing %s new users...', chatId, type, newUsers.length); } - if (this.getDataSourceNode(handle).t && !M.getChildren(handle)) { - this.setState({ - 'isLoading': true - }); - dbfetch.get(handle).always(() => { - this.finishedLoading(newState); - }); - return; + for (const k in chats) { + const chatRoom = self.chats[k]; + const participants = array.to.object(chatRoom.getParticipantsExceptMe()); + for (let j = newUsers.length; j--;) { + const u = newUsers[j]; + if (participants[u]) { + chatRoom.trackDataChange(); + break; + } + } } - const entries = this.getEntries(); - this.initSelectionManager(entries); - this.setState({ - entries - }); - } - } - onAttachClicked() { - this.props.onAttachClicked(); - } - onContextMenu() {} - componentWillUnmount() { - super.componentWillUnmount(); - if (this._listener) { - let _this$dataSource4; - (_this$dataSource4 = this.dataSource) == null || _this$dataSource4.removeChangeListener(this._listener); + self.renderMyStatus(); } - if (this._rawListener) { - mBroadcaster.removeListener(this._rawListener); + if (d) { + console.timeEnd(`openchat:${ chatId }.${ type}`); } - if (this._sortModeListener) { - mBroadcaster.removeListener(this._sortModeListener); + if (type === "group") { + ChatdIntegration._ensureKeysAreLoaded([], userHandles, chatHandle).catch(dump); } - $.selected = []; - this.selectionManager.destroy(); - this.selectionManager = undefined; - $('.dropdown.body.files-menu.context').css('z-index', ''); + ChatdIntegration._ensureContactExists(userHandles, chatHandle); } - onSortByChanged(newState) { - if (newState[0] === this.state.sortBy[0] && newState[1] === this.state.sortBy[1]) { - return; + if (self.chats[roomId]) { + room = self.chats[roomId]; + if (setAsActive) { + room.show(); } - const entries = this.getEntries({ - 'sortBy': newState - }); - this.setState({ - 'sortBy': newState, - entries, - 'selected': [], - 'highlighted': [] - }, () => { - if (this.props.onSortByChanged) { - this.props.onSortByChanged(newState); - } - if (this.props.fmConfigSortEnabled) { - const sortId = this.props.fmConfigSortId; - assert(sortId, 'fmConfigSortId missing'); - if (newState[0] === this.props.initialSortBy[0] && newState[1] === this.props.initialSortBy[1]) { - const sortModes = typeof fmconfig.sortmodes !== 'undefined' ? fmconfig.sortmodes : Object.create(null); - delete sortModes[sortId]; - mega.config.set('sortmodes', sortModes); - return; - } - const map = this.props.fmConfigSortMap || Object.create(null); - const name = map[newState[0]] || newState[0]; - const direction = newState[1] === "asc" ? 1 : -1; - fmsortmode(sortId, name, direction); - } - }); - this.initSelectionManager(entries); + $promise.resolve(roomId, room); + return [roomId, room, $promise]; } - onFmConfigSortModeChanged(sortModes) { - const currentSortMode = sortModes[this.props.fmConfigSortId]; - if (!currentSortMode) { - this.onSortByChanged(this.props.initialSortBy || ['name', 'asc']); - } else { - const newSortMode = this._translateFmConfigSortMode(currentSortMode); - if (this.state.sortBy[0] !== newSortMode[0] || this.state.sortBy[1] !== newSortMode[1]) { - this.onSortByChanged(newSortMode); - } - } + if (setAsActive && self.currentlyOpenedChat && self.currentlyOpenedChat !== roomId) { + self.hideChat(self.currentlyOpenedChat); + self.currentlyOpenedChat = null; } - render() { - return REaCt().createElement("div", { - ref: this.domRef, - className: "content-container", - onClick: ev => { - $.hideContextMenu(ev); - } - }, REaCt().createElement(BrowserEntries, { - isLoading: this.state.isLoading || this.props.nodeLoading, - currentlyViewedEntry: this.props.currentlyViewedEntry, - entries: this.state.entries || [], - onExpand: node => { - this.setState({ - 'selected': [], - 'highlighted': [] - }); - this.props.onExpand(node[this.props.keyProp || 'h']); - }, - sortBy: this.state.sortBy, - folderSelectNotAllowed: this.props.folderSelectNotAllowed, - onAttachClicked: this.onAttachClicked, - viewMode: this.props.viewMode, - selected: this.state.selected, - highlighted: this.state.highlighted, - onContextMenu: this.props.onContextMenu || this.onContextMenu, - selectionManager: this.selectionManager, - ref: browserEntries => { - this.browserEntries = browserEntries; - }, - onSortByChanged: newState => { - this.onSortByChanged(newState); - }, - listAdapterColumns: this.props.listAdapterColumns, - currentdirid: this.props.currentdirid, - containerClassName: this.props.containerClassName, - headerContainerClassName: this.props.headerContainerClassName, - megaListItemHeight: this.props.megaListItemHeight, - keyProp: this.props.keyProp || 'h', - NilComponent: this.props.NilComponent, - listAdapterOpts: this.props.listAdapterOpts - })); + room = new ChatRoom(self, roomId, type, userHandles, unixtime(), undefined, chatId, chatShard, chatdUrl, null, chatHandle, publicChatKey, ck, isMeeting, 0, mcoFlags, organiser); + self.chats.set(room.roomId, room); + if (setAsActive && !self.currentlyOpenedChat || self.currentlyOpenedChat === room.roomId) { + room.setActive(); } -} - -}, - -161 -(_, EXP_, REQ_) { - -"use strict"; -REQ_.d(EXP_, { -$: () => ColumnFavIcon -}); -const react0__ = REQ_(594); -const react0 = REQ_.n(react0__); -const _genericNodePropsComponent1__ = REQ_(984); - - -class ColumnFavIcon extends _genericNodePropsComponent1__.B { - render() { - const { - nodeAdapter - } = this.props; - const { - node - } = nodeAdapter.props; - const isFavouritable = node.r === 2; - return react0().createElement("td", { - megatype: ColumnFavIcon.megatype, - className: ColumnFavIcon.megatype - }, react0().createElement("span", { - className: `grid-status-icon sprite-fm-mono ${ missingkeys[node.h] ? " icon-info" : nodeAdapter.nodeProps.fav ? " icon-favourite-filled" : " icon-dot" }${!isFavouritable && " disabled" || ""}`, - onClick: () => { - if (isFavouritable) { - M.favourite([node.h], !node.fav); + room.showAfterCreation = setAsActive !== false; + return [roomId, room, new Promise((resolve, reject) => { + this.trigger('onRoomInitialized', [room, resolve, reject]); + room.setState(ChatRoom.STATE.JOINING); + const q = this._queuedChatRoomEvents[chatId]; + if (q) { + delete this._queuedChatRoomEvents[chatId]; + for (let i = 0; i < q.length; ++i) { + const [event, data] = q[i]; + if (d) { + this.logger.debug(`Dispatching deferred event '${event}'`, data); } + room.trigger(event, data); } - })); - } -} -ColumnFavIcon.sortable = true; -ColumnFavIcon.id = "fav"; -ColumnFavIcon.label = ""; -ColumnFavIcon.icon = "icon-favourite-filled"; -ColumnFavIcon.megatype = "fav"; -ColumnFavIcon.headerClassName = "grid-first-th fav"; - -}, - -984 -(_, EXP_, REQ_) { - -"use strict"; - -// EXPORTS -REQ_.d(EXP_, { - B: () => GenericNodePropsComponent -}); - -// EXTERNAL MODULE: ./js/chat/mixins.js -const mixins = REQ_(137); -;// ./js/ui/jsx/fm/nodes/nodeProperties.jsx -class NodeProperties { - static get(node, changeListener) { - assert(node.h, 'missing handle for node'); - if (NodeProperties._globalCleanupTimer) { - NodeProperties._globalCleanupTimer.abort(); - } - (NodeProperties._globalCleanupTimer = tSleep(120)).then(() => { - NodeProperties.cleanup(0); - }); - let nodeProps; - if (!NodeProperties._cache.has(node.h)) { - nodeProps = new NodeProperties(node, changeListener); - NodeProperties._cache.set(node.h, nodeProps); + q.timer.abort(); } - return nodeProps || NodeProperties._cache.get(node.h); + this.processQueuedMcsmPackets(); + })]; +}; +Chat.prototype.initContacts = function (userHandles, c) { + const newUsers = []; + for (let i = userHandles.length; i--;) { + const u = userHandles[i]; + const e = u in M.u; + M.addUser(e ? { + u + } : { + u, + c + }, e || !newUsers.push(u)); } - unuse(changeListener) { - const {node} = this; - if (!node) { - if (d) { - console.warn("This should not happen."); - } - return; - } - this.changeListeners.delete(changeListener); - let usages = NodeProperties._usages.get(this); - if (usages) { - NodeProperties._usages.set(this, --usages); - if (usages === 0 && NodeProperties._cache.size > NodeProperties.MAX_CACHE_SIZE) { - delay('nodePropCleanup', NodeProperties.cleanup, 1000); - } - } - } - static cleanup(maxCacheSize) { - maxCacheSize = typeof maxCacheSize === "undefined" ? NodeProperties.MAX_CACHE_SIZE : maxCacheSize; - const len = NodeProperties._cache.size; - let removed = 0; - for (const entry of NodeProperties._cache) { - const id = entry[0]; - const node = entry[1]; - const usage = NodeProperties._usages.get(node); - if (usage === 0) { - NodeProperties._usages.delete(node); - node._cleanup(); - NodeProperties._cache.delete(id); - removed++; - if (len - removed < maxCacheSize) { - return; - } - } + return newUsers; +}; +Chat.prototype.smartOpenChat = function (...args) { + const self = this; + if (typeof args[0] === 'string') { + args[0] = [u_handle, args[0]]; + if (args.length < 2) { + args.push('private'); } } - constructor(node, changeListener) { - this.node = node; - this.changeListeners = new Set(); - if (changeListener) { - this.changeListeners.add(changeListener); - } - const _onChange = () => { - this.initProps(); - for (const listener of this.changeListeners) { - listener(); + return new Promise((resolve, reject) => { + const waitForReadyState = function (aRoom, aShow) { + const verify = function () { + return aRoom.state === ChatRoom.STATE.READY; + }; + const ready = function () { + if (aShow) { + aRoom.show(); + } + resolve(aRoom); + }; + if (verify()) { + return ready(); } + const { + roomId + } = aRoom; + createTimeoutPromise(verify, 300, 3e4, false, `waitForReadyState(${roomId})`).then(ready).catch(reject); }; - if (this.node.addChangeListener) { - this._listener = this.node.addChangeListener(_onChange); - } else { - this._mbListener = mBroadcaster.addListener(`nodeUpdated:${ node.h}`, _onChange); - } - this.initProps(); - } - use(changeListener) { - if (changeListener) { - this.changeListeners.add(changeListener); - } - NodeProperties._usages.set(this, (NodeProperties._usages.get(this) | 0) + 1); - } - _cleanup() { - if (this._listener) { - this.node.removeChangeListener(this._listener); - } - if (this._mbListener) { - mBroadcaster.removeListener(this._mbListener); - } - oDestroy(this); - } - initProps() { - const {node} = this; - lazy(this, 'title', () => { - if (missingkeys[node.h]) { - return node.t ? l[8686] : l[8687]; - } - return M.getNameByHandle(node.h); - }); - lazy(this, 'classNames', () => { - const classNames = []; - if (node.su) { - classNames.push('inbound-share'); - } - if (node.t) { - classNames.push('folder'); - } else { - classNames.push('file'); - } - const share = this.shareData; - if (missingkeys[node.h] || share.down) { - if (share.down) { - classNames.push('taken-down'); - } - if (missingkeys[node.h]) { - classNames.push('undecryptable'); + const [members, type] = args; + if (members.length === 2 && type === 'private') { + const chatRoom = self.chats[members.every(h => h === members[0]) ? u_handle : array.one(members, u_handle)]; + if (chatRoom) { + if (args[5]) { + chatRoom.show(); } + return waitForReadyState(chatRoom, args[5]); } - if (share) { - classNames.push('linked'); - } - if (node.lbl && !folderlink) { - const colourLabel = M.getLabelClassFromId(node.lbl); - classNames.push('colour-label'); - classNames.push(colourLabel); - } - return classNames; - }); - lazy(this, 'icon', () => { - return fileIcon(node); - }); - lazy(this, 'isFolder', () => { - return !!node.t; - }); - lazy(this, 'shareData', () => { - return M.getNodeShare(node); - }); - lazy(this, 'isTakendown', () => { - return this.shareData && !!this.shareData.down; - }); - lazy(this, 'fav', () => { - return !!node.fav; - }); - lazy(this, 'size', () => { - return bytesToSize(node.tb || node.s); - }); - lazy(this, 'timestamp', () => { - return time2date(node.ts); - }); - lazy(this, 'root', () => { - return M.getNodeRoot(node.h); - }); - lazy(this, 'incomingShareData', () => { - const result = {}; - if (node.r === 1) { - result.accessLabel = l[56]; - result.accessIcon = 'icon-permissions-write'; - } else if (node.r === 2) { - result.accessLabel = l[57]; - result.accessIcon = 'icon-star'; - } else { - result.accessLabel = l[55]; - result.accessIcon = 'icon-read-only'; + } + const result = self.openChat.apply(self, args); + if (result instanceof MegaPromise) { + result.then(reject).catch(reject); + } else if (!Array.isArray(result)) { + reject(EINTERNAL); + } else { + const room = result[1]; + const roomId = result[0]; + const promise = result[2]; + if (!(promise instanceof Promise)) { + self.logger.error('Unexpected openChat() response...'); + return reject(EINTERNAL); } - return result; - }); - lazy(this, 'timestamp', () => { - return time2date(node.ts); - }); - lazy(this, 'onlineStatus', () => { - return M.onlineStatusClass(node.presence ? node.presence : "unavailable"); - }); - } -} -NodeProperties._cache = new Map(); -NodeProperties._usages = new WeakMap(); -NodeProperties._globalCleanupTimer = void 0; -NodeProperties.MAX_CACHE_SIZE = 100; -if (d) { - window.NodeProperties = NodeProperties; -} -;// ./js/ui/jsx/fm/nodes/genericNodePropsComponent.jsx - - -class GenericNodePropsComponent extends mixins.w9 { - constructor(props) { - super(props); - if (this.props.node.h) { - this.nodeProps = NodeProperties.get(this.props.node); - this.changeListener = this.changeListener.bind(this); + self.logger.debug('Waiting for chat "%s" to be ready...', roomId, [room]); + promise.then(aRoom => { + const aRoomId = aRoom && aRoom.roomId; + if (aRoomId !== roomId || room && room !== aRoom || !(aRoom instanceof ChatRoom)) { + self.logger.error('Unexpected openChat() procedure...', aRoomId, [aRoom]); + return reject(EINTERNAL); + } + waitForReadyState(aRoom); + }).catch(ex => { + if (ex === EACCESS) { + room.destroy(); + } + reject(ex); + }); } - } - changeListener() { - if (this.isMounted()) { - this.safeForceUpdate(); + }); +}; +Chat.prototype.hideAllChats = function () { + const self = this; + self.chats.forEach(chatRoom => { + if (chatRoom.isCurrentlyActive) { + chatRoom.hide(); } + }); +}; +Chat.prototype.retrieveSharedFilesHistory = async function (len = 47, chatRoom = null) { + chatRoom = len instanceof ChatRoom ? len : chatRoom || this.getCurrentRoom(); + return chatRoom.messagesBuff.retrieveSharedFilesHistory(len); +}; +Chat.prototype.getCurrentRoom = function () { + return this.chats[this.currentlyOpenedChat]; +}; +Chat.prototype.getCurrentMeeting = function () { + const chatRoom = this.getCurrentRoom(); + return chatRoom && chatRoom.scheduledMeeting || null; +}; +Chat.prototype.getCurrentRoomJid = function () { + return this.currentlyOpenedChat; +}; +Chat.prototype.hideChat = function (roomJid) { + const self = this; + const room = self.chats[roomJid]; + if (room) { + room.hide(); + } else { + self.logger.warn("Room not found: ", roomJid); + } +}; +Chat.prototype.sendMessage = function (roomJid, val) { + const fail = ex => { + this.logger.error(`sendMessage(${roomJid}) failed.`, ex); + }; + if (!this.chats[roomJid]) { + this.logger.warn("Queueing message for room: ", roomJid, val); + const timeout = this.options.delaySendMessageIfRoomNotAvailableTimeout; + return createTimeoutPromise(() => !!this.chats[roomJid], 500, timeout).then(() => { + return this.chats[roomJid].sendMessage(val); + }).catch(fail); } - UNSAFE_componentWillReceiveProps(nextProps) { - if (nextProps.highlighted !== this.props.highlighted) { - this.safeForceUpdate(); + return this.chats[roomJid].sendMessage(val).catch(fail); +}; +Chat.prototype.processNewUser = function (u, isNewChat) { + const self = this; + if (self.plugins.presencedIntegration) { + const user = M.u[u] || false; + if (user.c === 1) { + self.plugins.presencedIntegration.addContact(u, isNewChat); } } - UNSAFE_componentWillMount() { - let _this$nodeProps; - if (super.UNSAFE_componentWillMount) { - super.UNSAFE_componentWillMount(); + self.chats.forEach((chatRoom) => { + if (chatRoom.getParticipantsExceptMe().indexOf(u) > -1) { + chatRoom.trackDataChange(); } - (_this$nodeProps = this.nodeProps) == null || _this$nodeProps.use(this.changeListener); + }); + self.renderMyStatus(); +}; +Chat.prototype.processRemovedUser = function (u) { + const self = this; + if (self.plugins.presencedIntegration) { + self.plugins.presencedIntegration.removeContact(u); } - componentWillUnmount() { - let _this$nodeProps2; - super.componentWillUnmount(); - (_this$nodeProps2 = this.nodeProps) == null || _this$nodeProps2.unuse(this.changeListener); + self.chats.forEach((chatRoom) => { + if (chatRoom.getParticipantsExceptMe().indexOf(u) > -1) { + chatRoom.trackDataChange(); + } + }); + self.renderMyStatus(); +}; +Chat.prototype.refreshConversations = function () { + const self = this; + if (!u_type && !self.$container && !megaChatIsReady) { + $('.fm-chat-block').hide(); + return false; } -} - -}, - -818 -(_, EXP_, REQ_) { - -"use strict"; -REQ_.d(EXP_, { -A: () => __WEBPACK_DEFAULT_EXPORT__ -}); -const react0__ = REQ_(594); -const react0 = REQ_.n(react0__); -const _chat_mixins1__ = REQ_(137); - - -class ToggleCheckbox extends _chat_mixins1__.w9 { - constructor(props) { - super(props); - this.domRef = react0().createRef(); - this.onToggle = () => { - const newState = !this.state.value; - this.setState({ - value: newState - }); - if (this.props.onToggle) { - this.props.onToggle(newState); - } - }; - this.state = { - value: this.props.value - }; + $('.section.conversations .fm-chat-is-loading').addClass('hidden'); + if (self.$container.parent('.section.conversations .fm-right-files-block').length == 0) { + $('.section.conversations .fm-right-files-block').append(self.$container); } - render() { - return react0().createElement("div", { - ref: this.domRef, - className: ` - mega-switch - ${this.props.className} - ${this.state.value ? 'toggle-on' : ''} - `, - role: "switch", - "aria-checked": !!this.state.value, - onClick: this.onToggle - }, react0().createElement("div", { - className: `mega-feature-switch sprite-fm-mono-after - ${this.state.value ? 'icon-check-after' : 'icon-minimise-after'}` - })); + self.$leftPane = self.$leftPane || $('.conversationsApp .fm-left-panel'); + if (is_chatlink || megaChat._joinDialogIsShown) { + self.$leftPane.addClass('hidden'); + } else { + self.$leftPane.removeClass('hidden'); } -} -const __WEBPACK_DEFAULT_EXPORT__ = { - ToggleCheckbox }; - -}, - -318 -(_, EXP_, REQ_) { - -"use strict"; - -// EXPORTS -REQ_.d(EXP_, { - A: () => modalDialogs -}); - -// UNUSED EXPORTS: ExtraFooterElement - -// EXTERNAL MODULE: ./js/ui/utils.jsx -const utils = REQ_(314); -// EXTERNAL MODULE: ./js/chat/mixins.js -const mixins = REQ_(137); -;// ./js/ui/forms.jsx -const React = REQ_(594); - -class Checkbox extends mixins.w9 { - constructor(props) { - super(props); - this.domRef = React.createRef(); - this.state = { - checked: this.props.checked ? this.props.checked : false - }; - this.onLabelClick = this.onLabelClick.bind(this); - this.onChange = this.onChange.bind(this); - } - onLabelClick(e) { - const state = !this.state.checked; - this.setState({ - 'checked': state - }); - if (this.props.onLabelClick) { - this.props.onLabelClick(e, state); +Chat.prototype.navigate = function megaChatNavigate(location, event, isLandingPage) { + return new Promise((resolve, reject) => { + this.routing.route(resolve, reject, location, event, isLandingPage); + }); +}; +if (is_mobile) { + Chat.prototype.navigate = function (location, event, isLandingPage) { + if (d) { + this.logger.warn('mobile-nop navigate(%s)', location, event, isLandingPage); } - this.onChange(e); - } - onChange(e) { - if (this.props.onChange) { - this.props.onChange(e, this.state.checked); + if (is_chatlink) { + mobile.chatlink.show(is_chatlink.ph, is_chatlink.key); + } else { + loadSubPage('fm', event); } - } - render() { - const { - name, - id, - children - } = this.props; - const className = this.state.checked ? 'checkboxOn' : 'checkboxOff'; - return React.createElement("div", { - ref: this.domRef, - className: "formsCheckbox" - }, React.createElement("div", { - className: ` - checkdiv - ${className} - `, - onClick: this.onLabelClick - }, React.createElement("input", { - type: "checkbox", - name, - id, - className, - checked: this.state.checked, - onChange: this.onChange - })), React.createElement("label", { - htmlFor: id, - className: "radio-txt" - }, children)); - } + return Promise.resolve(); + }; } -const ui_forms = { - Checkbox -}; -;// ./js/ui/modalDialogs.jsx -const modalDialogs_React = REQ_(594); - - - -const ContactsUI = REQ_(251); -class ExtraFooterElement extends modalDialogs_React.Component { - render() { - return this.props.children; +Chat.prototype.renderListing = async function megaChatRenderListing(location, isInitial) { + if (!isInitial && !M.chat) { + console.debug('renderListing: Not in chat.'); + throw EACCESS; } -} -class SafeShowDialogController extends mixins.w9 { - constructor(props) { - super(props); - this.dialogName = 'unnamed-dialog'; - this.dialogBecameVisible = null; - const { - render - } = this; - this.render = () => { - if (this.dialogBecameVisible) { - console.assert($.dialog === this.dialogName, `${this.dialogName} state overridden.`); - return render.call(this); - } - return null; - }; + M.hideEmptyGrids(); + this.refreshConversations(); + this.hideAllChats(); + if (!is_chatlink && mega.ui.flyout && (mega.ui.flyout.name.startsWith('contact') || mega.ui.flyout.name === 'chat')) { + mega.ui.flyout.hide(); } - shouldComponentUpdate(nextProps, nextState) { - if (!this.dialogBecameVisible) { - return false; + $('.files-grid-view').addClass('hidden'); + $('.fm-blocks-view').addClass('hidden'); + $('.fm-chat-block').addClass('hidden'); + $('.fm-right-files-block').addClass('hidden'); + $('.fm-right-files-block.in-chat').removeClass('hidden'); + $('.nw-conversations-item').removeClass('selected'); + $('.fm-empty-conversations').removeClass('hidden'); + M.onSectionUIOpen('conversations'); + let room; + if (!location && this.chats.length) { + const valid = room => room && room._leaving !== true && !room.isNote && room.isDisplayable() && room; + room = valid(this.chats[this.lastOpenedChat]); + if (!room) { + let idx = 0; + const rooms = Object.values(this.chats).filter(r => this.currentlyOpenedView === null || r.isMeeting === !!this.currentlyOpenedView).sort(M.sortObjFn('lastActivity', -1)); + do { + room = valid(rooms[idx]); + } while (!room && ++idx < rooms.length); + } + if (room) { + location = room.getRoomUrl(); } - return super.shouldComponentUpdate(nextProps, nextState); } - componentDidMount() { - super.componentDidMount(); - M.safeShowDialog(this.dialogName, () => { - if (!this.isMounted()) { - throw new Error(`${this.dialogName} component is no longer mounted.`); + if (location) { + $('.fm-empty-conversations').addClass('hidden'); + return this.navigate(location, undefined, isInitial).catch(ex => { + if (d) { + this.logger.warn('Failed to navigate to %s...', location, room, ex); } - this.dialogBecameVisible = 1; - this.forceUpdate(); - }); - } - componentWillUnmount() { - super.componentWillUnmount(); - if (this.dialogBecameVisible) { - this.dialogBecameVisible = false; - console.assert($.dialog === this.dialogName); - if ($.dialog === this.dialogName) { - closeDialog(); + if (!room) { + return this.renderListing(null); } - } - } - componentDidUpdate() { - assert(this.dialogBecameVisible); - super.componentDidUpdate(); - if (++this.dialogBecameVisible === 2) { - requestAnimationFrame(() => { - const dialog = document.querySelectorAll(`.${this.dialogName}`); - console.assert(dialog.length === 1, `Unexpected ${this.dialogName} state.`); - console.assert($.dialog === this.dialogName, `${this.dialogName} state overridden.`); - if (dialog.length === 1 && $.dialog === this.dialogName) { - dialog[0].classList.remove('hidden', 'arrange-to-back'); - } + onIdle(() => { + room.destroy(); }); - } - } -} -class ModalDialog extends mixins.w9 { - constructor(props) { - super(props); - this.domRef = modalDialogs_React.createRef(); - this.onBlur = this.onBlur.bind(this); - this.onCloseClicked = this.onCloseClicked.bind(this); - this.onPopupDidMount = this.onPopupDidMount.bind(this); + throw ex; + }); } - componentDidMount() { - super.componentDidMount(); - if (!this.props.hideOverlay) { - $(document.body).addClass('overlayed'); - $('.fm-dialog-overlay').removeClass('hidden'); + return ENOENT; +}; +Chat.prototype.setAttachments = function (roomId) { + 'use strict'; + + if (M.chat) { + if (d) { + console.assert(this.chats[roomId] && this.chats[roomId].isCurrentlyActive, 'check this...'); } - $('textarea:focus').trigger("blur"); - if (!this.props.noCloseOnClickOutside) { - const convApp = document.querySelector('.conversationsApp'); - if (convApp) { - convApp.removeEventListener('click', this.onBlur); - convApp.addEventListener('click', this.onBlur); - } - $('.fm-modal-dialog').rebind(`click.modalDialogOv${ this.getUniqueId()}`, ({ - target - }) => { - if ($(target).is('.fm-modal-dialog')) { - this.onBlur(); + M.v = Object.values(M.chc[roomId] || {}); + if (M.v.length) { + let _this$chats$roomId; + const sv = (_this$chats$roomId = this.chats[roomId]) == null || (_this$chats$roomId = _this$chats$roomId.messagesBuff) == null || (_this$chats$roomId = _this$chats$roomId.sharedFiles) == null ? void 0 : _this$chats$roomId._sortedVals; + if (sv && sv.length === M.v.length) { + M.v.sort((a, b) => sv.indexOf(a.m) - sv.indexOf(b.m)); + } else { + if (d) { + this.logger.info('falling back to order-value sorting.', sv); } - }); - $('.fm-dialog-overlay').rebind(`click.modalDialog${ this.getUniqueId()}`, () => { - if (this.props.closeDlgOnClickOverlay) { - this.onBlur(); + M.v.sort(M.sortObjFn('co')); + } + for (let i = M.v.length; i--;) { + const n = M.v[i]; + if (!n.revoked && !n.seen) { + n.seen = -1; + if (this._shallLoadImageFor(n)) { + this._enqueueImageLoad(n); + } } - return false; - }); - } - $(document).rebind(`keyup.modalDialog${ this.getUniqueId()}`, ({ - keyCode - }) => { - if (!this.props.stopKeyPropagation && keyCode === 27) { - this.onBlur(); } - }); + if ($.triggerSlideShow) { + delay('chat:refresh-slideshow-on-single-entry', () => { + const { + slideshowid: id + } = window; + if (id && $.triggerSlideShow === id) { + slideshow(id); + } + delete $.triggerSlideShow; + }); + } + } + } else if (d) { + console.warn('Not in chat...'); } - onBlur(e) { - let _this$domRef; - const $element = $((_this$domRef = this.domRef) == null ? void 0 : _this$domRef.current); - if (!e || !$(e.target).closest('.mega-dialog').is($element)) { - const convApp = document.querySelector('.conversationsApp'); - if (convApp) { - convApp.removeEventListener('click', this.onBlur); +}; +Chat.prototype._enqueueMessageUpdate = function (message) { + this._queuedMessageUpdates.push(message); + delay('chat:enqueue-message-updates', () => { + const queue = this._queuedMessageUpdates; + this._queuedMessageUpdates = []; + for (let i = queue.length; i--;) { + queue[i].trackDataChange(); + } + }, 400); +}; +Chat.prototype._shallLoadImageFor = function (n) { + return n && /:[01]\*/.test(n.fa); +}; +Chat.prototype._enqueueImageLoad = function (n) { + 'use strict'; + let cc = previews[n.h] || previews[n.hash]; + if (cc) { + if (cc.poster) { + n.src = cc.poster; + } else { + if (cc.full && n.mime !== 'image/png' && n.mime !== 'image/webp') { + cc = cc.prev || false; + } + if (String(cc.type).startsWith('image/')) { + n.src = cc.src; } - this.onCloseClicked(); } } - componentWillUnmount() { - let _this$props$popupWill, _this$props; - super.componentWillUnmount(); - if (!this.props.noCloseOnClickOutside) { - const convApp = document.querySelector('.conversationsApp'); - if (convApp) { - convApp.removeEventListener('click', this.onBlur); + let cached = n.src; + if (this._shallLoadImageFor(n)) { + let load = false; + let dedup = true; + if (this._imageAttributeCache[n.fa]) { + this._imageAttributeCache[n.fa].push(n.ch); + } else { + this._imageAttributeCache[n.fa] = [n.ch]; + load = !cached; + } + if (this._imageLoadCache[n.fa]) { + this._imageLoadCache[n.fa].push(n.ch); + } else { + this._imageLoadCache[n.fa] = [n.ch]; + if (load) { + this._imagesToBeLoaded[n.fa] = n; + dedup = false; } - $('.fm-dialog-overlay').off(`click.modalDialog${ this.getUniqueId()}`); } - if (!this.props.hideOverlay) { - $(document.body).removeClass('overlayed'); - $('.fm-dialog-overlay').addClass('hidden'); + if (dedup) { + cached = true; + } else { + delay('chat:enqueue-image-load', this._doLoadImages.bind(this), 350); } - $(this.domNode).off(`dialog-closed.modalDialog${ this.getUniqueId()}`); - $(document).off(`keyup.modalDialog${ this.getUniqueId()}`); - (_this$props$popupWill = (_this$props = this.props).popupWillUnmount) == null || _this$props$popupWill.call(_this$props); } - onCloseClicked() { - const self = this; - if (self.props.onClose) { - self.props.onClose(self); - } + if (cached) { + this._doneLoadingImage(n.fa); } - onPopupDidMount(elem) { - this.domNode = elem; - $(elem).rebind(`dialog-closed.modalDialog${ this.getUniqueId()}`, () => this.onCloseClicked()); - if (this.props.popupDidMount) { - this.props.popupDidMount(elem); +}; +Chat.prototype._doLoadImages = function () { + "use strict"; + + const self = this; + const originals = Object.create(null); + let imagesToBeLoaded = self._imagesToBeLoaded; + self._imagesToBeLoaded = Object.create(null); + const chatImageParser = function (h, data) { + const n = M.chd[(self._imageLoadCache[h] || [])[0]] || false; + if (n && data !== 0xDEAD) { + n.src = mObjectURL([data.buffer || data], 'image/jpeg'); + n.srcBuffer = data; + } else if (d) { + console.warn('Failed to load image for %s', h, n); + } + self._doneLoadingImage(h); + }; + for (const k in imagesToBeLoaded) { + const node = imagesToBeLoaded[k]; + const mime = filemime(node); + if (node.s < LOAD_ORIGINALS[mime]) { + originals[node.fa] = node; + delete imagesToBeLoaded[k]; } } - render() { - const self = this; - let classes = 'mega-dialog'; - let selectedNumEle = null; - let footer = null; - const extraFooterElements = []; - const otherElements = []; - let x = 0; - modalDialogs_React.Children.forEach(self.props.children, (child) => { - if (!child) { - return; - } - if (child.type.name === 'ExtraFooterElement') { - extraFooterElements.push(modalDialogs_React.cloneElement(child, { - key: x++ - })); + const onSuccess = function (ctx, origNodeHandle, data) { + chatImageParser(origNodeHandle, data); + }; + const onError = function (origNodeHandle) { + chatImageParser(origNodeHandle, 0xDEAD); + }; + const loadOriginal = function (n) { + const origFallback = ex => { + const type = String(n.fa).indexOf(':1*') > 0 ? 1 : 0; + if (d) { + console.debug('Failed to load original image on chat.', n.h, n, ex); + } + imagesToBeLoaded[n.fa] = originals[n.fa]; + delete originals[n.fa]; + delay(`ChatRoom[${ self.roomId }]:origFallback${ type}`, () => { + api_getfileattr(imagesToBeLoaded, type, onSuccess, onError); + }); + }; + M.gfsfetch(n.h, 0, -1).then((data) => { + const handler = is_image(n); + if (typeof handler === 'function') { + handler(data, buffer => { + if (buffer) { + chatImageParser(n.fa, buffer); + } else { + origFallback(EFAILED); + } + }); } else { - otherElements.push(modalDialogs_React.cloneElement(child, { - key: x++ - })); + chatImageParser(n.fa, data); } + }).catch(origFallback); + }; + if ($.len(originals)) { + Object.values(originals).map(loadOriginal); + } + api_getfileattr(imagesToBeLoaded, 1, onSuccess, onError); + [imagesToBeLoaded, originals].forEach((obj) => { + Object.keys(obj).forEach((handle) => { + self._startedLoadingImage(handle); }); - if (self.props.className) { - classes += ` ${self.props.className}`; - } - if (self.props.dialogType) { - classes += ` dialog-template-${self.props.dialogType}`; - } - if (self.props.dialogName) { - classes += ` ${self.props.dialogName}`; + }); + imagesToBeLoaded = Object.create(null); +}; +Chat.prototype._getImageNodes = function (h, src) { + let nodes = this._imageLoadCache[h] || []; + let handles = [].concat(nodes); + for (let i = nodes.length; i--;) { + const n = M.chd[nodes[i]] || false; + if (this._imageAttributeCache[n.fa]) { + handles = handles.concat(this._imageAttributeCache[n.fa]); } - if (self.props.showSelectedNum && self.props.selectedNum) { - selectedNumEle = modalDialogs_React.createElement("div", { - className: "selected-num" - }, modalDialogs_React.createElement("span", null, self.props.selectedNum)); + } + handles = array.unique(handles); + nodes = handles.map((ch) => { + const n = M.chd[ch] || false; + if (src && n.src) { + Object.assign(src, n); } - let buttons; - if (self.props.buttons) { - buttons = []; - self.props.buttons.forEach((v, i) => { - if (v) { - buttons.push(modalDialogs_React.createElement("button", { - className: (v.defaultClassname ? v.defaultClassname : "mega-button") + (v.className ? ` ${ v.className}` : "") + (self.props.dialogType === "action" ? "large" : ""), - onClick: e => { - if ($(e.target).is(".disabled")) { - return false; - } - if (v.onClick) { - v.onClick(e, self); - } - }, - key: v.key + i - }, v.iconBefore ? modalDialogs_React.createElement("div", null, modalDialogs_React.createElement("i", { - className: v.iconBefore - })) : null, modalDialogs_React.createElement("span", null, v.label), v.iconAfter ? modalDialogs_React.createElement("div", null, modalDialogs_React.createElement("i", { - className: v.iconAfter - })) : null)); - } - }); - if (buttons && buttons.length > 0 || extraFooterElements && extraFooterElements.length > 0) { - footer = modalDialogs_React.createElement("footer", null, buttons && buttons.length > 0 ? modalDialogs_React.createElement("div", { - className: "footer-container" - }, buttons) : null, extraFooterElements && extraFooterElements.length > 0 ? modalDialogs_React.createElement("aside", null, extraFooterElements) : null); + return n; + }); + return nodes; +}; +Chat.prototype._startedLoadingImage = function (h) { + "use strict"; + + const nodes = this._getImageNodes(h); + for (let i = nodes.length; i--;) { + const n = nodes[i]; + if (!n.src && n.seen !== 2) { + let imgNode = document.getElementById(n.ch); + if (imgNode && (imgNode = imgNode.querySelector('img'))) { + imgNode.parentNode.parentNode.classList.add('thumb-loading'); } } - return modalDialogs_React.createElement(utils.Ay.RenderTo, { - element: document.body, - className: "fm-modal-dialog", - popupDidMount: this.onPopupDidMount - }, modalDialogs_React.createElement("div", { - ref: this.domRef, - id: self.props.id, - className: classes, - "aria-labelledby": self.props.dialogName ? `${self.props.dialogName }-title` : null, - role: "dialog", - "aria-modal": "true", - onClick: self.props.onClick - }, modalDialogs_React.createElement("button", { - className: "close", - onClick: self.onCloseClicked - }, modalDialogs_React.createElement("i", { - className: "sprite-fm-mono icon-dialog-close" - })), self.props.title ? self.props.dialogType === "message" ? modalDialogs_React.createElement("header", null, self.props.icon ? modalDialogs_React.createElement("i", { - className: `graphic ${self.props.icon}` - }) : self.props.iconElement, modalDialogs_React.createElement("div", null, modalDialogs_React.createElement("h3", { - id: self.props.dialogName ? `${self.props.dialogName }-title` : null - }, self.props.title, selectedNumEle), self.props.subtitle ? modalDialogs_React.createElement("p", null, self.props.subtitle) : null, otherElements)) : modalDialogs_React.createElement("header", null, self.props.icon ? modalDialogs_React.createElement("i", { - className: `graphic ${self.props.icon}` - }) : self.props.iconElement, modalDialogs_React.createElement("h2", { - id: self.props.dialogName ? `${self.props.dialogName }-title` : null - }, self.props.title, selectedNumEle), self.props.subtitle ? modalDialogs_React.createElement("p", null, self.props.subtitle) : null) : null, self.props.dialogType !== "message" ? otherElements : null, buttons || extraFooterElements ? footer : null)); } -} -ModalDialog.defaultProps = { - 'hideable': true, - 'noCloseOnClickOutside': false, - 'closeDlgOnClickOverlay': true, - 'showSelectedNum': false, - 'selectedNum': 0 }; -class SelectContactDialog extends mixins.w9 { - constructor(props) { - super(props); - this.dialogName = 'send-contact-dialog'; - this.state = { - selected: [] +Chat.prototype._doneLoadingImage = function (h) { + const self = this; + const setSource = function (n, img, src) { + const message = n.mo; + img.onload = function () { + img.onload = null; + n.srcWidth = this.naturalWidth; + n.srcHeight = this.naturalHeight; + if (message) { + self._enqueueMessageUpdate(message); + } }; - this.state.selected = this.props.selected || []; - this.onSelected = this.onSelected.bind(this); - } - onSelected(nodes) { - let _this$props$onSelecte, _this$props2; - this.setState({ - selected: nodes - }); - (_this$props$onSelecte = (_this$props2 = this.props).onSelected) == null || _this$props$onSelecte.call(_this$props2, nodes); - } - componentDidMount() { - super.componentDidMount(); - M.safeShowDialog(this.dialogName, () => $(`.${this.dialogName}`)); - } - componentWillUnmount() { - super.componentWillUnmount(); - if ($.dialog === this.dialogName) { - closeDialog(); + img.setAttribute('src', src); + }; + const root = {}; + const nodes = this._getImageNodes(h, root); + const {src} = root; + for (let i = nodes.length; i--;) { + const n = nodes[i]; + let imgNode = document.getElementById(n.ch); + if (imgNode && (imgNode = imgNode.querySelector('img'))) { + const parent = imgNode.parentNode; + const container = parent.parentNode; + if (src) { + container.classList.add('thumb'); + parent.classList.remove('no-thumb'); + } else { + container.classList.add('thumb-failed'); + } + n.seen = 2; + container.classList.remove('thumb-loading'); + setSource(n, imgNode, src || window.noThumbURI || ''); } + if (src) { + n.src = src; + if (root.srcBuffer && root.srcBuffer.byteLength) { + n.srcBuffer = root.srcBuffer; + } + if (n.srcBuffer && !previews[n.h] && is_image3(n)) { + preqs[n.h] = 1; + previewimg(n.h, n.srcBuffer, 'image/jpeg'); + previews[n.h].fromChat = Date.now(); + } + } + delete n.mo; } - render() { - return modalDialogs_React.createElement(ModalDialog, { - title: l.share_contact_title, - className: ` - send-contact - contrast - small-footer - dialog-template-tool - ${this.props.className} - ${this.dialogName} - `, - selected: this.state.selected, - buttons: [{ - key: "cancel", - label: this.props.cancelLabel, - onClick: ev => { - this.props.onClose(); - ev.preventDefault(); - ev.stopPropagation(); - } - }, { - key: "select", - label: this.props.selectLabel, - className: this.state.selected.length === 0 ? 'positive disabled' : 'positive', - onClick: ev => { - if (this.state.selected.length > 0) { - let _this$props$onSelecte2, _this$props3; - (_this$props$onSelecte2 = (_this$props3 = this.props).onSelected) == null || _this$props$onSelecte2.call(_this$props3, this.state.selected); - this.props.onSelectClicked(this.state.selected); - } - ev.preventDefault(); - ev.stopPropagation(); - } - }], - onClose: this.props.onClose - }, modalDialogs_React.createElement("section", { - className: "content" - }, modalDialogs_React.createElement("div", { - className: "content-block" - }, modalDialogs_React.createElement(ContactsUI.ContactPickerWidget, { - megaChat: this.props.megaChat, - exclude: this.props.exclude, - selectableContacts: "true", - onSelectDone: this.props.onSelectClicked, - onSelected: this.onSelected, - onClose: this.props.onClose, - selected: this.state.selected, - contacts: M.u, - headerClasses: "left-aligned", - multiple: true - })))); - } -} -SelectContactDialog.clickTime = 0; -SelectContactDialog.defaultProps = { - selectLabel: l.share_contact_action, - cancelLabel: l.msg_dlg_cancel, - hideable: true -}; -class ConfirmDialog extends mixins.w9 { - static saveState(o) { - const state = mega.config.get('xcod') >>> 0; - mega.config.set('xcod', state | 1 << o.props.pref); - } - static clearState(o) { - const state = mega.config.get('xcod') >>> 0; - mega.config.set('xcod', state & ~(1 << o.props.pref)); - } - static autoConfirm(o) { - console.assert(o.props.pref > 0); - const state = mega.config.get('xcod') >>> 0; - return !!(state & 1 << o.props.pref); - } - constructor(props) { - super(props); - this.dialogName = 'confirm-dialog'; - this._wasAutoConfirmed = undefined; - this._keyUpEventName = `keyup.confirmDialog${ this.getUniqueId()}`; - this.dialogName = this.props.name || this.dialogName; - lazy(this, '_autoConfirm', () => this.props.onConfirmClicked && this.props.dontShowAgainCheckbox && ConfirmDialog.autoConfirm(this)); - } - unbindEvents() { - $(document).off(this._keyUpEventName); + if (src) { + mBroadcaster.sendMessage('chat_image_preview'); } - componentDidMount() { - super.componentDidMount(); - M.safeShowDialog(this.dialogName, () => { - queueMicrotask(() => { - if (!this.isMounted()) { - return; - } - if (this._autoConfirm) { - if (!this._wasAutoConfirmed) { - this._wasAutoConfirmed = 1; - queueMicrotask(() => { - this.onConfirmClicked(); - }); - } - return; - } - $(document).rebind(this._keyUpEventName, e => { - if (e.which === 13 || e.keyCode === 13) { - if (!this.isMounted()) { - this.unbindEvents(); - return; - } - this.onConfirmClicked(); - return false; - } - }); - }); - }); +}; +Chat.prototype.onChatsHistoryReady = promisify(function (resolve, reject, timeout) { + if (this.allChatsHadInitialLoadedHistory()) { + return resolve(); } - componentWillUnmount() { - super.componentWillUnmount(); - this.unbindEvents(); - if ($.dialog === this.dialogName) { - closeDialog(); + let timer = null; + const {chatd} = this.plugins.chatdIntegration; + const eventName = `onMessagesHistoryDone.ochr${ makeid(16)}`; + const ready = () => { + queueMicrotask(resolve); + chatd.off(eventName); + if (timer) { + timer.abort(); + timer = null; } - delete this._wasAutoConfirmed; - } - onConfirmClicked() { - this.unbindEvents(); - if (this.props.onConfirmClicked) { - this.props.onConfirmClicked(); + }; + chatd.on(eventName, () => { + if (this.allChatsHadInitialLoadedHistory()) { + ready(); } + }); + if (timeout > 0) { + (timer = tSleep(timeout / 1e3)).then(ready); } - render() { - const self = this; - if (this._autoConfirm) { - return null; +}); +Chat.prototype.allChatsHadLoadedHistory = function () { + const chatIds = this.chats.keys(); + for (let i = chatIds.length; i--;) { + const room = this.chats[chatIds[i]]; + if (room.isLoading()) { + return false; } - const classes = `delete-message${ self.props.name ? ` ${self.props.name}` : "" }${self.props.className ? ` ${self.props.className}` : ""}`; - let dontShowCheckbox = null; - if (self.props.dontShowAgainCheckbox) { - dontShowCheckbox = modalDialogs_React.createElement("div", { - className: "footer-checkbox" - }, modalDialogs_React.createElement(ui_forms.Checkbox, { - name: "delete-confirm", - id: "delete-confirm", - onLabelClick: (e, state) => { - if (state === true) { - ConfirmDialog.saveState(self); - } else { - ConfirmDialog.clearState(self); - } - } - }, l[7039])); + } + return true; +}; +Chat.prototype.allChatsHadInitialLoadedHistory = function () { + const self = this; + const chatIds = self.chats.keys(); + for (let i = chatIds.length; i--;) { + const room = self.chats[chatIds[i]]; + if (room.chatId && room.initialMessageHistLoaded === false) { + return false; } - return modalDialogs_React.createElement(ModalDialog, { - title: this.props.title, - subtitle: this.props.subtitle, - className: classes, - dialogId: this.props.name, - dialogType: this.props.dialogType, - icon: this.props.icon, - onClose: () => { - self.props.onClose(self); - }, - buttons: [{ - "label": self.props.cancelLabel, - "key": "cancel", - "onClick" (e) { - ConfirmDialog.clearState(self); - self.props.onClose(self); - e.preventDefault(); - e.stopPropagation(); - } - }, { - "label": self.props.confirmLabel, - "key": "select", - "className": "positive", - "onClick" (e) { - self.onConfirmClicked(); - e.preventDefault(); - e.stopPropagation(); - } - }] - }, self.props.children, dontShowCheckbox ? modalDialogs_React.createElement(ExtraFooterElement, null, dontShowCheckbox) : null); } -} -lazy(ConfirmDialog, 'defaultProps', () => { - return freeze({ - 'confirmLabel': l[6826], - 'cancelLabel': l.msg_dlg_cancel, - 'dontShowAgainCheckbox': true, - 'hideable': true, - 'dialogType': 'message' - }); -}); -const modalDialogs = { - ModalDialog, - SelectContactDialog, - SafeShowDialogController, - ConfirmDialog + return true; }; +Chat.prototype.getPrivateRoom = function (h) { + 'use strict'; -}, - -486 -(_, EXP_, REQ_) { - -"use strict"; -REQ_.d(EXP_, { -O: () => PerfectScrollbar + return this.chats[h] || false; +}; +Chat.prototype.createAndShowPrivateRoom = promisify(function (resolve, reject, h) { + M.openFolder(`chat/p/${ h}`).then(() => { + const room = this.getPrivateRoom(h); + assert(room, 'room not found..'); + resolve(room); + }).catch(reject); }); -const _applyDecoratedDescriptor0__ = REQ_(793); -const _chat_mixins1__ = REQ_(137); - -let _dec, _dec2, _class, _PerfectScrollbar; -const React = REQ_(594); - -const PerfectScrollbar = (_dec = (0,_chat_mixins1__.hG)(30, true), _dec2 = (0,_chat_mixins1__.hG)(30, true), _class = (_PerfectScrollbar = class PerfectScrollbar extends _chat_mixins1__.w9 { - constructor(props) { - super(props); - this.domRef = React.createRef(); - this.isUserScroll = true; - this.scrollEventIncId = 0; - } - get$Node() { - if (!this.$Node) { - let _this$domRef; - this.$Node = $((_this$domRef = this.domRef) == null ? void 0 : _this$domRef.current); - } - return this.$Node; - } - doProgramaticScroll(newPos, forced, isX, skipReinitialised) { - if (!this.isMounted()) { - return; - } - const self = this; - const $elem = self.get$Node(); - let animFrameInner = false; - const prop = !isX ? 'scrollTop' : 'scrollLeft'; - const event = `scroll.progscroll${ self.scrollEventIncId++}`; - $elem.rebind(event, () => { - if (animFrameInner) { - cancelAnimationFrame(animFrameInner); - animFrameInner = false; - } - $elem.off(event); - if (!skipReinitialised) { - self.reinitialised(true); - } else if (typeof skipReinitialised === 'function') { - onIdle(skipReinitialised); - } - self.isUserScroll = true; - }); - self.isUserScroll = false; - $elem[0][prop] = Math.round(newPos); - Ps.update($elem[0]); - animFrameInner = requestAnimationFrame(() => { - animFrameInner = false; - self.isUserScroll = true; - $elem.off(event); +Chat.prototype.createAndShowGroupRoomFor = function (contactHashes, topic = '', opts = {}) { + this.trigger('onNewGroupChatRequest', [contactHashes, { + topic, + ...opts + }]); +}; +Chat.prototype.createAndStartMeeting = function (topic, audio, video) { + megaChat.createAndShowGroupRoomFor([], topic, { + keyRotation: false, + createChatLink: true, + isMeeting: true + }); + megaChat.rebind('onRoomInitialized.meetingCreate', (e, room) => { + room.rebind('onNewMeetingReady.meetingCreate', () => { + room.startCall(audio, video); }); - return true; - } - componentDidMount() { - let _this$props$didMount, _this$props; - super.componentDidMount(); - const self = this; - const $elem = self.get$Node(); - $elem.height('100%'); - const options = Object.assign({}, { - 'handlers': ['click-rail', 'drag-thumb', 'keyboard', 'wheel', 'touch'], - 'minScrollbarLength': 20 - }, self.props.options); - Ps.initialize($elem[0], options); - if (self.props.onFirstInit) { - self.props.onFirstInit(self, $elem); - } - $elem.rebind(`ps-scroll-y.ps${ self.getUniqueId()}`, (e) => { - if ($elem.attr('data-scroll-disabled') === "true") { - e.stopPropagation(); - e.preventDefault(); - e.originalEvent.stopPropagation(); - e.originalEvent.preventDefault(); - return false; - } - if (self.props.onUserScroll && self.isUserScroll === true && $elem.is(e.target)) { - self.props.onUserScroll(self, $elem, e); + }); +}; +Chat.prototype._destroyAllChatsFromChatd = function () { + const self = this; + asyncApiReq({ + 'a': 'mcf', + 'v': Chatd.VERSION + }).then(r => { + r.c.forEach((chatRoomMeta) => { + if (chatRoomMeta.g === 1) { + chatRoomMeta.u.forEach((u) => { + if (u.u !== u_handle) { + api_req({ + a: 'mcr', + id: chatRoomMeta.id, + u: u.u, + v: Chatd.VERSION + }); + } + }); + api_req({ + a: 'mcr', + id: chatRoomMeta.id, + u: u_handle, + v: Chatd.VERSION + }); } }); - $elem.rebind(`disable-scroll.ps${ self.getUniqueId()}`, () => { - Ps.destroy($elem[0]); - }); - $elem.rebind(`enable-scroll.ps${ self.getUniqueId()}`, () => { - Ps.initialize($elem[0], options); - }); - $elem.rebind(`forceResize.ps${ self.getUniqueId()}`, (e, forced, scrollPositionYPerc, scrollToElement) => { - self.onResize(forced, scrollPositionYPerc, scrollToElement); + }); +}; +Chat.prototype._leaveAllGroupChats = function () { + asyncApiReq({ + 'a': 'mcf', + 'v': Chatd.VERSION + }).then(r => { + r.c.forEach((chatRoomMeta) => { + if (chatRoomMeta.g === 1) { + asyncApiReq({ + "a": "mcr", + "id": chatRoomMeta.id, + "v": Chatd.VERSION + }); + } }); - self.onResize(); - this.attachAnimationEvents(); - (_this$props$didMount = (_this$props = this.props).didMount) == null || _this$props$didMount.call(_this$props, this.getUniqueId(), this); + }); +}; +Chat.prototype.getEmojiDataSet = async function (name) { + assert(name === "categories" || name === "emojis", "Invalid emoji dataset name passed."); + if (!this._emojiDataLoading) { + this._emojiDataLoading = Object.create(null); } - componentWillUnmount() { - let _this$props$willUnmou, _this$props2; - super.componentWillUnmount(); - const $elem = this.get$Node(); - $elem.off(`ps-scroll-y.ps${ this.getUniqueId()}`); - const ns = `.ps${ this.getUniqueId()}`; - $elem.parents('.have-animation').unbind(`animationend${ ns } webkitAnimationEnd${ ns } oAnimationEnd${ ns}`); - (_this$props$willUnmou = (_this$props2 = this.props).willUnmount) == null || _this$props$willUnmou.call(_this$props2, this.getUniqueId(), this); + if (!this._emojiData) { + this._emojiData = { + 'emojisUtf': Object.create(null), + 'emojisSlug': Object.create(null) + }; } - attachAnimationEvents() {} - eventuallyReinitialise(forced, scrollPositionYPerc, scrollToElement) { - const self = this; - if (!self.isComponentEventuallyVisible()) { + if (this._emojiData[name]) { + return this._emojiData[name]; + } + if (this._emojiDataLoading[name]) { + return this._emojiDataLoading[name]; + } + if (name === "categories") { + this._emojiData[name] = ["people", "nature", "food", "activity", "travel", "objects", "symbols", "flags"]; + return this._emojiData[name]; + } + const { + promise + } = mega; + this._emojiDataLoading[name] = promise; + M.xhr({ + type: 'json', + url: `${staticpath}js/chat/emojidata/${name}_v${EMOJI_DATASET_VERSION}.json` + }).then((ev, data) => { + if (!data) { + promise.reject(EFAILED); return; } - const $elem = self.get$Node(); - const h = self.getContentHeight(); - if (forced || self._currHeight !== h) { - self._currHeight = h; - self._doReinit(scrollPositionYPerc, scrollToElement, forced, $elem); + this._emojiData[name] = data; + delete this._emojiDataLoading[name]; + if (name === "emojis") { + this._mapEmojisToAliases(); } - } - _doReinit(scrollPositionYPerc, scrollToElement, forced, $elem) { - let fired = false; - if (this.props.onReinitialise) { - fired = this.props.onReinitialise(this, $elem, forced, scrollPositionYPerc, scrollToElement); + promise.resolve(data); + }).catch((ex, error) => { + if (d) { + this.logger.warn('Failed to load emoji data "%s": %s', name, error, [ex]); } - if (fired === false) { - if (scrollPositionYPerc) { - if (scrollPositionYPerc === -1) { - this.scrollToBottom(true); - } else { - this.scrollToPercentY(scrollPositionYPerc, true); - } - } else if (scrollToElement) { - this.scrollToElement(scrollToElement, true); - } + delete this._emojiDataLoading[name]; + promise.reject(error || ex); + }); + return promise; +}; +Chat.prototype._mapEmojisToAliases = function () { + const { + emojis + } = this._emojiData; + if (emojis) { + this._emojiData.emojisUtf = Object.create(null); + this._emojiData.emojisSlug = Object.create(null); + for (let i = emojis.length; i--;) { + const emoji = emojis[i]; + this._emojiData.emojisUtf[emoji.u] = emoji; + this._emojiData.emojisSlug[emoji.n] = emoji; } } - scrollToBottom(skipReinitialised) { - this.reinitialise(skipReinitialised, true); +}; +Chat.prototype.isValidEmojiSlug = function (slug) { + const self = this; + const emojiData = self._emojiData.emojis; + if (!emojiData) { + self.getEmojiDataSet('emojis'); + return false; } - reinitialise(skipReinitialised, bottom) { - let _this$domRef2; - const $elem = (_this$domRef2 = this.domRef) == null ? void 0 : _this$domRef2.current; - if (!$elem) { - return; - } - this.isUserScroll = false; - if (bottom) { - $elem.scrollTop = this.getScrollHeight(); - } - Ps.update($elem); - this.isUserScroll = true; - if (!skipReinitialised) { - this.reinitialised(true); + for (let i = 0; i < emojiData.length; i++) { + if (emojiData[i].n === slug) { + return true; } } - getDOMRect(node) { - let _this$domRef3; - node = node || ((_this$domRef3 = this.domRef) == null ? void 0 : _this$domRef3.current); - return node && node.getBoundingClientRect(); +}; +Chat.prototype.getPresence = function (user_handle) { + if (user_handle && this.plugins.presencedIntegration) { + return this.plugins.presencedIntegration.getPresence(user_handle); } - getScrollOffset(value) { - let _this$domRef4; - const $elem = (_this$domRef4 = this.domRef) == null ? void 0 : _this$domRef4.current; - if ($elem) { - return this.getDOMRect($elem.children[0])[value] - this.getDOMRect($elem)[value]; +}; +Chat.prototype.getPresenceAsCssClass = function (user_handle) { + const presence = this.getPresence(user_handle); + return this.presenceStringToCssClass(presence); +}; +Chat.prototype.presenceStringToCssClass = function (presence) { + if (presence === UserPresence.PRESENCE.ONLINE) { + return 'online'; + } else if (presence === UserPresence.PRESENCE.AWAY) { + return 'away'; + } else if (presence === UserPresence.PRESENCE.DND) { + return 'busy'; + } else if (!presence || presence === UserPresence.PRESENCE.OFFLINE) { + return 'offline'; + } else { + return 'black'; + } +}; +Chat.prototype.generateTempMessageId = function (roomId, messageAndMeta) { + let messageIdHash = u_handle + roomId; + if (messageAndMeta) { + messageIdHash += messageAndMeta; + } + return `m${ fastHashFunction(messageIdHash) }_${ unixtime()}`; +}; +Chat.prototype.getChatById = function (chatdId) { + const self = this; + if (self.chats[chatdId]) { + return self.chats[chatdId]; + } else if (self.chatIdToRoomId && self.chatIdToRoomId[chatdId] && self.chats[self.chatIdToRoomId[chatdId]]) { + return self.chats[self.chatIdToRoomId[chatdId]]; + } + if (this.chats[this.handleToId[chatdId]]) { + return this.chats[this.handleToId[chatdId]]; + } + let found = false; + self.chats.forEach((chatRoom) => { + if (!found && chatRoom.chatId === chatdId) { + found = chatRoom; + return false; } - return 0; + }); + return found; +}; +Chat.prototype.getNoteChat = function () { + return Object.values(this.chats).find(c => c.isNote); +}; +Chat.prototype.getMessageByMessageId = async function (chatId, messageId) { + const chatRoom = this.getChatById(chatId); + const msg = chatRoom.messagesBuff.getMessageById(messageId); + if (msg) { + return msg; } - getScrollHeight() { - const res = this.getScrollOffset('height'); - if (res < 1) { - return this._lastKnownScrollHeight || 0; + const { + chatdPersist + } = this.plugins.chatdIntegration.chatd; + if (chatdPersist) { + const [msg] = await chatdPersist.getMessageByMessageId(chatId, messageId).catch(dump) || []; + if (msg) { + return Message.fromPersistableObject(chatRoom, msg); } - this._lastKnownScrollHeight = res; - return res; } - getScrollWidth() { - const res = this.getScrollOffset('width'); - if (res < 1) { - return this._lastKnownScrollWidth || 0; + if (d) { + this.logger.debug('getMessageByMessageId: Cannot find %s on %s', messageId, chatId); + } + return Promise.reject(ENOENT); +}; +Chat.prototype.haveAnyActiveCall = function () { + const self = this; + const chatIds = self.chats.keys(); + for (let i = 0; i < chatIds.length; i++) { + if (self.chats[chatIds[i]].haveActiveCall()) { + return true; + } + } + return false; +}; +Chat.prototype.haveAnyOnHoldCall = function () { + const self = this; + const chatIds = self.chats.keys(); + for (let i = 0; i < chatIds.length; i++) { + if (self.chats[chatIds[i]].haveActiveOnHoldCall()) { + return true; } - this._lastKnownScrollWidth = res; - return res; - } - getContentHeight() { - const $elem = this.get$Node(); - return $elem[0].scrollHeight; } - getContentWidth() { - const $elem = this.get$Node(); - return $elem[0].scrollWidth; + return false; +}; +Chat.prototype.openChatAndSendFilesDialog = function (user_handle) { + 'use strict'; + + this.smartOpenChat(user_handle).then((room) => { + if (room.$rConversationPanel && room.$rConversationPanel.isMounted()) { + room.trigger('openSendFilesDialog'); + } else { + room.one('onComponentDidMount.sendFilesDialog', () => { + onIdle(() => room.trigger('openSendFilesDialog')); + }); + } + room.setActive(); + }).catch(this.logger.error.bind(this.logger)); +}; +Chat.prototype.openChatAndAttachNodes = async function (targets, nodes, silent) { + const promises = []; + if (d) { + console.group('Attaching nodes to chat room(s)...', targets, nodes); } - setCssContentHeight(h) { - const $elem = this.get$Node(); - return $elem.css('height', h); + const attachNodes = roomId => this.smartOpenChat(roomId).then(room => { + return room.attachNodes(nodes).then(res => { + if (res !== EBLOCKED && res !== ENOENT) { + return room; + } + }); + }).catch(ex => { + if (d) { + this.logger.warn('Cannot openChat for %s and hence nor attach nodes to it.', roomId, ex); + } + throw ex; + }); + if (!Array.isArray(targets)) { + targets = [targets]; } - isAtTop() { - let _this$domRef5; - return ((_this$domRef5 = this.domRef) == null ? void 0 : _this$domRef5.current.scrollTop) === 0; + for (let i = targets.length; i--;) { + promises.push(attachNodes(targets[i])); } - isAtBottom() { - return Math.round(this.getScrollPositionY()) === Math.round(this.getScrollHeight()); + const result = (await Promise.allSettled(promises)).map(e => e.value).filter(Boolean); + let folderCount = 0; + let fileCount = 0; + for (let i = nodes.length; i--;) { + const { + t + } = M.getNodeByHandle(nodes[i]) || {}; + if (t === 1) { + folderCount++; + } else { + fileCount++; + } } - isCloseToBottom(minPixelsOff) { - return this.getScrollHeight() - this.getScrollPositionY() <= minPixelsOff; + let message = mega.icu.format(l.toast_send_chat_items, nodes.length); + if (fileCount === 0 && folderCount) { + message = mega.icu.format(l.toast_send_chat_folders, folderCount); + } else if (folderCount === 0 && fileCount) { + message = mega.icu.format(l.toast_send_chat_files, fileCount); } - getScrolledPercentY() { - return 100 / this.getScrollHeight() * this.getScrollPositionY(); + for (let i = result.length; i--;) { + if (result[i] instanceof ChatRoom) { + const room = result[i]; + mega.ui.toast.show(message); + if (!silent) { + await M.openFolder(room.getRoomUrl().replace('fm/', '')).catch(dump); + } + break; + } } - getScrollPositionY() { - let _this$domRef6; - return (_this$domRef6 = this.domRef) == null ? void 0 : _this$domRef6.current.scrollTop; + if (d) { + console.groupEnd(); } - getScrollPositionX() { - let _this$domRef7; - return (_this$domRef7 = this.domRef) == null ? void 0 : _this$domRef7.current.scrollLeft; + return result; +}; +Chat.prototype.toggleUIFlag = function (name) { + this.chatUIFlags.set(name, this.chatUIFlags[name] ? 0 : 1); +}; +Chat.prototype.onSnActionPacketReceived = function () { + if (this._queuedMccPackets.length > 0) { + const aps = this._queuedMccPackets; + this._queuedMccPackets = []; + for (let i = 0; i < aps.length; i++) { + mBroadcaster.sendMessage('onChatdChatUpdatedActionPacket', aps[i]); + } } - getClientWidth() { - let _this$domRef8; - return (_this$domRef8 = this.domRef) == null ? void 0 : _this$domRef8.current.clientWidth; + this.processQueuedMcsmPackets(); +}; +Chat.prototype.processQueuedMcsmPackets = function () { + const aps = Object.values(this._queuedMcsmPackets); + if (aps.length) { + for (let i = 0; i < aps.length; i++) { + const ap = aps[i]; + const { + type, + data + } = ap; + const { + meetingsManager + } = this.plugins; + if (type === 'mcsmp') { + const chatRoom = this.getChatById(data.cid); + if (chatRoom) { + const scheduledMeeting = meetingsManager.attachMeeting(data, true); + delete this._queuedMcsmPackets[scheduledMeeting.id]; + return scheduledMeeting.iAmOwner ? null : notify.notifyFromActionPacket({ + ...data, + a: type + }); + } + } + if (type === 'mcsmr') { + meetingsManager.detachMeeting(data); + delete this._queuedMcsmPackets[data.id]; + } + } } - getClientHeight() { - let _this$domRef9; - return (_this$domRef9 = this.domRef) == null ? void 0 : _this$domRef9.current.clientHeight; +}; +Chat.prototype.getFrequentContacts = function () { + if (Chat._frequentsCache) { + return Chat._frequentsCache; } - scrollToPercentY(posPerc, skipReinitialised) { - const $elem = this.get$Node(); - const targetPx = this.getScrollHeight() / 100 * posPerc; - if ($elem[0].scrollTop !== targetPx) { - this.doProgramaticScroll(targetPx, 0, 0, skipReinitialised); + const {chats} = this; + const recentContacts = {}; + const promises = []; + const finishedLoadingChats = {}; + const loadingMoreChats = {}; + const _calculateLastTsFor = function (r, maxMessages) { + const mb = r.messagesBuff; + const len = mb.messages.length; + const msgs = mb.messages.slice(Math.max(0, len - maxMessages), len); + for (let i = 0; i < msgs.length; i++) { + const msg = msgs[i]; + let contactHandle = msg.userId === mega.BID && msg.meta ? msg.meta.userId : msg.userId; + if (r.type === "private" && contactHandle === u_handle) { + contactHandle = contactHandle || r.getParticipantsExceptMe()[0]; + } + if (contactHandle !== mega.BID && contactHandle !== strongvelope.COMMANDER && contactHandle in M.u && M.u[contactHandle].c === 1 && contactHandle !== u_handle) { + if (!recentContacts[contactHandle] || recentContacts[contactHandle].ts < msg.delay) { + recentContacts[contactHandle] = { + 'userId': contactHandle, + 'ts': msg.delay + }; + } + } } - } - scrollToPercentX(posPerc, skipReinitialised) { - const $elem = this.get$Node(); - const targetPx = this.getScrollWidth() / 100 * posPerc; - if ($elem[0].scrollLeft !== targetPx) { - this.doProgramaticScroll(targetPx, false, true, skipReinitialised); + }; + const _histDecryptedCb = function () { + const mb = this.messagesBuff; + if (!loadingMoreChats[this.chatId] && mb.messages.length < 32 && mb.haveMoreHistory()) { + loadingMoreChats[this.chatId] = true; + mb.retrieveChatHistory(false); + } else { + this.unbind(CHAT_ONHISTDECR_RECNT); + _calculateLastTsFor(this, 32); + delete loadingMoreChats[this.chatId]; + finishedLoadingChats[this.chatId] = true; + mb.detachMessages(); + } + }; + const _checkFinished = function (chatId) { + return function () { + return finishedLoadingChats[chatId] === true; + }; + }; + chats.forEach(chatRoom => { + const name = `getFrequentContacts(${chatRoom.roomId})`; + if (chatRoom.isLoading()) { + finishedLoadingChats[chatRoom.chatId] = false; + chatRoom.rebind(CHAT_ONHISTDECR_RECNT, _histDecryptedCb); + promises.push(createTimeoutPromise(_checkFinished(chatRoom.chatId), 300, 10000, false, name)); + } else if (chatRoom.messagesBuff.messages.length < 32 && chatRoom.messagesBuff.haveMoreHistory()) { + loadingMoreChats[chatRoom.chatId] = true; + finishedLoadingChats[chatRoom.chatId] = false; + chatRoom.messagesBuff.retrieveChatHistory(false); + chatRoom.rebind(CHAT_ONHISTDECR_RECNT, _histDecryptedCb); + promises.push(createTimeoutPromise(_checkFinished(chatRoom.chatId), 300, 15000, false, name)); + } else { + _calculateLastTsFor(chatRoom, 32); + } + }); + Chat._frequentsCache = new Promise((resolve, reject) => { + Promise.allSettled(promises).then(() => { + const result = Object.values(recentContacts).sort((a, b) => a.ts < b.ts ? 1 : b.ts < a.ts ? -1 : 0).reverse(); + tSleep(300).then(() => { + delete Chat._frequentsCache; + }); + return result; + }).then(resolve).catch(reject); + }); + return Chat._frequentsCache; +}; +Chat.prototype.lastRoomContacts = async function (chatRoom) { + let timeout; + let loaded = false; + let loadMore = false; + const { + promise + } = mega; + const proc = () => { + if (timeout) { + timeout.abort(); + } + const { + messages + } = chatRoom.messagesBuff; + const arr = messages.slice(Math.max(0, messages.length - 32)); + let first = ''; + let second = ''; + for (let i = arr.length; i--;) { + const message = arr[i]; + const h = message.userId === mega.BID && message.meta ? message.meta.userId : message.userId; + if (h !== mega.BID && h !== strongvelope.COMMANDER && h !== u_handle && h in M.u && M.u[h].c === 1) { + if (first && first !== h) { + second = h; + break; + } + first = h; + } + } + if (second) { + promise.resolve([first, second]); + } else if (first) { + promise.resolve([first]); + } else { + promise.resolve([]); } - } - scrollToY(posY, skipReinitialised) { - const $elem = this.get$Node(); - if ($elem[0].scrollTop !== posY) { - this.doProgramaticScroll(posY, 0, 0, skipReinitialised); + chatRoom.messagesBuff.detachMessages(); + }; + const next = () => { + if (!loadMore && chatRoom.messagesBuff.messages.length < 32 && chatRoom.messagesBuff.haveMoreHistory()) { + if (timeout) { + timeout.restart(); + } + loadMore = true; + chatRoom.messagesBuff.retrieveChatHistory(false); + } else { + chatRoom.off('onHistoryDecrypted.lrc'); + proc(); } + }; + if (chatRoom.isLoading()) { + loaded = false; + chatRoom.rebind('onHistoryDecrypted.lrc', next); + timeout = tSleep(10); + } else if (chatRoom.messagesBuff.messages.length < 32 && chatRoom.messagesBuff.haveMoreHistory()) { + loaded = false; + loadMore = true; + chatRoom.rebind('onHistoryDecrypted.lrc', next); + chatRoom.messagesBuff.retrieveChatHistory(false); + timeout = tSleep(10); + } else { + proc(); } - scrollToElement(element, skipReinitialised) { - if (element && element.offsetParent) { - this.doProgramaticScroll(element.offsetTop, 0, 0, skipReinitialised); - } + if (timeout) { + timeout.then(() => { + if (!loaded) { + chatRoom.off('onHistoryDecrypted.lrc'); + promise.resolve([]); + } + }); } - disable() { - if (this.isMounted()) { - const $elem = this.get$Node(); - $elem.attr('data-scroll-disabled', true); - $elem.addClass('ps-disabled'); - Ps.disable($elem[0]); - } + return promise; +}; +Chat.prototype.eventuallyAddDldTicketToReq = function (req) { + if (!u_handle) { + return; } - enable() { - if (this.isMounted()) { - const $elem = this.get$Node(); - $elem.removeAttr('data-scroll-disabled'); - $elem.removeClass('ps-disabled'); - Ps.enable($elem[0]); - } + const currentRoom = this.getCurrentRoom(); + if (currentRoom && currentRoom.type === "public" && currentRoom.publicChatHandle && (is_chatlink || currentRoom.membersSetFromApi && !currentRoom.membersSetFromApi.members[u_handle])) { + req.cauth = currentRoom.publicChatHandle; } - reinitialised(forced) { - if (this.props.onReinitialise) { - this.props.onReinitialise(this, this.get$Node(), forced ? forced : false); +}; +Chat.prototype.removeMessagesByRetentionTime = function (chatId) { + if (this.chats.length > 0) { + if (chatId) { + if (this.logger && d > 3) { + this.logger.debug(`Chat.prototype.removeMessagesByRetentionTime chatId=${chatId}`); + } + const room = this.getChatById(chatId); + if (room) { + room.removeMessagesByRetentionTime(); + } + return; } - } - onResize(forced, scrollPositionYPerc, scrollToElement) { - if (forced && forced.originalEvent) { - forced = true; - scrollPositionYPerc = undefined; + const chatIds = this.chats.keys(); + for (let i = 0; i < chatIds.length; i++) { + const chatRoom = this.chats[chatIds[i]]; + if (chatRoom.retentionTime > 0 && chatRoom.state === ChatRoom.STATE.READY) { + if (this.logger && d > 3) { + this.logger.debug(`Chat.prototype.removeMessagesByRetentionTime roomId=${chatRoom.roomId}`); + } + chatRoom.removeMessagesByRetentionTime(); + } } - this.eventuallyReinitialise(forced, scrollPositionYPerc, scrollToElement); } - inViewport(domNode) { - return verge.inViewport(domNode); +}; +Chat.prototype.loginOrRegisterBeforeJoining = function (chatHandle, forceRegister, forceLogin, notJoinReq, onLoginSuccessCb) { + if (!chatHandle && page !== 'securechat' && (page === 'chat' || page.indexOf('chat') > -1)) { + chatHandle = getSitePath().split("chat/")[1].split("#")[0]; } - componentDidUpdate() { - if (this.props.requiresUpdateOnResize || this.requiresUpdateOnResize) { - this.onResize(true); + assert(chatHandle, 'missing chat handle when calling megaChat.loginOrRegisterBeforeJoining'); + const chatRoom = megaChat.getCurrentRoom(); + const chatKey = `#${ window.location.hash.split("#").pop()}`; + const finish = function (stay) { + if (!notJoinReq) { + localStorage.autoJoinOnLoginChat = JSON.stringify([chatHandle, unixtime(), chatKey, chatRoom.chatId]); } - this.attachAnimationEvents(); - } - customIsEventuallyVisible() { - const {chatRoom} = this.props; - return !chatRoom || chatRoom.isCurrentlyActive; - } - render() { - const { - style, - className, - children - } = this.props; - return React.createElement("div", { - ref: this.domRef, - style, - className - }, children); - } -}, _PerfectScrollbar.defaultProps = { - className: "perfectScrollbarContainer", - requiresUpdateOnResize: true -}, _PerfectScrollbar), (0,_applyDecoratedDescriptor0__.A)(_class.prototype, "eventuallyReinitialise", [_dec], Object.getOwnPropertyDescriptor(_class.prototype, "eventuallyReinitialise"), _class.prototype), (0,_applyDecoratedDescriptor0__.A)(_class.prototype, "onResize", [_dec2], Object.getOwnPropertyDescriptor(_class.prototype, "onResize"), _class.prototype), _class); - -}, - -314 -(_, EXP_, REQ_) { - -"use strict"; -REQ_.d(EXP_, { -Ay: () => __WEBPACK_DEFAULT_EXPORT__, -P9: () => ParsedHTML, -T9: () => withOverflowObserver, -lI: () => reactStringWrap, -oM: () => OFlowParsedHTML, -sp: () => OFlowEmoji, -zT: () => Emoji -}); -const react0__ = REQ_(594); -const react0 = REQ_.n(react0__); -const react_dom1__ = REQ_(206); -const react_dom1 = REQ_.n(react_dom1__); -const _chat_mixins_js2__ = REQ_(137); - - - -class RenderTo extends react0().Component { - constructor(...args) { - super(...args); - this.$$rootRef = undefined; - this.popupElement = undefined; - } - _setClassNames() { - this.popupElement.className = this.props.className || ''; - } - _renderLayer() { - this.$$rootRef.render(this.props.children); - queueMicrotask(() => { - let _this$props$popupDidM, _this$props; - return (_this$props$popupDidM = (_this$props = this.props).popupDidMount) == null ? void 0 : _this$props$popupDidM.call(_this$props, this.popupElement); - }); - } - componentDidUpdate() { - this._setClassNames(); - this._renderLayer(); - } - componentWillUnmount() { - let _this$props$popupWill, _this$props2; - onIdle(() => this.$$rootRef.unmount()); - (_this$props$popupWill = (_this$props2 = this.props).popupWillUnmount) == null || _this$props$popupWill.call(_this$props2, this.popupElement); - this.props.element.removeChild(this.popupElement); - } - componentDidMount() { - this.popupElement = document.createElement('div'); - this.$$rootRef = (0,react_dom1__.createRoot)(this.popupElement); - this._setClassNames(); - if (this.props.style) { - $(this.popupElement).css(this.props.style); + if (!stay) { + window.location.reload(); } - this.props.element.appendChild(this.popupElement); - this._renderLayer(); + return stay; + }; + const doShowLoginDialog = function () { + mega.ui.showLoginRequiredDialog({ + minUserType: 3, + skipInitialDialog: 1, + onLoginSuccessCb + }).done(() => { + if (page !== 'login' && onLoginSuccessCb) { + onLoginSuccessCb(); + } + }); + }; + const doShowRegisterDialog = function () { + mega.ui.showRegisterDialog({ + title: l[5840], + onCreatingAccount () {}, + onLoginAttemptFailed () { + msgDialog(`warninga:${ l[171]}`, l[1578], l[218], null, (e) => { + if (e) { + $('.pro-register-dialog').addClass('hidden'); + if (signupPromptDialog) { + signupPromptDialog.hide(); + } + doShowLoginDialog(); + } + }); + }, + onAccountCreated (gotLoggedIn, registerData) { + if (finish(!gotLoggedIn)) { + security.register.cacheRegistrationData(registerData); + mega.ui.sendSignupLinkDialog(registerData); + megaChat.destroy(); + } + }, + onLoginSuccessCb + }); + }; + if (u_handle && u_handle !== "AAAAAAAAAAA") { + return finish(); } - render() { - return null; + if (forceRegister) { + return doShowRegisterDialog(); + } else if (forceLogin) { + return doShowLoginDialog(); } -} -const withOverflowObserver = Component => class extends _chat_mixins_js2__.u9 { - constructor(props) { - super(props); - this.displayName = 'OverflowObserver'; - this.ref = react0().createRef(); - this.state = { - overflowed: false - }; - this.handleMouseEnter = this.handleMouseEnter.bind(this); + if (u_wasloggedin()) { + doShowLoginDialog(); + } else { + doShowRegisterDialog(); } - handleMouseEnter() { - const element = this.ref && this.ref.current; - if (element) { - this.setState({ - overflowed: element.scrollWidth > element.offsetWidth - }); +}; +Chat.prototype.highlight = (text, matches, dontEscape) => { + if (text && matches) { + text = dontEscape ? text : escapeHTML(text); + const tags = []; + text = text.replace(/<[^>]+>/g, match => `@@!${ tags.push(match) - 1 }!@@`).split(' '); + const done = []; + for (let i = 0; i < matches.length; i++) { + const match = matches[i].str; + if (!done.includes(match)) { + done.push(match); + for (let j = 0; j < text.length; j++) { + const word = text[j]; + const wordNormalized = ChatSearch._normalize_str(word); + const matchPos = wordNormalized.indexOf(match); + if (matchPos > -1) { + const split = wordNormalized.split(match); + text[j] = wordNormalized === word ? split.join(`[$]${match}[/$]`) : megaChat._highlightDiacritics(word, matchPos, split, match); + } + } + } } + text = text.join(' ').replace(/\@\@\!\d+\!\@\@/g, match => { + return tags[parseInt(match.replace("@@!", "").replace("!@@"), 10)]; + }); + return text.replace(/\[\$]/g, '').replace(/\[\/\$]/g, ''); } - shouldComponentUpdate(nextProps, nextState) { - return nextState.overflowed !== this.state.overflowed || nextProps.children !== this.props.children || nextProps.content !== this.props.content; + return null; +}; +Chat.prototype._highlightDiacritics = function (word, matchPos, split, match) { + const parts = []; + const origMatch = word.substring(matchPos, matchPos + match.length); + let pos = 0; + for (let k = 0; k < split.length; k++) { + parts.push(word.substring(pos, pos + split[k].length)); + pos = pos + split[k].length + match.length; } - render() { - const { - simpletip - } = this.props; - return react0().createElement("div", { - ref: this.ref, - className: ` - overflow-observer - ${this.state.overflowed ? 'simpletip simpletip-tc' : ''} - `, - "data-simpletipposition": (simpletip == null ? void 0 : simpletip.position) || 'top', - "data-simpletipoffset": simpletip == null ? void 0 : simpletip.offset, - "data-simpletip-class": (simpletip == null ? void 0 : simpletip.className) || 'medium-width center-align', - onMouseEnter: this.handleMouseEnter - }, react0().createElement(Component, this.props)); + return parts.join(`[$]${origMatch}[/$]`); +}; +Chat.prototype.html = function (content) { + if (content) { + return this.plugins.emoticonsFilter.processHtmlMessage(escapeHTML(content)); } + return ''; }; -const Emoji = ({ - children -}) => { - return react0().createElement(ParsedHTML, { - content: megaChat.html(children) +Chat.prototype.updateKeysInProtocolHandlers = function () { + this.chats.forEach(r => { + const ph = r.protocolHandler; + if (ph) { + ph.reinitWithNewData(u_handle, u_privCu25519, u_privEd25519, u_pubEd25519, ph.chatMode); + } }); }; -class ParsedHTML extends react0().Component { - constructor(...args) { - super(...args); - this.ref = react0().createRef(); +Chat.prototype.eventuallyInitMeetingUI = function () { + if (!window.location.hash) { + return; } - updateInternalState() { - const { - children, - content - } = this.props; - const ref = this.ref && this.ref.current; - if (!children && !content) { - return d > 1 && console.warn('Emoji: No content passed.'); + let loc = page.split("#")[0]; + loc = loc.replace("fm/", "/"); + if (loc.indexOf("chat/") === 0) { + this.initialPubChatHandle = loc.substr(5).split("?")[0]; + } +}; +Chat.prototype.enqueueChatRoomEvent = function (eventName, eventData) { + if (!this.is_initialized) { + return; + } + const { + chatId + } = eventData; + if (!this._queuedChatRoomEvents[chatId]) { + this._queuedChatRoomEvents[chatId] = []; + (this._queuedChatRoomEvents[chatId].timer = tSleep(15)).then(() => { + if (d) { + this.logger.warn('Timer ran out, events lost...', this._queuedChatRoomEvents[chatId]); + } + delete this._queuedChatRoomEvents[chatId]; + }); + } + this._queuedChatRoomEvents[chatId].push([eventName, eventData]); +}; +Chat.prototype.autoJoinIfNeeded = function () { + const rawAutoLoginInfo = localStorage.autoJoinOnLoginChat; + if (u_type && rawAutoLoginInfo) { + const autoLoginChatInfo = tryCatch(JSON.parse.bind(JSON))(rawAutoLoginInfo) || false; + if (unixtime() - 7200 < autoLoginChatInfo[1]) { + const req = this.plugins.chatdIntegration.getMciphReqFromHandleAndKey(autoLoginChatInfo[0], autoLoginChatInfo[2].substr(1)); + megaChat.rebind('onRoomInitialized.autoJoin', (e, megaRoom) => { + if (megaRoom.chatId === autoLoginChatInfo[3]) { + megaRoom.setActive(); + megaChat.unbind('onRoomInitialized.autoJoin'); + localStorage.removeItem("autoJoinOnLoginChat"); + } + }); + asyncApiReq(req).catch(dump); + } else { + localStorage.removeItem("autoJoinOnLoginChat"); } - if (ref) { - if (ref.childNodes.length) { - while (ref.firstChild) { - ref.removeChild(ref.firstChild); + } +}; +Chat.prototype.openScheduledMeeting = function (meetingId, toCall) { + const meeting = this.scheduledMeetings[meetingId]; + if (!meeting) { + console.warn('Meeting does not exist', meetingId); + return; + } + window.focus(); + meeting.chatRoom.activateWindow(); + meeting.chatRoom.show(); + if (toCall && this.hasSupportForCalls) { + this.openScheduledMeeting._queue = this.openScheduledMeeting._queue || []; + this.openScheduledMeeting._queue.push(meetingId); + delay('megachat:openScheduledMeetingCall', () => { + const meetingId = this.openScheduledMeeting._queue[0]; + delete this.openScheduledMeeting._queue; + const meetingRoom = this.scheduledMeetings[meetingId].chatRoom; + meetingRoom.activateWindow(); + meetingRoom.show(); + const haveCall = this.haveAnyActiveCall(); + if (haveCall && window.sfuClient) { + const { + chatRoom + } = this.activeCall; + if (chatRoom && chatRoom.chatId === meetingRoom.chatId) { + const peers = chatRoom.getCallParticipants(); + if (peers.includes(u_handle)) { + return d && console.warn('Already in this call'); + } } } - ref.appendChild(parseHTML(children || content)); - } + (0,utils.dQ)(true, meetingRoom).then(() => meetingRoom.startAudioCall(true)).catch(ex => d && console.warn('Already in a call.', ex)); + }); } - shouldComponentUpdate(nextProps) { - return nextProps && (nextProps.children !== this.props.children || nextProps.content !== this.props.content); +}; +Chat.prototype.playSound = tryCatch((sound, options, stop) => { + if (options === true) { + stop = true; + options = undefined; } - componentDidUpdate() { - this.updateInternalState(); + if (stop) { + ion.sound.stop(sound); } - componentDidMount() { - this.updateInternalState(); + return ion.sound.play(sound, options); +}); +Chat.prototype.fetchSoundBuffer = async function (sound) { + if (this.SOUNDS.buffers[sound]) { + return this.SOUNDS.buffers[sound].slice(); } - render() { - const { - className, - onClick, - tag - } = this.props; - return react0().createElement(tag || 'span', { - ref: this.ref, - className, - onClick + let res = await M.xhr({ + url: `${staticpath}media/${sound}.mp3`, + type: 'arraybuffer' + }).catch(() => { + console.warn('Failed to fetch sound .mp3 file', sound); + }); + if (!res) { + res = await M.xhr({ + url: `${staticpath}media/${sound}.ogg`, + type: 'arraybuffer' + }).catch(() => { + console.error('Failed to fetch sound .ogg file', sound); }); } -} -const reactStringWrap = (src, find, WrapClass, wrapProps) => { - const endTag = find.replace('[', '[/'); - return react0().createElement(react0().Fragment, null, src.split(find)[0], react0().createElement(WrapClass, wrapProps, src.substring(src.indexOf(find) + find.length, src.indexOf(endTag))), src.split(endTag)[1]); + if (!res) { + throw ENOENT; + } + this.SOUNDS.buffers[sound] = res.target.response.slice(); + return res.target.response; }; -const OFlowEmoji = withOverflowObserver(Emoji); -const OFlowParsedHTML = withOverflowObserver(ParsedHTML); -const __WEBPACK_DEFAULT_EXPORT__ = { - RenderTo, - SoonFcWrap: _chat_mixins_js2__.hG, - OFlowEmoji, - OFlowParsedHTML +window.Chat = Chat; + const chat = { + Chat }; +})(); -}, - -594 -(module) { - -"use strict"; -module.exports = React; - -}, - -206 -(module) { - -"use strict"; -module.exports = ReactDOM; - -}, - -793 -(__webpack_module__, EXP_, REQ_) { - -"use strict"; -REQ_.d(EXP_, { -A: () => _applyDecoratedDescriptor -}); -function _applyDecoratedDescriptor(i, e, r, n, l) { - let a = {}; - return Object.keys(n).forEach((i) => { - a[i] = n[i]; - }), a.enumerable = !!a.enumerable, a.configurable = !!a.configurable, ("value" in a || a.initializer) && (a.writable = !0), a = r.slice().reverse().reduce((r, n) => { - return n(i, e, r) || r; - }, a), l && void 0 !== a.initializer && (a.value = a.initializer ? a.initializer.call(l) : void 0, a.initializer = void 0), void 0 === a.initializer ? (Object.defineProperty(i, e, a), null) : a; -} - - -}, - -168 -(__webpack_module__, EXP_, REQ_) { - -"use strict"; -REQ_.d(EXP_, { -A: () => _extends -}); -function _extends() { - return _extends = Object.assign ? Object.assign.bind() : function (n) { - for (let e = 1; e < arguments.length; e++) { - const t = arguments[e]; - for (const r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); - } - return n; - }, _extends.apply(null, arguments); -} - - -} - - }; - - // The module cache - const __webpack_module_cache__ =Object.create(null); - - // The require function - function REQ_(moduleId) { - // Check if module is in cache - const cachedModule = __webpack_module_cache__[moduleId]; - if (cachedModule !== undefined) { - return cachedModule.exports; - } - // Create a new module (and put it into the cache) - const module = __webpack_module_cache__[moduleId] = { - // no module.id needed - // no module.loaded needed - exports:Object.create(null) - }; - - // Execute the module function - __webpack_modules__[moduleId](module, module.exports, REQ_); - - // Return the exports of the module - return module.exports; - } - - - - (() => { - // getDefaultExport function for compatibility with non-harmony modules - REQ_.n = (module) => { - const getter = module && module.__esModule ? - () => module.default : - () => module; - REQ_.d(getter, { a: getter }); - return getter; - }; - })(); - - - (() => { - // define getter functions for harmony exports - REQ_.d = (exports, definition) => { - for(const key in definition) { - if(REQ_.o(definition, key) && !REQ_.o(exports, key)) { - Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); - } - } - }; - })(); - - - (() => { - REQ_.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop) - })(); - - - (() => { - // define __esModule on exports - REQ_.r = (exports) => { - if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { - Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); - } - Object.defineProperty(exports, '__esModule', { value: true }); - }; - })(); - - - - // startup - // Load entry module and return exports - REQ_(326); - // This entry module is referenced by other modules so it can't be inlined - const EXP_ = REQ_(732); - -})() -; + })() +; \ No newline at end of file diff --git a/js/chat/bundle.schedule-meeting.js b/js/chat/bundle.schedule-meeting.js new file mode 100644 index 0000000000..e2822ca2b2 --- /dev/null +++ b/js/chat/bundle.schedule-meeting.js @@ -0,0 +1,2439 @@ +/** @file automatically generated, do not edit it. */ +"use strict"; +(self.webpackChunk_meganz_webclient = self.webpackChunk_meganz_webclient || []).push([[716],{ + + 8894 +(_, EXP_, REQ_) { + + REQ_.d(EXP_, { + V: () => withDateObserver + }); + const _babel_runtime_helpers_extends0__ = REQ_(8168); + const react1__ = REQ_(1594); + const react1___default = REQ_.n(react1__); + + +const withDateObserver = Component => class extends react1___default().Component { + constructor(...args) { + super(...args); + this.listener = undefined; + this.state = { + timestamp: undefined + }; + } + componentWillUnmount() { + mBroadcaster.removeListener(this.listener); + } + componentDidMount() { + this.listener = mBroadcaster.addListener(withDateObserver.NAMESPACE, timestamp => this.setState({ + timestamp + })); + } + render() { + return JSX_(Component, (0,_babel_runtime_helpers_extends0__ .A)({}, this.props, { + timestamp: this.state.timestamp + })); + } +}; +withDateObserver.NAMESPACE = 'meetings:onSelectDate'; + + }, + + 9290 +(_, EXP_, REQ_) { + + REQ_.d(EXP_, { + A: () => __WEBPACK_DEFAULT_EXPORT__ + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _mixins_js1__ = REQ_(8264); + const _dateObserver2__ = REQ_(8894); + + + +class Datepicker extends react0___default().Component { + constructor(props) { + super(props); + this.OPTIONS = { + classes: 'meetings-datepicker-calendar', + dateFormat: '@', + minDate: null, + startDate: null, + selectedDates: [], + prevHtml: '', + nextHtml: '', + altField: null, + firstDay: 0, + autoClose: true, + toggleSelected: false, + position: 'bottom left', + language: { + daysMin: [l[8763], l[8764], l[8765], l[8766], l[8767], l[8768], l[8769]], + months: [l[408], l[409], l[410], l[411], l[412], l[413], l[414], l[415], l[416], l[417], l[418], l[419]], + monthsShort: [l[24035], l[24037], l[24036], l[24038], l[24047], l[24039], l[24040], l[24041], l[24042], l[24043], l[24044], l[24045]] + }, + onSelect: dateText => { + const prevDate = new Date(+this.props.value); + const nextDate = new Date(+dateText); + nextDate.setHours(prevDate.getHours(), prevDate.getMinutes()); + this.props.onSelect(nextDate.getTime()); + mBroadcaster.sendMessage(_dateObserver2__ .V.NAMESPACE, nextDate.getTime()); + } + }; + this.domRef = react0___default().createRef(); + this.inputRef = react0___default().createRef(); + this.datepicker = null; + this.formatValue = value => { + if (typeof value === 'number') { + return time2date(value / 1000, 18); + } + return value; + }; + this.OPTIONS.startDate = new Date(this.props.startDate); + this.OPTIONS.selectedDates = this.props.selectedDates || [this.OPTIONS.startDate]; + this.OPTIONS.minDate = this.props.minDate ? new Date(this.props.minDate) : new Date(); + this.OPTIONS.position = this.props.position || this.OPTIONS.position; + this.OPTIONS.altField = `input.${this.props.altField}`; + } + initialize() { + const inputRef = this.inputRef && this.inputRef.current; + if (inputRef) { + let _this$props$onMount, _this$props; + $(inputRef).datepicker(this.OPTIONS); + this.datepicker = $(inputRef).data('datepicker'); + (_this$props$onMount = (_this$props = this.props).onMount) == null || _this$props$onMount.call(_this$props, this.datepicker); + } + } + componentWillUnmount() { + if (this.domRef && this.domRef.current) { + $(this.domRef.current).unbind(`keyup.${Datepicker.NAMESPACE}`); + } + } + componentDidMount() { + M.require('datepicker_js').done(() => this.initialize()); + if (this.domRef && this.domRef.current) { + $(this.domRef.current).rebind(`keyup.${Datepicker.NAMESPACE}`, ({ + keyCode + }) => { + if (keyCode === 13) { + this.datepicker.hide(); + return false; + } + }); + } + } + render() { + const { + NAMESPACE + } = Datepicker; + const { + value, + name, + className, + placeholder, + isLoading, + onFocus, + onChange, + onBlur + } = this.props; + const formattedValue = this.formatValue(value); + return JSX_("div", { + ref: this.domRef, + className: NAMESPACE + }, JSX_("div", { + className: "mega-input datepicker-input" + }, JSX_("input", { + ref: this.inputRef, + type: "text", + name, + className: ` + dialog-input + ${className || ''} + `, + autoComplete: "off", + disabled: isLoading, + placeholder: placeholder || '', + value: formattedValue, + onFocus: ev => onFocus == null ? void 0 : onFocus(ev), + onChange: ev => onChange == null ? void 0 : onChange(ev), + onBlur: ev => onBlur == null ? void 0 : onBlur(ev) + }), JSX_("i", { + className: "sprite-fm-mono icon-calendar1", + onClick: isLoading ? null : () => { + if (this.datepicker) { + let _this$inputRef$curren; + this.datepicker.show(); + (_this$inputRef$curren = this.inputRef.current) == null || _this$inputRef$curren.focus(); + } + } + }))); + } +} +Datepicker.NAMESPACE = 'meetings-datepicker'; + const __WEBPACK_DEFAULT_EXPORT__ = (0,_mixins_js1__ .Zz)(_dateObserver2__ .V)(Datepicker); + + }, + + 9811 +(_, EXP_, REQ_) { + + REQ_.d(EXP_, { + c: () => DateTime + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _helpers_jsx1__ = REQ_(6521); + const _datepicker_jsx2__ = REQ_(9290); + const _select_jsx3__ = REQ_(3448); + + + + +class DateTime extends react0___default().Component { + constructor(...args) { + super(...args); + this.state = { + datepickerRef: undefined, + manualDateInput: '', + manualTimeInput: '', + initialDate: '' + }; + this.handleChange = ev => { + const { + onChange + } = this.props; + const { + datepickerRef, + initialDate + } = this.state; + if (!datepickerRef) { + return; + } + const { + value + } = ev.target; + const date = (0,_helpers_jsx1__ .XH)(value); + const timestamp = date.valueOf(); + const dateObj = new Date(timestamp); + dateObj.setHours(initialDate.getHours(), initialDate.getMinutes()); + datepickerRef.selectedDates = [dateObj]; + datepickerRef.currentDate = dateObj; + datepickerRef.nav._render(); + datepickerRef.views.days._render(); + onChange == null || onChange(value); + this.setState({ + manualDateInput: dateObj.getTime() + }); + }; + } + render() { + const { + name, + startDate, + altField, + value, + minDate, + filteredTimeIntervals, + label, + isLoading, + onMount, + onSelectDate, + onSelectTime, + onBlur + } = this.props; + return JSX_(react0___default().Fragment, null, label && JSX_("span", null, label), JSX_(_datepicker_jsx2__ .A, { + name: `${_datepicker_jsx2__ .A.NAMESPACE}-${name}`, + className: isLoading ? 'disabled' : '', + isLoading, + startDate, + altField: `${_select_jsx3__ .A.NAMESPACE}-${altField}`, + value, + minDate, + onMount: datepickerRef => this.setState({ + datepickerRef + }, () => onMount(datepickerRef)), + onSelect: onSelectDate, + onFocus: ({ + target + }) => { + this.setState({ + manualDateInput: undefined, + manualTimeInput: undefined, + initialDate: new Date(value) + }, () => target.select()); + }, + onChange: this.handleChange, + onBlur: () => onBlur(this.state.manualDateInput) + }), JSX_(_select_jsx3__ .A, { + name: `${_select_jsx3__ .A.NAMESPACE}-${altField}`, + className: isLoading ? 'disabled' : '', + isLoading, + typeable: true, + options: filteredTimeIntervals, + value: (() => typeof value === 'number' ? value : this.state.datepickerRef.currentDate.getTime())(), + format: toLocaleTime, + onSelect: onSelectTime, + onChange: () => false, + onBlur: timestamp => { + if (timestamp) { + onSelectTime({ + value: timestamp + }); + } + } + })); + } +} + + }, + + 4156 +(_, EXP_, REQ_) { + +REQ_.r(EXP_); + REQ_.d(EXP_, { + "default": () => Edit + }); + const _babel_runtime_helpers_extends0__ = REQ_(8168); + const react1__ = REQ_(1594); + const react1___default = REQ_.n(react1__); + const _mixins_js2__ = REQ_(8264); + const _utils_jsx3__ = REQ_(1497); + const _ui_modalDialogs_jsx4__ = REQ_(8120); + const _ui_utils_jsx5__ = REQ_(6411); + const _link_jsx6__ = REQ_(4649); + const _datetime_jsx7__ = REQ_(9811); + const _helpers_jsx8__ = REQ_(6521); + const _button_jsx9__ = REQ_(6740); + + + + + + + + + + +class Edit extends _mixins_js2__ .w9 { + constructor(props) { + super(props); + this.occurrenceRef = null; + this.datepickerRefs = []; + this.interval = ChatRoom.SCHEDULED_MEETINGS_INTERVAL; + this.incomingCallListener = 'onPrepareIncomingCallDialog.recurringEdit'; + this.state = { + startDateTime: undefined, + endDateTime: undefined, + isDirty: false, + closeDialog: false, + overlayed: false + }; + this.onStartDateSelect = startDateTime => { + this.setState({ + startDateTime, + isDirty: true + }, () => { + this.datepickerRefs.endDateTime.selectDate(new Date(startDateTime + this.interval)); + }); + }; + this.onEndDateSelect = endDateTime => { + this.setState({ + endDateTime, + isDirty: true + }, () => { + const { + startDateTime, + endDateTime + } = this.state; + if (endDateTime < startDateTime) { + if (endDateTime < Date.now()) { + return this.setState({ + endDateTime: startDateTime + this.interval + }); + } + this.handleTimeSelect({ + startDateTime: endDateTime - this.interval + }); + } + }); + }; + this.handleTimeSelect = ({ + startDateTime, + endDateTime + }) => { + startDateTime = startDateTime || this.state.startDateTime; + endDateTime = endDateTime || this.state.endDateTime; + this.setState(state => { + return { + startDateTime: endDateTime <= state.startDateTime ? endDateTime - this.interval : startDateTime, + endDateTime: startDateTime >= state.endDateTime ? startDateTime + this.interval : endDateTime, + isDirty: true + }; + }); + }; + const { + scheduledMeeting, + occurrenceId + } = this.props; + this.occurrenceRef = scheduledMeeting.occurrences[occurrenceId]; + if (this.occurrenceRef) { + this.state.startDateTime = this.occurrenceRef.start; + this.state.endDateTime = this.occurrenceRef.end; + } + } + componentWillUnmount() { + super.componentWillUnmount(); + if (this.incomingCallListener) { + megaChat.off(this.incomingCallListener); + } + if ($.dialog === _utils_jsx3__ .oK) { + closeDialog(); + } + } + componentDidMount() { + super.componentDidMount(); + M.safeShowDialog(_utils_jsx3__ .oK, () => { + if (!this.isMounted()) { + throw Error(`Edit dialog: component not mounted.`); + } + megaChat.rebind(this.incomingCallListener, () => { + if (this.isMounted()) { + this.setState({ + overlayed: true, + closeDialog: false + }); + megaChat.plugins.callManager2.rebind('onRingingStopped.recurringEdit', () => { + megaChat.plugins.callManager2.off('onRingingStopped.recurringEdit'); + this.setState({ + overlayed: false + }); + fm_showoverlay(); + }); + } + }); + return $(`#${_utils_jsx3__ .CU}`); + }); + } + componentDidUpdate(prevProps) { + if (prevProps.callExpanded && !this.props.callExpanded) { + if (!$.dialog) { + M.safeShowDialog(_utils_jsx3__ .oK, `#${_utils_jsx3__ .CU}`); + } + fm_showoverlay(); + this.setState({ + closeDialog: false + }); + } + if (!prevProps.callExpanded && this.props.callExpanded) { + this.setState({ + closeDialog: false + }); + } + } + render() { + const { + chatRoom, + callExpanded, + onClose + } = this.props; + const { + startDateTime, + endDateTime, + isDirty, + closeDialog, + overlayed + } = this.state; + const dialogClasses = ['fluid']; + if (closeDialog) { + dialogClasses.push('with-confirmation-dialog'); + } + if (callExpanded || overlayed) { + dialogClasses.push('hidden'); + } + const withUpgrade = !u_attr.p && endDateTime - startDateTime > 36e5; + if (withUpgrade) { + dialogClasses.push('upgrade'); + } + return JSX_(_ui_modalDialogs_jsx4__ .A.ModalDialog, (0,_babel_runtime_helpers_extends0__ .A)({}, this.state, { + id: _utils_jsx3__ .CU, + className: dialogClasses.join(' '), + dialogName: _utils_jsx3__ .oK, + dialogType: "main", + onClose: () => { + return isDirty ? this.setState({ + closeDialog: true + }) : onClose(); + } + }), JSX_("header", null, JSX_("h2", null, l.edit_meeting_title)), JSX_("div", { + className: "fm-dialog-body" + }, JSX_(_utils_jsx3__ .fI, null, JSX_("div", { + className: "mega-banner body recurring-edit-banner" + }, JSX_("div", { + className: "cell" + }, (0,_ui_utils_jsx5__ .lI)(l.scheduled_edit_occurrence_note, '[A]', _link_jsx6__ .A, { + onClick: () => { + onClose(); + megaChat.trigger(megaChat.plugins.meetingsManager.EVENTS.EDIT, chatRoom); + } + })))), JSX_(_utils_jsx3__ .fI, { + className: "start-aligned" + }, JSX_(_utils_jsx3__ .VP, null, JSX_("i", { + className: "sprite-fm-mono icon-recents-filled" + })), JSX_("div", { + className: "schedule-date-container" + }, JSX_(_datetime_jsx7__ .c, { + name: "startDateTime", + altField: "startTime", + datepickerRef: this.datepickerRefs.startDateTime, + startDate: startDateTime, + value: startDateTime, + filteredTimeIntervals: (0,_helpers_jsx8__ .a4)(startDateTime), + label: l.schedule_start_date, + onMount: datepicker => { + this.datepickerRefs.startDateTime = datepicker; + }, + onSelectDate: startDateTime => this.onStartDateSelect(startDateTime), + onSelectTime: ({ + value: startDateTime + }) => this.handleTimeSelect({ + startDateTime + }), + onChange: value => this.setState({ + startDateTime: value + }), + onBlur: timestamp => { + if (timestamp) { + timestamp = timestamp < Date.now() ? this.occurrenceRef.start : timestamp; + this.onStartDateSelect(timestamp); + } + } + }), JSX_(_datetime_jsx7__ .c, { + name: "endDateTime", + altField: "endTime", + datepickerRef: this.datepickerRefs.endDateTime, + startDate: endDateTime, + value: endDateTime, + filteredTimeIntervals: (0,_helpers_jsx8__ .a4)(endDateTime, startDateTime), + label: l.schedule_end_date, + onMount: datepicker => { + this.datepickerRefs.endDateTime = datepicker; + }, + onSelectDate: endDateTime => this.onEndDateSelect(endDateTime), + onSelectTime: ({ + value: endDateTime + }) => this.handleTimeSelect({ + endDateTime + }), + onChange: timestamp => this.setState({ + endDateTime: timestamp + }), + onBlur: timestamp => timestamp && this.onEndDateSelect(timestamp) + }))), withUpgrade && JSX_(_utils_jsx3__ .dh, { + onUpgradeClicked: () => { + onClose(); + loadSubPage('pro'); + eventlog(500257); + } + })), JSX_("footer", null, JSX_("div", { + className: "footer-container" + }, JSX_(_button_jsx9__ .A, { + className: "mega-button positive", + onClick: () => { + const { + startDateTime, + endDateTime + } = this.state; + if (startDateTime !== this.occurrenceRef.start || endDateTime !== this.occurrenceRef.end) { + delay('chat-event-sm-edit-meeting', () => eventlog(99923)); + this.occurrenceRef.update(startDateTime, endDateTime); + } + onClose(); + } + }, JSX_("span", null, l.update_meeting_button)))), !(overlayed || callExpanded) && closeDialog && JSX_(_utils_jsx3__ .pD, { + onToggle: () => this.setState({ + closeDialog: false + }), + onClose + })); + } +} + + }, + + 8389 +(_, EXP_, REQ_) { + +// ESM COMPAT FLAG +REQ_.r(EXP_); + +// EXPORTS +REQ_.d(EXP_, { + "default": () => Schedule +}); + +// EXTERNAL MODULE: ./node_modules/@babel/runtime/helpers/esm/extends.js +const esm_extends = REQ_(8168); +// EXTERNAL MODULE: external "React" +const external_React_ = REQ_(1594); +const REaCt = REQ_.n(external_React_); +// EXTERNAL MODULE: ./js/chat/mixins.js +const mixins = REQ_(8264); +// EXTERNAL MODULE: ./js/ui/modalDialogs.jsx + 1 modules +const modalDialogs = REQ_(8120); +// EXTERNAL MODULE: ./js/chat/ui/meetings/button.jsx +const meetings_button = REQ_(6740); +// EXTERNAL MODULE: ./js/ui/perfectScrollbar.jsx +const perfectScrollbar = REQ_(1301); +// EXTERNAL MODULE: ./js/chat/ui/contacts.jsx +const ui_contacts = REQ_(8022); +;// ./js/chat/ui/meetings/schedule/invite.jsx + + + + +class Invite extends mixins.w9 { + constructor(props) { + super(props); + this.domRef = REaCt().createRef(); + this.wrapperRef = REaCt().createRef(); + this.inputRef = REaCt().createRef(); + this.state = { + value: '', + expanded: false, + loading: true, + frequents: [], + frequentsInitial: [], + contacts: [], + contactsInitial: [], + selected: [] + }; + this.handleMousedown = ({ + target + }) => this.domRef && this.domRef.current && this.domRef.current.contains(target) ? null : this.setState({ + expanded: false + }); + this.getSortedContactsList = frequents => { + const filteredContacts = []; + M.u.forEach(contact => { + if (contact.c === 1 && !frequents.includes(contact.u) && !this.state.selected.includes(contact.u)) { + filteredContacts.push(contact); + } + }); + const sortFn = M.getSortByNameFn2(1); + filteredContacts.sort((a, b) => sortFn(a, b)); + return filteredContacts; + }; + this.doMatch = (value, collection) => { + value = value.toLowerCase(); + return collection.filter(contact => { + contact = typeof contact === 'string' ? M.getUserByHandle(contact) : contact; + const name = M.getNameByHandle(contact.u).toLowerCase(); + const email = contact.m && contact.m.toLowerCase(); + return name.includes(value) || email.includes(value); + }); + }; + this.handleSearch = this.handleSearch.bind(this); + this.state.selected = this.props.participants || []; + } + reinitializeWrapper() { + const wrapperRef = this.wrapperRef && this.wrapperRef.current; + if (wrapperRef) { + wrapperRef.reinitialise(); + wrapperRef.scrollToY(0); + } + } + buildContactsList() { + megaChat.getFrequentContacts().then(frequentContacts => { + if (this.isMounted()) { + const frequents = frequentContacts.slice(-ui_contacts.lO).map(c => c.userId); + const contacts = this.getSortedContactsList(frequents); + this.setState({ + frequents, + frequentsInitial: frequents, + contacts, + contactsInitial: contacts, + loading: false + }); + } + }); + } + handleSearch(ev) { + const { + value + } = ev.target; + const searching = value.length >= 2; + const frequents = searching ? this.doMatch(value, this.state.frequentsInitial) : this.state.frequentsInitial; + const contacts = searching ? this.doMatch(value, this.state.contactsInitial) : this.state.contactsInitial; + this.setState({ + value, + contacts, + frequents + }, () => this.reinitializeWrapper()); + } + handleSelect({ + userHandle, + expanded = false + }) { + this.setState(state => ({ + value: '', + expanded, + selected: state.selected.includes(userHandle) ? state.selected.filter(c => c !== userHandle) : [...state.selected, userHandle] + }), () => { + let _this$inputRef$curren; + this.props.onSelect(this.state.selected); + this.buildContactsList(); + this.reinitializeWrapper(); + (_this$inputRef$curren = this.inputRef.current) == null || _this$inputRef$curren.focus(); + }); + } + getFilteredContacts(contacts) { + if (contacts && contacts.length) { + return contacts.map(contact => { + contact = contact instanceof MegaDataMap ? contact : M.u[contact]; + return this.state.selected.includes(contact.u) ? null : JSX_("div", { + key: contact.u, + className: "invite-section-item", + onClick: () => { + this.handleSelect({ + userHandle: contact.u, + expanded: true + }); + } + }, JSX_(ui_contacts.eu, { + contact + }), JSX_("div", { + className: "invite-item-data" + }, JSX_("div", { + className: "invite-item-name" + }, JSX_(ui_contacts.uA, { + overflow: true, + simpletip: { + offset: 10 + }, + contact + })), JSX_("div", { + className: "invite-item-mail" + }, contact.m))); + }); + } + return null; + } + renderContent() { + const { + frequents, + contacts, + selected + } = this.state; + const hasMoreFrequents = frequents.length && frequents.some(h => !selected.includes(h)); + const $$SECTION = (title, children) => JSX_("div", { + className: "invite-section" + }, JSX_("div", { + className: "invite-section-title" + }, title), children && JSX_("div", { + className: "invite-section-list" + }, children)); + if (hasMoreFrequents || contacts.length) { + return JSX_(perfectScrollbar.O, { + ref: this.wrapperRef, + className: "invite-scroll-wrapper", + options: { + 'suppressScrollX': true + } + }, hasMoreFrequents ? $$SECTION(l.recent_contact_label, this.getFilteredContacts(frequents)) : '', contacts.length ? $$SECTION(l.all_contact_label, this.getFilteredContacts(contacts)) : '', frequents.length === 0 && contacts.length === 0 && $$SECTION(l.invite_no_results_found, null)); + } + return $$SECTION(l.invite_no_contacts_to_add, null); + } + componentWillUnmount() { + super.componentWillUnmount(); + document.removeEventListener('mousedown', this.handleMousedown); + } + componentDidMount() { + super.componentDidMount(); + document.addEventListener('mousedown', this.handleMousedown); + this.buildContactsList(); + } + render() { + const { + className, + isLoading + } = this.props; + const { + value, + expanded, + loading, + selected + } = this.state; + return JSX_("div", { + ref: this.domRef, + className: ` + ${Invite.NAMESPACE} + ${className || ''} + ` + }, JSX_("div", { + className: "multiple-input" + }, JSX_("ul", { + className: "token-input-list-mega", + onClick: ({ + target + }) => isLoading ? null : target.classList.contains('token-input-list-mega') && this.setState({ + expanded: true + }) + }, selected.map(handle => { + return JSX_("li", { + key: handle, + className: "token-input-token-mega" + }, JSX_("div", { + className: "contact-tag-item" + }, JSX_(ui_contacts.eu, { + contact: M.u[handle], + className: "avatar-wrapper box-avatar" + }), JSX_(ui_contacts.uA, { + contact: M.u[handle], + overflow: true + }), JSX_("i", { + className: "sprite-fm-mono icon-close-component", + onClick: () => isLoading ? null : this.handleSelect({ + userHandle: handle + }) + }))); + }), JSX_("li", { + className: "token-input-input-token-mega" + }, JSX_("input", { + ref: this.inputRef, + type: "text", + name: "participants", + className: `${Invite.NAMESPACE}-input`, + disabled: isLoading, + autoComplete: "off", + placeholder: selected.length ? '' : l.schedule_participant_input, + value, + onClick: () => this.setState({ + expanded: true + }), + onChange: this.handleSearch, + onKeyDown: ({ + target, + keyCode + }) => { + const { + selected + } = this.state; + return keyCode === 8 && target.value === '' && selected.length && this.handleSelect({ + userHandle: selected[selected.length - 1] + }); + } + })))), loading ? null : JSX_("div", { + className: `mega-input-dropdown ${expanded ? '' : 'hidden'}` + }, this.renderContent())); + } +} +Invite.NAMESPACE = 'meetings-invite'; +// EXTERNAL MODULE: ./js/chat/ui/meetings/schedule/helpers.jsx +const helpers = REQ_(6521); +// EXTERNAL MODULE: ./js/chat/ui/meetings/schedule/datetime.jsx +const datetime = REQ_(9811); +// EXTERNAL MODULE: ./js/chat/chatRoom.jsx +const chat_chatRoom = REQ_(7057); +// EXTERNAL MODULE: ./js/ui/utils.jsx +const utils = REQ_(6411); +// EXTERNAL MODULE: ./js/chat/ui/conversations.jsx + 2 modules +const conversations = REQ_(4904); +// EXTERNAL MODULE: ./js/chat/ui/meetings/schedule/utils.jsx +const schedule_utils = REQ_(1497); +// EXTERNAL MODULE: ./js/chat/ui/meetings/schedule/datepicker.jsx +const datepicker = REQ_(9290); +// EXTERNAL MODULE: ./js/chat/ui/meetings/schedule/select.jsx +const schedule_select = REQ_(3448); +;// ./js/chat/ui/meetings/schedule/recurring.jsx + + + + + + + + +class Recurring extends mixins.w9 { + constructor(props) { + let _Object$values$find; + super(props); + this.domRef = REaCt().createRef(); + this.VIEWS = { + DAILY: 0x00, + WEEKLY: 0x01, + MONTHLY: 0x02 + }; + this.FREQUENCIES = { + DAILY: 'd', + WEEKLY: 'w', + MONTHLY: 'm' + }; + this.WEEK_DAYS = { + MONDAY: { + value: 1, + label: l.schedule_day_control_mon + }, + TUESDAY: { + value: 2, + label: l.schedule_day_control_tue + }, + WEDNESDAY: { + value: 3, + label: l.schedule_day_control_wed + }, + THURSDAY: { + value: 4, + label: l.schedule_day_control_thu + }, + FRIDAY: { + value: 5, + label: l.schedule_day_control_fri + }, + SATURDAY: { + value: 6, + label: l.schedule_day_control_sat + }, + SUNDAY: { + value: 7, + label: l.schedule_day_control_sun + } + }; + this.OFFSETS = [[l.recur_freq_offset_first_mon || '[A]first[/A][B]Monday[/B]', l.recur_freq_offset_first_tue || '[A]first[/A][B]Tuesday[/B]', l.recur_freq_offset_first_wed || '[A]first[/A][B]Wednesday[/B]', l.recur_freq_offset_first_thu || '[A]first[/A][B]Thursday[/B]', l.recur_freq_offset_first_fri || '[A]first[/A][B]Friday[/B]', l.recur_freq_offset_first_sat || '[A]first[/A][B]Saturday[/B]', l.recur_freq_offset_first_sun || '[A]first[/A][B]Sunday[/B]'], [l.recur_freq_offset_second_mon || '[A]second[/A][B]Monday[/B]', l.recur_freq_offset_second_tue || '[A]second[/A][B]Tuesday[/B]', l.recur_freq_offset_second_wed || '[A]second[/A][B]Wednesday[/B]', l.recur_freq_offset_second_thu || '[A]second[/A][B]Thursday[/B]', l.recur_freq_offset_second_fri || '[A]second[/A][B]Friday[/B]', l.recur_freq_offset_second_sat || '[A]second[/A][B]Saturday[/B]', l.recur_freq_offset_second_sun || '[A]second[/A][B]Sunday[/B]'], [l.recur_freq_offset_third_mon || '[A]third[/A][B]Monday[/B]', l.recur_freq_offset_third_tue || '[A]third[/A][B]Tuesday[/B]', l.recur_freq_offset_third_wed || '[A]third[/A][B]Wednesday[/B]', l.recur_freq_offset_third_thu || '[A]third[/A][B]Thursday[/B]', l.recur_freq_offset_third_fri || '[A]third[/A][B]Friday[/B]', l.recur_freq_offset_third_sat || '[A]third[/A][B]Saturday[/B]', l.recur_freq_offset_third_sun || '[A]third[/A][B]Sunday[/B]'], [l.recur_freq_offset_fourth_mon || '[A]fourth[/A][B]Monday[/B]', l.recur_freq_offset_fourth_tue || '[A]fourth[/A][B]Tuesday[/B]', l.recur_freq_offset_fourth_wed || '[A]fourth[/A][B]Wednesday[/B]', l.recur_freq_offset_fourth_thu || '[A]fourth[/A][B]Thursday[/B]', l.recur_freq_offset_fourth_fri || '[A]fourth[/A][B]Friday[/B]', l.recur_freq_offset_fourth_sat || '[A]fourth[/A][B]Saturday[/B]', l.recur_freq_offset_fourth_sun || '[A]fourth[/A][B]Sunday[/B]'], [l.recur_freq_offset_fifth_mon || '[A]fifth[/A][B]Monday[/B]', l.recur_freq_offset_fifth_tue || '[A]fifth[/A][B]Tuesday[/B]', l.recur_freq_offset_fifth_wed || '[A]fifth[/A][B]Wednesday[/B]', l.recur_freq_offset_fifth_thu || '[A]fifth[/A][B]Thursday[/B]', l.recur_freq_offset_fifth_fri || '[A]fifth[/A][B]Friday[/B]', l.recur_freq_offset_fifth_sat || '[A]fifth[/A][B]Saturday[/B]', l.recur_freq_offset_fifth_sun || '[A]fifth[/A][B]Sunday[/B]']]; + this.OFFSET_POS_REGEX = /\[A]([^[]+)\[\/A]/; + this.OFFSET_DAY_REGEX = /\[B]([^[]+)\[\/B]/; + this.MONTH_RULES = { + DAY: 'day', + OFFSET: 'offset' + }; + this.initialEnd = (0,helpers.PS)(this.props.startDateTime, 6); + this.initialWeekDays = Object.values(this.WEEK_DAYS).map(d => d.value); + this.initialMonthDay = this.props.startDateTime ? new Date(this.props.startDateTime).getDate() : undefined; + this.state = { + view: this.VIEWS.DAILY, + frequency: this.FREQUENCIES.DAILY, + end: this.initialEnd, + prevEnd: undefined, + interval: 0, + weekDays: this.initialWeekDays, + monthRule: this.MONTH_RULES.DAY, + monthDays: [this.initialMonthDay], + offset: { + value: 1, + weekDay: 1 + }, + monthDaysWarning: this.initialMonthDay > 28 + }; + this.toggleView = (view, frequency, state) => this.props.isLoading ? null : this.setState({ + view, + frequency, + ...state + }); + this.MonthDaySelect = ({ + offset + }) => { + const dayIdx = (offset && offset.weekDay || 1) - 1; + const posIdx = (offset && offset.value || 1) - 1; + const dayValues = this.OFFSETS[posIdx].map((part, idx) => ({ + value: idx + 1, + label: this.OFFSET_DAY_REGEX.exec(part)[1] + })); + const posValues = []; + for (let i = 0; i < this.OFFSETS.length; i++) { + posValues.push({ + value: i + 1, + label: this.OFFSET_POS_REGEX.exec(this.OFFSETS[i][dayIdx])[1] + }); + } + const posFirst = this.OFFSETS[posIdx][dayIdx].indexOf('[A]') < this.OFFSETS[posIdx][dayIdx].indexOf('[B]'); + const pos = JSX_(schedule_select.A, { + name: "recurring-offset-value", + className: "inline", + icon: true, + value: posValues[posIdx].label, + isLoading: this.props.isLoading, + options: posValues, + onSelect: option => { + this.setState(state => ({ + monthRule: this.MONTH_RULES.OFFSET, + offset: { + value: option.value, + weekDay: state.offset.weekDay || this.WEEK_DAYS.MONDAY.value + } + })); + } + }); + return JSX_(REaCt().Fragment, null, posFirst && pos, JSX_(schedule_select.A, { + name: "recurring-offset-day", + className: "inline", + icon: true, + value: dayValues[dayIdx].label, + isLoading: this.props.isLoading, + options: dayValues, + onSelect: option => { + this.setState(state => ({ + monthRule: this.MONTH_RULES.OFFSET, + offset: { + value: state.offset.value || 1, + weekDay: option.value + } + })); + } + }), !posFirst && pos); + }; + this.IntervalSelect = () => { + const { + interval, + view + } = this.state; + return JSX_("div", { + className: "mega-input inline recurring-interval" + }, JSX_(schedule_select.A, { + name: `${Recurring.NAMESPACE}-interval`, + value: interval > 0 ? interval : 1, + icon: true, + isLoading: this.props.isLoading, + options: [...Array(view === this.VIEWS.WEEKLY ? 52 : 12).keys()].map(value => { + value += 1; + return { + value, + label: value + }; + }), + onSelect: ({ + value + }) => { + this.setState({ + interval: value === 1 ? 0 : value + }); + } + })); + }; + const { + chatRoom, + startDateTime + } = this.props; + const weekDay = new Date(startDateTime).getDay(); + this.state.offset.weekDay = ((_Object$values$find = Object.values(this.WEEK_DAYS).find(d => d.value === weekDay)) == null ? void 0 : _Object$values$find.value) || this.WEEK_DAYS.SUNDAY.value; + if (chatRoom && chatRoom.scheduledMeeting && chatRoom.scheduledMeeting.isRecurring) { + const { + frequency, + interval, + end, + weekDays, + monthDays, + offset + } = chatRoom.scheduledMeeting.recurring; + this.state.view = frequency === 'd' ? this.VIEWS.DAILY : frequency === 'w' ? this.VIEWS.WEEKLY : this.VIEWS.MONTHLY; + this.state.frequency = frequency; + this.state.end = end; + this.state.interval = interval; + this.state.weekDays = weekDays && weekDays.length ? weekDays : this.initialWeekDays; + this.state.monthRule = monthDays && monthDays.length ? this.MONTH_RULES.DAY : this.MONTH_RULES.OFFSET; + this.state.monthDays = monthDays && monthDays.length ? [monthDays[0]] : [this.initialMonthDay]; + this.state.offset = offset && Object.keys(offset).length ? offset : this.state.offset; + } + } + getFormattedState(state) { + const { + frequency, + end, + interval, + weekDays, + monthRule, + monthDays, + offset + } = state; + switch (true) { + case frequency === this.FREQUENCIES.DAILY: + return { + frequency, + end, + weekDays + }; + case frequency === this.FREQUENCIES.WEEKLY: + return { + frequency, + end, + ...interval && { + interval + }, + weekDays + }; + case frequency === this.FREQUENCIES.MONTHLY: + return { + frequency, + end, + ...interval && { + interval + }, + ...monthRule === this.MONTH_RULES.DAY ? { + monthDays + } : { + offset: [[offset.value, offset.weekDay]] + } + }; + } + } + renderDayControls() { + const { + weekDays, + view + } = this.state; + const handleWeeklySelection = (weekDay, remove) => { + this.setState(state => { + if (remove) { + return { + weekDays: state.weekDays.length === 1 ? state.weekDays : state.weekDays.filter(d => d !== weekDay) + }; + } + return { + weekDays: [...state.weekDays, weekDay] + }; + }, () => { + const { + weekDays + } = this.state; + if (weekDays.length === Object.keys(this.WEEK_DAYS).length) { + this.toggleView(this.VIEWS.DAILY, this.FREQUENCIES.DAILY); + } + }); + }; + const handleDailySelection = weekDay => { + this.toggleView(this.VIEWS.WEEKLY, this.FREQUENCIES.WEEKLY, { + weekDays: weekDays.filter(d => d !== weekDay) + }); + }; + return JSX_("div", { + className: "recurring-field-row" + }, Object.values(this.WEEK_DAYS).map(({ + value, + label + }) => { + const isCurrentlySelected = weekDays.includes(value); + return JSX_(meetings_button.A, { + key: value, + className: ` + mega-button + action + recurring-toggle-button + ${isCurrentlySelected ? 'active' : ''} + ${weekDays.length === 1 && isCurrentlySelected ? 'disabled' : ''} + `, + onClick: this.props.isLoading ? null : () => { + if (view === this.VIEWS.WEEKLY) { + return handleWeeklySelection(value, isCurrentlySelected); + } + return handleDailySelection(value); + } + }, label); + })); + } + renderIntervalControls() { + const { + view, + interval + } = this.state; + return JSX_("div", { + className: "recurring-field-row" + }, (0,utils.lI)(mega.icu.format(view === this.VIEWS.MONTHLY ? l.recur_rate_monthly : l.recur_rate_weekly, interval > 0 ? interval : 1), "[S]", this.IntervalSelect)); + } + renderEndControls() { + const { + isLoading, + onMount + } = this.props; + const { + end, + prevEnd + } = this.state; + return JSX_("div", { + className: "recurring-field-row" + }, JSX_("div", { + className: "recurring-title-heading" + }, l.recurring_ends), JSX_("div", { + className: "recurring-radio-buttons" + }, JSX_("div", { + className: "recurring-label-wrap" + }, JSX_("div", { + className: ` + uiTheme + ${end ? 'radioOff' : 'radioOn'} + ` + }, JSX_("input", { + type: "radio", + name: `${Recurring.NAMESPACE}-radio-end`, + disabled: isLoading, + className: ` + uiTheme + ${end ? 'radioOff' : 'radioOn'} + `, + onChange: () => { + this.setState(state => ({ + end: undefined, + prevEnd: state.end || state.prevEnd + })); + } + })), JSX_("div", { + className: "radio-txt" + }, JSX_("span", { + className: "recurring-radio-label", + onClick: () => isLoading ? null : this.setState(state => ({ + end: undefined, + prevEnd: state.end || state.prevEnd + })) + }, l.recurring_never))), JSX_("div", { + className: "recurring-label-wrap" + }, JSX_("div", { + className: ` + uiTheme + ${end ? 'radioOn' : 'radioOff'} + ` + }, JSX_("input", { + type: "radio", + name: `${Recurring.NAMESPACE}-radio-end`, + disabled: isLoading, + className: ` + uiTheme + ${end ? 'radioOn' : 'radioOff'} + `, + onChange: () => isLoading ? null : this.setState({ + end: prevEnd || this.initialEnd + }) + })), JSX_("div", { + className: "radio-txt" + }, JSX_("span", { + className: "recurring-radio-label", + onClick: () => isLoading || end ? null : this.setState({ + end: prevEnd || this.initialEnd + }) + }, l.recurring_on), JSX_(datepicker.A, { + name: `${Recurring.NAMESPACE}-endDateTime`, + position: "top left", + startDate: end || this.initialEnd, + selectedDates: [new Date(end)], + isLoading, + value: end || prevEnd || '', + placeholder: time2date(end || prevEnd || this.initialEnd / 1000, 18), + onMount, + onSelect: timestamp => this.setState({ + end: timestamp + }, () => this.safeForceUpdate()) + }))))); + } + renderDaily() { + return JSX_("div", { + className: `${Recurring.NAMESPACE}-daily` + }, this.renderDayControls(), this.renderEndControls()); + } + renderWeekly() { + return JSX_("div", { + className: `${Recurring.NAMESPACE}-weekly` + }, this.renderIntervalControls(), this.renderDayControls(), this.renderEndControls()); + } + renderMonthly() { + const { + isLoading + } = this.props; + const { + monthRule, + monthDays, + monthDaysWarning, + offset + } = this.state; + return JSX_("div", { + className: `${Recurring.NAMESPACE}-monthly` + }, this.renderIntervalControls(), JSX_("div", { + className: "recurring-field-row" + }, JSX_("div", { + className: "recurring-radio-buttons", + onClick: isLoading ? null : ev => { + const { + name, + value + } = ev.target; + if (name === `${Recurring.NAMESPACE}-radio-monthRule`) { + this.setState({ + monthRule: value + }); + } + } + }, JSX_("div", { + className: "recurring-label-wrap" + }, JSX_("div", { + className: ` + uiTheme + ${monthRule === 'day' ? 'radioOn' : 'radioOff'} + ` + }, JSX_("input", { + type: "radio", + name: `${Recurring.NAMESPACE}-radio-monthRule`, + value: "day", + disabled: isLoading, + className: ` + uiTheme + ${monthRule === 'day' ? 'radioOn' : 'radioOff'} + ` + })), JSX_("div", { + className: "radio-txt" + }, JSX_("span", { + className: "recurring-radio-label", + onClick: () => isLoading ? null : this.setState({ + monthRule: this.MONTH_RULES.DAY + }) + }, l.recurring_frequency_day), JSX_("div", { + className: "mega-input inline recurring-day" + }, JSX_(schedule_select.A, { + name: `${Recurring.NAMESPACE}-monthDay`, + icon: true, + value: monthDays[0], + isLoading, + options: [...Array(31).keys()].map(value => { + value += 1; + return { + value, + label: value + }; + }), + onSelect: ({ + value + }) => { + this.setState({ + monthRule: this.MONTH_RULES.DAY, + monthDays: [value], + monthDaysWarning: value > 28 + }); + } + })))), monthDaysWarning && JSX_("div", { + className: "recurring-label-wrap" + }, JSX_("div", { + className: "mega-banner body with-btn" + }, JSX_("div", { + className: "green-notification cell text-cell" + }, JSX_("div", { + className: "versioning-body-text" + }, mega.icu.format(l.recurring_monthdays_warning, monthDays[0]))))), JSX_("div", { + className: "recurring-label-wrap" + }, JSX_("div", { + className: ` + uiTheme + ${monthRule === this.MONTH_RULES.OFFSET ? 'radioOn' : 'radioOff'} + ` + }, JSX_("input", { + type: "radio", + name: `${Recurring.NAMESPACE}-radio-monthRule`, + value: "offset", + disabled: isLoading, + className: ` + uiTheme + ${monthRule === this.MONTH_RULES.OFFSET ? 'radioOn' : 'radioOff'} + ` + })), JSX_("div", { + className: "radio-txt" + }, JSX_(this.MonthDaySelect, { + offset + }))))), this.renderEndControls()); + } + renderNavigation(view) { + return JSX_(REaCt().Fragment, null, JSX_(meetings_button.A, { + className: ` + mega-button + action + recurring-nav-button + ${view === this.VIEWS.DAILY ? 'active' : ''} + `, + onClick: () => this.toggleView(this.VIEWS.DAILY, this.FREQUENCIES.DAILY) + }, l.recurring_daily), JSX_(meetings_button.A, { + className: ` + mega-button + action + recurring-nav-button + ${view === this.VIEWS.WEEKLY ? 'active' : ''} + `, + onClick: () => this.toggleView(this.VIEWS.WEEKLY, this.FREQUENCIES.WEEKLY) + }, l.recurring_weekly), JSX_(meetings_button.A, { + className: ` + mega-button + action + recurring-nav-button + ${view === this.VIEWS.MONTHLY ? 'active' : ''} + `, + onClick: () => this.toggleView(this.VIEWS.MONTHLY, this.FREQUENCIES.MONTHLY) + }, l.recurring_monthly)); + } + renderContent(view) { + switch (view) { + case this.VIEWS.DAILY: + return this.renderDaily(); + case this.VIEWS.WEEKLY: + return this.renderWeekly(); + case this.VIEWS.MONTHLY: + return this.renderMonthly(); + } + } + UNSAFE_componentWillUpdate(nextProps, nextState) { + if (this.state.view !== this.VIEWS.DAILY && nextState.view === this.VIEWS.DAILY) { + nextState.weekDays = this.initialWeekDays; + } + if (nextState.weekDays.length === Object.keys(this.WEEK_DAYS).length && this.state.view !== this.VIEWS.WEEKLY && nextState.view === this.VIEWS.WEEKLY || !(0,helpers.ro)(nextProps.startDateTime, this.props.startDateTime) && this.state.view === this.VIEWS.WEEKLY) { + const weekday = new Date(nextProps.startDateTime).getDay(); + nextState.weekDays = [weekday === 0 ? 7 : weekday]; + } + if (!(0,helpers.ro)(nextProps.startDateTime, this.props.startDateTime) && this.state.view === this.VIEWS.MONTHLY) { + let _Object$values$find2; + const nextDate = new Date(nextProps.startDateTime); + nextState.monthDays = [nextDate.getDate()]; + nextState.offset.weekDay = ((_Object$values$find2 = Object.values(this.WEEK_DAYS).find(d => d.value === nextDate.getDay())) == null ? void 0 : _Object$values$find2.value) || this.WEEK_DAYS.SUNDAY.value; + nextState.monthDaysWarning = nextState.monthDays > 28; + } + if (nextState.view === this.VIEWS.MONTHLY && this.state.interval > 12) { + nextState.interval = 12; + } + this.props.onUpdate(this.getFormattedState(nextState)); + } + componentDidMount() { + super.componentDidMount(); + this.props.onUpdate(this.getFormattedState(this.state)); + } + render() { + const { + NAMESPACE + } = Recurring; + const { + view + } = this.state; + return JSX_(schedule_utils.fI, null, JSX_(schedule_utils.VP, null), JSX_(schedule_utils.VP, null, JSX_("div", { + ref: this.domRef, + className: ` + ${NAMESPACE} + ${this.props.isLoading ? 'disabled' : ''} + ` + }, JSX_("div", { + className: `${NAMESPACE}-container` + }, JSX_("div", { + className: `${NAMESPACE}-navigation` + }, this.renderNavigation(view)), JSX_("div", { + className: `${NAMESPACE}-content` + }, this.renderContent(view)))))); + } +} +Recurring.NAMESPACE = 'meetings-recurring'; +;// ./js/chat/ui/meetings/schedule/schedule.jsx + + + + + + + + + + + + + + +class Schedule extends mixins.w9 { + constructor(...args) { + super(...args); + this.domRef = REaCt().createRef(); + this.scheduledMeetingRef = null; + this.localStreamRef = '.float-video'; + this.datepickerRefs = []; + this.incomingCallListener = 'onPrepareIncomingCallDialog.scheduleDialog'; + this.ringingStoppedListener = 'onRingingStopped.scheduleDialog'; + this.interval = ChatRoom.SCHEDULED_MEETINGS_INTERVAL; + this.nearestHalfHour = (0,helpers.i_)(); + this.state = { + topic: '', + startDateTime: this.nearestHalfHour, + endDateTime: this.nearestHalfHour + this.interval, + timezone: (0,helpers.dB)(), + recurring: false, + participants: [], + link: false, + sendInvite: false, + waitingRoom: false, + openInvite: false, + description: '', + closeDialog: false, + isEdit: false, + isDirty: false, + isLoading: false, + topicInvalid: false, + invalidTopicMsg: '', + descriptionInvalid: false, + overlayed: false + }; + this.onTopicChange = value => { + if (value.length > ChatRoom.TOPIC_MAX_LENGTH) { + this.setState({ + invalidTopicMsg: l.err_schedule_title_long, + topicInvalid: true + }); + value = value.substring(0, ChatRoom.TOPIC_MAX_LENGTH); + } else if (value.length === 0) { + this.setState({ + invalidTopicMsg: l.schedule_title_missing, + topicInvalid: true + }); + } else if (this.state.invalidTopicMsg) { + this.setState({ + invalidTopicMsg: '', + topicInvalid: false + }); + } + this.handleChange('topic', value); + }; + this.onTextareaChange = value => { + if (value.length > 3000) { + this.setState({ + descriptionInvalid: true + }); + value = value.substring(0, 3000); + } else if (this.state.descriptionInvalid) { + this.setState({ + descriptionInvalid: false + }); + } + this.handleChange('description', value); + }; + this.onStartDateSelect = () => { + this.datepickerRefs.endDateTime.selectDate(new Date(this.state.startDateTime + this.interval)); + }; + this.onEndDateSelect = () => { + const { + startDateTime, + endDateTime + } = this.state; + if (endDateTime < startDateTime) { + if (endDateTime < Date.now()) { + return this.setState({ + endDateTime: startDateTime + this.interval + }); + } + this.handleDateSelect({ + startDateTime: endDateTime - this.interval + }); + } + }; + this.handleToggle = prop => { + return Object.keys(this.state).includes(prop) && this.setState(state => ({ + [prop]: !state[prop], + isDirty: true + })); + }; + this.handleChange = (prop, value) => { + return Object.keys(this.state).includes(prop) && this.setState({ + [prop]: value, + isDirty: true + }); + }; + this.handleDateSelect = ({ + startDateTime, + endDateTime + }, callback) => { + this.setState(state => ({ + startDateTime: startDateTime || state.startDateTime, + endDateTime: endDateTime || state.endDateTime, + isDirty: true + }), () => { + const { + recurring + } = this.state; + if (recurring && recurring.end) { + const recurringEnd = (0,helpers.PS)(this.state.startDateTime, 6); + this.datepickerRefs.recurringEnd.selectDate(new Date(recurringEnd)); + } + if (callback) { + callback(); + } + }); + }; + this.handleTimeSelect = ({ + startDateTime, + endDateTime + }) => { + startDateTime = startDateTime || this.state.startDateTime; + endDateTime = endDateTime || this.state.endDateTime; + this.setState(state => { + return { + startDateTime: endDateTime <= state.startDateTime ? endDateTime - this.interval : startDateTime, + endDateTime: startDateTime >= state.endDateTime ? startDateTime + this.interval : endDateTime, + isDirty: true + }; + }); + }; + this.handleParticipantSelect = participants => { + return participants && Array.isArray(participants) && this.setState({ + participants, + isDirty: true + }, () => { + const domRef = this.domRef && this.domRef.current; + if (domRef) { + domRef.reinitialise(); + } + }); + }; + this.handleSubmit = () => { + if (this.state.topic) { + return this.setState({ + isLoading: true + }, async () => { + const { + chatRoom, + onClose + } = this.props; + const params = [this.state, chatRoom]; + if (chatRoom) { + delay('chat-event-sm-edit-meeting', () => eventlog(99923)); + } else { + delay('chat-event-sm-button-create', () => eventlog(99922)); + } + delay('chat-events-sm-settings', () => this.submitStateEvents({ + ...this.state + })); + await megaChat.plugins.meetingsManager[chatRoom ? 'updateMeeting' : 'createMeeting'](...params); + this.setState({ + isLoading: false + }, () => { + onClose(); + megaChat.trigger(conversations.qY.NAV_RENDER_VIEW, conversations.Vw.MEETINGS); + }); + }); + } + return this.setState({ + topicInvalid: true, + invalidTopicMsg: l.schedule_title_missing + }); + }; + } + syncPublicLink() { + if (this.state.isEdit) { + const { + chatRoom + } = this.props; + chatRoom.updatePublicHandle().then(() => this.isMounted() && this.setState({ + link: !!chatRoom.publicLink + })).catch(dump); + } + } + getFilteredTimeIntervals(timestamp, offsetFrom) { + const timeIntervals = (0,helpers.a4)(timestamp, offsetFrom); + const { + end + } = this.scheduledMeetingRef || {}; + if (this.state.isEdit && end < Date.now()) { + return timeIntervals; + } + return timeIntervals.filter(o => { + return offsetFrom ? o.value > this.nearestHalfHour : o.value > Date.now(); + }); + } + submitStateEvents(state) { + if (state.link) { + eventlog(500162); + } + if (state.sendInvite) { + eventlog(500163); + } + if (state.waitingRoom) { + eventlog(500164); + } + if (state.openInvite) { + eventlog(500165); + } + if (state.description) { + eventlog(500166); + } + if (state.recurring) { + eventlog(500167); + } else { + eventlog(500168); + } + eventlog(500169, state.topic.length); + } + componentWillUnmount() { + super.componentWillUnmount(); + if ($.dialog === schedule_utils.oK) { + closeDialog(); + } + [document, this.localStreamRef].map(el => $(el).unbind(`.${schedule_utils.CU}`)); + megaChat.off(this.incomingCallListener); + } + UNSAFE_componentWillMount() { + const { + chatRoom + } = this.props; + if (chatRoom) { + const { + scheduledMeeting, + publicLink, + options + } = chatRoom; + this.state.topic = scheduledMeeting.title; + this.state.startDateTime = scheduledMeeting.start; + this.state.endDateTime = scheduledMeeting.end; + this.state.timezone = scheduledMeeting.timezone || (0,helpers.dB)(); + this.state.recurring = scheduledMeeting.recurring; + this.state.participants = chatRoom.getParticipantsExceptMe(); + this.state.link = !!publicLink; + this.state.description = scheduledMeeting.description || ''; + this.state.sendInvite = scheduledMeeting.flags; + this.state.waitingRoom = options[chat_chatRoom.U_.WAITING_ROOM]; + this.state.openInvite = options[chat_chatRoom.U_.OPEN_INVITE]; + this.state.isEdit = true; + this.scheduledMeetingRef = scheduledMeeting; + } + } + componentDidMount() { + super.componentDidMount(); + this.syncPublicLink(); + if ($.dialog === 'onboardingDialog') { + closeDialog(); + } + M.safeShowDialog(schedule_utils.oK, () => { + if (!this.isMounted()) { + throw new Error(`${schedule_utils.oK} dialog: component ${schedule_utils.CU} not mounted.`); + } + $(document).rebind(`keyup.${schedule_utils.CU}`, ({ + keyCode, + target + }) => { + return this.state.closeDialog || target instanceof HTMLTextAreaElement ? null : keyCode === 13 && this.handleSubmit(); + }); + $(this.localStreamRef).rebind(`click.${schedule_utils.CU}`, () => { + if (this.state.isDirty) { + this.handleToggle('closeDialog'); + return false; + } + }); + megaChat.rebind(this.incomingCallListener, () => { + if (this.isMounted()) { + this.setState({ + overlayed: true, + closeDialog: false + }); + megaChat.plugins.callManager2.rebind(this.ringingStoppedListener, () => { + megaChat.plugins.callManager2.off(this.ringingStoppedListener); + this.setState({ + overlayed: false + }); + fm_showoverlay(); + }); + } + }); + return $(`#${schedule_utils.CU}`); + }); + } + componentDidUpdate(prevProps) { + if (prevProps.callExpanded && !this.props.callExpanded) { + if (!$.dialog) { + M.safeShowDialog(schedule_utils.oK, `#${schedule_utils.CU}`); + } + fm_showoverlay(); + this.setState({ + closeDialog: false + }); + } + if (!prevProps.callExpanded && this.props.callExpanded) { + this.setState({ + closeDialog: false + }); + } + } + render() { + let _this$props$chatRoom; + const { + topic, + startDateTime, + endDateTime, + recurring, + participants, + link, + sendInvite, + waitingRoom, + openInvite, + description, + closeDialog, + isEdit, + isDirty, + isLoading, + topicInvalid, + invalidTopicMsg, + descriptionInvalid, + overlayed + } = this.state; + return JSX_(modalDialogs.A.ModalDialog, (0,esm_extends.A)({}, this.state, { + id: schedule_utils.CU, + className: ` + ${closeDialog ? 'with-confirmation-dialog' : ''} + ${this.props.callExpanded || overlayed ? 'hidden' : ''} + `, + dialogName: schedule_utils.oK, + dialogType: "main", + onClose: () => isDirty ? this.handleToggle('closeDialog') : this.props.onClose() + }), JSX_(Header, { + chatRoom: isEdit && this.props.chatRoom + }), JSX_(perfectScrollbar.O, { + ref: this.domRef, + className: "fm-dialog-body", + options: { + suppressScrollX: true + } + }, JSX_(schedule_utils.pd, { + name: "topic", + placeholder: l.schedule_title_input, + value: topic, + invalid: topicInvalid, + invalidMessage: invalidTopicMsg, + autoFocus: true, + isLoading, + onFocus: () => topicInvalid && this.setState({ + topicInvalid: false + }), + onChange: this.onTopicChange + }), JSX_(schedule_utils.fI, { + className: `unencrypted-warning-row ${topicInvalid ? 'with-topic-err' : ''}` + }, JSX_(schedule_utils.VP, null), JSX_(schedule_utils.VP, null, JSX_("div", { + className: "unencrypted-warning" + }, JSX_("i", { + className: "sprite-fm-mono icon-info" + }), JSX_("span", null, l.schedule_encryption_note)))), JSX_(schedule_utils.fI, { + className: "start-aligned" + }, JSX_(schedule_utils.VP, null, JSX_("i", { + className: "sprite-fm-mono icon-recents-filled" + })), JSX_("div", { + className: "schedule-date-container" + }, JSX_(datetime.c, { + name: "startDateTime", + altField: "startTime", + datepickerRef: this.datepickerRefs.startDateTime, + startDate: startDateTime, + value: startDateTime, + filteredTimeIntervals: this.getFilteredTimeIntervals(startDateTime), + label: l.schedule_start_date, + isLoading, + onMount: datepicker => { + this.datepickerRefs.startDateTime = datepicker; + }, + onSelectDate: startDateTime => { + this.handleDateSelect({ + startDateTime + }, this.onStartDateSelect); + }, + onSelectTime: ({ + value: startDateTime + }) => this.handleTimeSelect({ + startDateTime + }), + onChange: value => this.handleChange('startDateTime', value), + onBlur: timestamp => { + if (timestamp) { + const startDateTime = timestamp < Date.now() ? this.nearestHalfHour : timestamp; + this.handleDateSelect({ + startDateTime + }, this.onStartDateSelect); + } + } + }), JSX_(datetime.c, { + name: "endDateTime", + altField: "endTime", + datepickerRef: this.datepickerRefs.endDateTime, + isLoading, + startDate: endDateTime, + value: endDateTime, + filteredTimeIntervals: this.getFilteredTimeIntervals(endDateTime, startDateTime), + label: l.schedule_end_date, + onMount: datepicker => { + this.datepickerRefs.endDateTime = datepicker; + }, + onSelectDate: endDateTime => { + this.handleDateSelect({ + endDateTime + }, this.onEndDateSelect); + }, + onSelectTime: ({ + value: endDateTime + }) => this.handleTimeSelect({ + endDateTime + }), + onChange: value => this.handleChange('endDateTime', value), + onBlur: timestamp => { + this.handleDateSelect({ + endDateTime: timestamp + }, this.onEndDateSelect); + } + }))), !u_attr.p && endDateTime - startDateTime > 36e5 && JSX_(schedule_utils.dh, { + onUpgradeClicked: () => { + this.props.onClose(); + loadSubPage('pro'); + eventlog(500258); + } + }), JSX_(schedule_utils.Sc, { + name: "recurring", + checked: recurring, + label: l.schedule_recurring_label, + isLoading, + onToggle: prop => { + this.handleToggle(prop); + delay('chat-event-sm-recurring', () => eventlog(99919)); + } + }), recurring && JSX_(Recurring, { + chatRoom: this.props.chatRoom, + startDateTime, + endDateTime, + isLoading, + onMount: datepicker => { + this.datepickerRefs.recurringEnd = datepicker; + }, + onUpdate: state => { + this.setState({ + recurring: state + }); + } + }), JSX_(schedule_utils.fI, null, JSX_(schedule_utils.VP, null, JSX_("i", { + className: "sprite-fm-mono icon-contacts" + })), JSX_(schedule_utils.VP, null, JSX_(Invite, { + className: isLoading ? 'disabled' : '', + isLoading, + participants, + onSelect: this.handleParticipantSelect + }))), JSX_(schedule_utils.dO, { + name: "link", + toggled: link, + label: l.schedule_link_label, + isLoading, + subLabel: l.schedule_link_info, + onToggle: prop => { + this.handleToggle(prop); + delay('chat-event-sm-meeting-link', () => eventlog(99920)); + } + }), JSX_(schedule_utils.Sc, { + name: "sendInvite", + checked: sendInvite, + label: l.schedule_invite_label, + isLoading, + onToggle: prop => { + this.handleToggle(prop); + delay('chat-event-sm-calendar-invite', () => eventlog(99921)); + } + }), JSX_(schedule_utils.Sc, { + name: "waitingRoom", + className: (_this$props$chatRoom = this.props.chatRoom) != null && _this$props$chatRoom.havePendingCall() ? 'disabled' : '', + checked: waitingRoom, + label: l.waiting_room, + subLabel: l.waiting_room_info, + isLoading, + onToggle: waitingRoom => { + let _this$props$chatRoom2; + if ((_this$props$chatRoom2 = this.props.chatRoom) != null && _this$props$chatRoom2.havePendingCall()) { + return; + } + this.handleToggle(waitingRoom); + delay('chat-event-sm-waiting-room', () => eventlog(500297)); + } + }), JSX_(schedule_utils.Sc, { + name: "openInvite", + checked: openInvite, + label: l.open_invite_desc, + isLoading, + onToggle: ev => { + this.handleToggle(ev); + delay('chat-event-sm-open-invite', () => eventlog(500298)); + } + }), waitingRoom && openInvite ? JSX_(schedule_utils.fI, null, JSX_("div", { + className: "schedule-dialog-banner warn" + }, JSX_(utils.P9, null, l.waiting_room_invite.replace('[A]', ` + `).replace('[/A]', '')))) : null, JSX_(schedule_utils.TM, { + name: "description", + isLoading, + invalid: descriptionInvalid, + placeholder: l.schedule_description_input, + value: description, + onFocus: () => descriptionInvalid && this.setState({ + descriptionInvalid: false + }), + onChange: this.onTextareaChange + })), JSX_(Footer, { + isLoading, + isEdit, + topic, + onSubmit: this.handleSubmit + }), !(overlayed || this.props.callExpanded) && closeDialog && JSX_(schedule_utils.pD, { + onToggle: this.handleToggle, + onClose: this.props.onClose + })); + } +} +const Header = ({ + chatRoom +}) => { + const $$container = title => JSX_("header", null, JSX_("h2", null, title)); + if (chatRoom) { + const { + scheduledMeeting + } = chatRoom; + return $$container(scheduledMeeting.isRecurring ? l.edit_meeting_series_title : l.edit_meeting_title); + } + return $$container(l.schedule_meeting_title); +}; +const Footer = ({ + isLoading, + isEdit, + topic, + onSubmit +}) => { + return JSX_("footer", null, JSX_("div", { + className: "footer-container" + }, JSX_(meetings_button.A, { + className: ` + mega-button + positive + ${isLoading ? 'disabled' : ''} + `, + onClick: () => isLoading ? null : onSubmit(), + topic + }, JSX_("span", null, isEdit ? l.update_meeting_button : l.schedule_meeting_button)))); +}; + + }, + + 3448 +(_, EXP_, REQ_) { + + REQ_.d(EXP_, { + A: () => __WEBPACK_DEFAULT_EXPORT__ + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _mixins_js1__ = REQ_(8264); + const _ui_perfectScrollbar_jsx2__ = REQ_(1301); + const _helpers_jsx3__ = REQ_(6521); + const _dateObserver4__ = REQ_(8894); + + + + + +class Select extends react0___default().Component { + constructor(...args) { + super(...args); + this.domRef = react0___default().createRef(); + this.inputRef = react0___default().createRef(); + this.menuRef = react0___default().createRef(); + this.optionRefs = {}; + this.state = { + expanded: false, + manualTimeInput: '', + timestamp: '' + }; + this.handleMousedown = ({ + target + }) => { + let _this$domRef; + return (_this$domRef = this.domRef) != null && _this$domRef.current.contains(target) ? null : this.setState({ + expanded: false + }); + }; + this.handleToggle = ({ + target + } = {}) => { + let _this$menuRef, _menuRef$domRef; + const menuRef = (_this$menuRef = this.menuRef) == null ? void 0 : _this$menuRef.current; + const menuElement = (_menuRef$domRef = menuRef.domRef) == null ? void 0 : _menuRef$domRef.current; + if (target !== menuElement) { + const { + value + } = this.props; + this.setState(state => ({ + expanded: !state.expanded + }), () => { + if (value && this.optionRefs[value]) { + menuRef.scrollToElement(this.optionRefs[value]); + } + }); + } + }; + } + getFormattedDuration(duration) { + duration = moment.duration(duration); + const days = duration.get('days'); + const hours = duration.get('hours'); + const minutes = duration.get('minutes'); + if (!hours && !minutes && !days) { + return ''; + } + const totalHours = days ? ~~duration.asHours() : hours; + if (!hours && minutes) { + return days ? `(${totalHours}\u00a0h ${minutes}\u00a0m)` : `(${minutes}\u00a0m)`; + } + return minutes ? `(${totalHours}\u00a0h ${minutes}\u00a0m)` : `(${totalHours}\u00a0h)`; + } + componentWillUnmount() { + document.removeEventListener('mousedown', this.handleMousedown); + if (this.inputRef && this.inputRef.current) { + $(this.inputRef.current).unbind(`keyup.${Select.NAMESPACE}`); + } + } + componentDidMount() { + let _this$inputRef; + document.addEventListener('mousedown', this.handleMousedown); + const inputRef = (_this$inputRef = this.inputRef) == null ? void 0 : _this$inputRef.current; + if (inputRef) { + $(inputRef).rebind(`keyup.${Select.NAMESPACE}`, ({ + keyCode + }) => { + if (keyCode === 13) { + this.handleToggle(); + inputRef.blur(); + return false; + } + }); + } + } + render() { + const { + NAMESPACE + } = Select; + const { + name, + className, + icon, + typeable, + options, + value, + format, + isLoading, + onChange, + onBlur, + onSelect + } = this.props; + return JSX_("div", { + ref: this.domRef, + className: ` + ${NAMESPACE} + ${className || ''} + ` + }, JSX_("div", { + className: ` + mega-input + dropdown-input + ${typeable ? 'typeable' : ''} + `, + onClick: isLoading ? null : this.handleToggle + }, typeable ? null : value && JSX_("span", null, format ? format(value) : value), JSX_("input", { + ref: this.inputRef, + type: "text", + className: ` + ${NAMESPACE}-input + ${name} + `, + value: (() => { + if (this.state.manualTimeInput) { + return this.state.manualTimeInput; + } + return format ? format(value) : value; + })(), + onFocus: ({ + target + }) => { + this.setState({ + manualTimeInput: '', + timestamp: '' + }, () => target.select()); + }, + onChange: ({ + target + }) => { + const { + value: manualTimeInput + } = target; + const { + value + } = this.props; + const prevDate = moment(value); + const inputTime = (0,_helpers_jsx3__ .We)(manualTimeInput); + prevDate.set({ + hours: inputTime.get('hours'), + minutes: inputTime.get('minutes') + }); + const timestamp = prevDate.valueOf(); + onChange == null || onChange(timestamp); + if (this.optionRefs[value]) { + this.menuRef.current.scrollToElement(this.optionRefs[value]); + } + this.setState({ + manualTimeInput, + timestamp + }); + }, + onBlur: () => { + onBlur(this.state.timestamp); + this.setState({ + manualTimeInput: '', + timestamp: '' + }); + } + }), icon && JSX_("i", { + className: "sprite-fm-mono icon-dropdown" + }), options && JSX_("div", { + className: ` + mega-input-dropdown + ${this.state.expanded ? '' : 'hidden'} + ` + }, JSX_(_ui_perfectScrollbar_jsx2__ .O, { + ref: this.menuRef, + options: { + suppressScrollX: true + } + }, options.map(option => { + return JSX_("div", { + ref: ref => { + this.optionRefs[option.value] = ref; + }, + key: option.value, + className: ` + option + ${option.value === value || option.label === value ? 'active' : ''} + `, + onClick: () => onSelect(option) + }, option.label, "\xA0", option.duration && this.getFormattedDuration(option.duration)); + }))))); + } +} +Select.NAMESPACE = 'meetings-select'; + const __WEBPACK_DEFAULT_EXPORT__ = (0,_mixins_js1__ .Zz)(_dateObserver4__ .V)(Select); + + }, + + 1497 +(_, EXP_, REQ_) { + + REQ_.d(EXP_, { + CU: () => NAMESPACE, + Sc: () => Checkbox, + TM: () => Textarea, + VP: () => Column, + dO: () => Switch, + dh: () => UpgradeNotice, + fI: () => Row, + oK: () => dialogName, + pD: () => CloseDialog, + pd: () => Input + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _ui_modalDialogs_jsx1__ = REQ_(8120); + const _button_jsx2__ = REQ_(6740); + + + +const NAMESPACE = 'schedule-dialog'; +const dialogName = `meetings-${"schedule-dialog"}`; +const CloseDialog = ({ + onToggle, + onClose +}) => { + return JSX_(react0___default().Fragment, null, JSX_(_ui_modalDialogs_jsx1__ .A.ModalDialog, { + name: `${NAMESPACE}-confirmation`, + dialogType: "message", + className: ` + with-close-btn + ${NAMESPACE}-confirmation + `, + title: l.schedule_discard_dlg_title, + icon: "sprite-fm-uni icon-question", + buttons: [{ + key: 'n', + label: l.schedule_discard_cancel, + onClick: () => onToggle('closeDialog') + }, { + key: 'y', + label: l.schedule_discard_confirm, + className: 'positive', + onClick: onClose + }], + noCloseOnClickOutside: true, + stopKeyPropagation: true, + hideOverlay: true, + onClose: () => onToggle('closeDialog') + }), JSX_("div", { + className: `${NAMESPACE}-confirmation-overlay`, + onClick: () => onToggle('closeDialog') + })); +}; +const Row = ({ + children, + className +}) => JSX_("div", { + className: ` + ${NAMESPACE}-row + ${className || ''} + ` +}, children); +const Column = ({ + children, + className +}) => JSX_("div", { + className: ` + ${NAMESPACE}-column + ${className || ''} + ` +}, children); +const Input = ({ + name, + placeholder, + value, + invalid, + invalidMessage, + autoFocus, + isLoading, + onFocus, + onChange +}) => { + return JSX_(Row, { + className: invalid ? 'invalid-aligned' : '' + }, JSX_(Column, null, JSX_("i", { + className: "sprite-fm-mono icon-rename" + })), JSX_(Column, null, JSX_("div", { + className: ` + mega-input + ${invalid ? 'error msg' : ''} + ` + }, JSX_("input", { + type: "text", + name: `${NAMESPACE}-${name}`, + className: isLoading ? 'disabled' : '', + disabled: isLoading, + autoFocus, + autoComplete: "off", + placeholder, + value, + onFocus, + onChange: ({ + target + }) => onChange(target.value) + }), invalid && JSX_("div", { + className: "message-container mega-banner" + }, invalidMessage)))); +}; +const Checkbox = ({ + name, + className, + checked, + label, + subLabel, + isLoading, + onToggle +}) => { + return JSX_(Row, { + className: ` + ${subLabel ? 'start-aligned' : ''} + ${className || ''} + ` + }, JSX_(Column, null, JSX_("div", { + className: ` + checkdiv + ${checked ? 'checkboxOn' : 'checkboxOff'} + ${isLoading ? 'disabled' : ''} + ` + }, JSX_("input", { + name: `${NAMESPACE}-${name}`, + disabled: isLoading, + type: "checkbox", + onChange: () => onToggle(name) + }))), JSX_(Column, { + className: subLabel ? 'with-sub-label' : '' + }, JSX_("label", { + htmlFor: `${NAMESPACE}-${name}`, + className: isLoading ? 'disabled' : '', + onClick: () => isLoading ? null : onToggle(name) + }, label), subLabel && JSX_("div", { + className: "sub-label" + }, subLabel))); +}; +const Switch = ({ + name, + toggled, + label, + isLoading, + subLabel, + onToggle +}) => { + return JSX_(Row, null, JSX_(Column, null, JSX_("i", { + className: "sprite-fm-uni icon-mega-logo" + })), JSX_(Column, { + className: subLabel ? `with-sub-label ${"schedule-dialog-switch"}` : "schedule-dialog-switch" + }, JSX_("span", { + className: ` + schedule-label + ${isLoading ? 'disabled' : ''} + `, + onClick: () => isLoading ? null : onToggle(name) + }, label), JSX_("div", { + className: ` + mega-switch + ${toggled ? 'toggle-on' : ''} + ${isLoading ? 'disabled' : ''} + `, + onClick: () => isLoading ? null : onToggle(name) + }, JSX_("div", { + className: ` + mega-feature-switch + sprite-fm-mono-after + ${toggled ? 'icon-check-after' : 'icon-minimise-after'} + ` + })), subLabel && JSX_("div", { + className: "sub-label" + }, subLabel))); +}; +const Textarea = ({ + name, + placeholder, + isLoading, + value, + invalid, + onChange, + onFocus +}) => { + return JSX_(Row, { + className: "start-aligned" + }, JSX_(Column, null, JSX_("i", { + className: "sprite-fm-mono icon-description" + })), JSX_(Column, null, JSX_("div", { + className: `mega-input box-style textarea ${invalid ? 'error' : ''}` + }, JSX_("textarea", { + name: `${NAMESPACE}-${name}`, + className: isLoading ? 'disabled' : '', + placeholder, + value, + readOnly: isLoading, + onChange: ({ + target + }) => onChange(target.value), + onFocus + })), invalid && JSX_("div", { + className: "mega-input error msg textarea-error" + }, JSX_("div", { + className: "message-container mega-banner" + }, l.err_schedule_desc_long)))); +}; +const UpgradeNotice = ({ + onUpgradeClicked +}) => { + return !!mega.flags.ff_chmon && JSX_(Row, { + className: "schedule-upgrade-notice" + }, JSX_("h3", null, l.schedule_limit_title), JSX_("div", null, l.schedule_limit_upgrade_features), JSX_(_button_jsx2__ .A, { + className: "mega-button positive", + onClick: onUpgradeClicked + }, JSX_("span", null, l.upgrade_now))); +}; + + } + +}]); \ No newline at end of file diff --git a/js/chat/bundle.start-conversation.js b/js/chat/bundle.start-conversation.js new file mode 100644 index 0000000000..ad3ec0de31 --- /dev/null +++ b/js/chat/bundle.start-conversation.js @@ -0,0 +1,1233 @@ +/** @file automatically generated, do not edit it. */ +"use strict"; +(self.webpackChunk_meganz_webclient = self.webpackChunk_meganz_webclient || []).push([[543],{ + + 2678 +(_, EXP_, REQ_) { + +REQ_.r(EXP_); + REQ_.d(EXP_, { + "default": () => __WEBPACK_DEFAULT_EXPORT__ + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _contacts1__ = REQ_(8022); + const _ui_modalDialogs_jsx2__ = REQ_(8120); + const _mixins_js3__ = REQ_(8264); + + + + +class ContactSelectorDialog extends _mixins_js3__ .w9 { + constructor(...args) { + super(...args); + this.dialogName = 'contact-selector-dialog'; + } + componentDidMount() { + super.componentDidMount(); + M.safeShowDialog(this.dialogName, () => $(`.${this.dialogName}`)); + } + componentWillUnmount() { + super.componentWillUnmount(); + if ($.dialog === this.dialogName) { + closeDialog(); + } + } + render() { + const { + active, + selectFooter, + exclude, + allowEmpty, + multiple, + topButtons, + showAddContact, + className, + multipleSelectedButtonLabel, + singleSelectedButtonLabel, + nothingSelectedButtonLabel, + onClose, + onSelectDone + } = this.props; + return JSX_(_ui_modalDialogs_jsx2__ .A.ModalDialog, { + className: ` + popup + contacts-search + ${className} + ${this.dialogName} + `, + onClose + }, JSX_(_contacts1__ .hU, { + active, + className: "popup contacts-search small-footer", + contacts: M.u, + selectFooter, + megaChat, + withSelfNote: megaChat.WITH_SELF_NOTE, + exclude, + allowEmpty, + multiple, + topButtons, + showAddContact, + multipleSelectedButtonLabel, + singleSelectedButtonLabel, + nothingSelectedButtonLabel, + onClose, + onAddContact: () => { + eventlog(500237); + onClose(); + }, + onSelected: () => { + eventlog(500238); + onClose(); + }, + onSelectDone + })); + } +} + const __WEBPACK_DEFAULT_EXPORT__ = ContactSelectorDialog; + + }, + + 192 +(_, EXP_, REQ_) { + + REQ_.d(EXP_, { + $: () => withPermissionsObserver + }); + const _babel_runtime_helpers_extends0__ = REQ_(8168); + const react1__ = REQ_(1594); + const react1___default = REQ_.n(react1__); + const _mixins_js2__ = REQ_(8264); + const _ui_modalDialogs_jsx3__ = REQ_(8120); + const _ui_utils_jsx4__ = REQ_(6411); + + + + + +const errors = { + browser: 'NotAllowedError: Permission denied', + system: 'NotAllowedError: Permission denied by system', + dismissed: 'NotAllowedError: Permission dismissed', + nil: 'NotFoundError: Requested device not found', + sharedCam: 'NotReadableError: Could not start video source', + sharedMic: 'NotReadableError: Could not start audio source', + sharedGeneric: 'NotReadableError: Device in use' +}; +const isUserActionError = error => { + return error && error === errors.browser; +}; +const withPermissionsObserver = Component => { + return class extends _mixins_js2__ .w9 { + constructor(props) { + super(props); + this.namespace = `PO-${Component.NAMESPACE}`; + this.observer = `onLocalMediaError.${this.namespace}`; + this.childRef = undefined; + this.platform = ua.details.os; + this.helpURL = `${l.mega_help_host}/chats-meetings/meetings/enable-audio-video-call-permissions`; + this.macURI = 'x-apple.systempreferences:com.apple.preference.security'; + this.winURI = 'ms-settings'; + this.CONTENT = { + [Av.Audio]: { + system: { + title: l.no_mic_title, + info: this.platform === 'Windows' ? l.no_mic_system_windows.replace('[A]', ``).replace('[/A]', '') : l.no_mic_system_mac.replace('[A]', ``).replace('[/A]', ''), + buttons: [this.platform === 'Apple' || this.platform === 'Windows' ? { + key: 'open-settings', + label: l.open_system_settings, + className: 'positive', + onClick: () => { + window.open(this.platform === 'Apple' ? `${this.macURI}?Privacy_Microphone` : `${this.winURI}:privacy-microphone`, '_blank', 'noopener,noreferrer'); + this.closePermissionsDialog(Av.Audio); + } + } : { + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Audio) + }] + }, + browser: { + title: l.no_mic_title, + cover: 'permissions-mic', + info: l.allow_mic_access.replace('[X]', ''), + buttons: [{ + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Audio) + }] + }, + nil: { + title: l.no_mic_detected_title, + info: l.no_mic_detected_info, + buttons: [{ + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Audio) + }] + }, + shared: { + title: l.no_mic_title, + info: l.shared_mic_err_info.replace('[A]', ``).replace('[/A]', ''), + buttons: [{ + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Audio) + }] + } + }, + [Av.Camera]: { + system: { + title: l.no_camera_title, + info: this.platform === 'Windows' ? l.no_camera_system_windows.replace('[A]', ``).replace('[/A]', '') : l.no_camera_system_mac.replace('[A]', ``).replace('[/A]', ''), + buttons: [this.platform === 'Apple' || this.platform === 'Windows' ? { + key: 'open-settings', + label: l.open_system_settings, + className: 'positive', + onClick: () => { + window.open(this.platform === 'Apple' ? `${this.macURI}?Privacy_Camera` : `${this.winURI}:privacy-webcam`, '_blank', 'noopener,noreferrer'); + this.closePermissionsDialog(Av.Camera); + } + } : { + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Camera) + }] + }, + browser: { + title: l.no_camera_title, + cover: 'permissions-camera', + info: l.allow_camera_access.replace('[X]', ''), + buttons: [{ + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Camera) + }] + }, + nil: { + title: l.no_camera_detected_title, + info: l.no_camera_detected_info, + buttons: [{ + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Camera) + }] + }, + shared: { + title: l.no_camera_title, + info: l.shared_cam_err_info.replace('[A]', ``).replace('[/A]', ''), + buttons: [{ + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Camera) + }] + } + }, + [Av.Screen]: { + title: l.no_screen_title, + info: l.no_screen_system.replace('[A]', ``).replace('[/A]', ''), + buttons: [{ + key: 'open-settings', + label: l.open_system_settings, + className: 'positive', + onClick: () => { + window.open(`${this.macURI}?Privacy_ScreenCapture`, '_blank', 'noopener,noreferrer'); + this.closePermissionsDialog(Av.Screen); + } + }] + } + }; + this.state = { + errMic: '', + errCamera: '', + errScreen: '', + [`dialog-${Av.Audio}`]: null, + [`dialog-${Av.Camera}`]: null, + [`dialog-${Av.Screen}`]: null + }; + this.getPermissionsDialogContent = () => { + const { + CONTENT, + state + } = this; + const { + errMic, + errCamera + } = state; + const { + browser, + system, + nil, + sharedCam, + sharedMic, + sharedGeneric + } = errors; + return { + [Av.Audio]: { + ...errMic === browser && CONTENT[Av.Audio].browser, + ...errMic === system && CONTENT[Av.Audio].system, + ...errMic === nil && CONTENT[Av.Audio].nil, + ...errMic === sharedMic && CONTENT[Av.Audio].shared, + ...errMic === sharedGeneric && CONTENT[Av.Audio].shared + }, + [Av.Camera]: { + ...errCamera === browser && CONTENT[Av.Camera].browser, + ...errCamera === system && CONTENT[Av.Camera].system, + ...errCamera === nil && CONTENT[Av.Camera].nil, + ...errCamera === sharedCam && CONTENT[Av.Camera].shared, + ...errCamera === sharedGeneric && CONTENT[Av.Camera].shared + }, + [Av.Screen]: CONTENT[Av.Screen] + }; + }; + this.resetError = av => { + this.setState({ + errMic: av === Av.Audio ? '' : this.state.errMic, + errCamera: av === Av.Camera ? '' : this.state.errCamera, + errScreen: av === Av.Screen ? '' : this.state.errScreen + }); + }; + this.hasToRenderPermissionsWarning = this.hasToRenderPermissionsWarning.bind(this); + this.renderPermissionsWarning = this.renderPermissionsWarning.bind(this); + } + hasToRenderPermissionsWarning(av) { + const CONFIG = { + [Av.Audio]: { + showOnUserActionError: true, + err: this.state.errMic + }, + [Av.Camera]: { + showOnUserActionError: true, + err: this.state.errCamera + }, + [Av.Screen]: { + showOnUserActionError: false, + err: this.state.errScreen + } + }; + const current = CONFIG[av]; + if (current) { + return isUserActionError(current.err) ? current.showOnUserActionError : current.err; + } + return false; + } + closePermissionsDialog(av) { + this.setState({ + [`dialog-${av}`]: false + }, () => { + let _this$childRef; + return (_this$childRef = this.childRef) == null ? void 0 : _this$childRef.safeForceUpdate(); + }); + } + renderPermissionsDialog(av, child) { + const content = this.getPermissionsDialogContent(); + const { + title, + info, + buttons, + cover + } = content[av] || {}; + return JSX_(_ui_modalDialogs_jsx3__ .A.ModalDialog, { + dialogName: `${this.namespace}-permissions-${av}`, + className: ` + meetings-permissions-dialog + dialog-template-message + with-close-btn + warning + `, + buttons, + hideOverlay: Component.NAMESPACE === 'preview-meeting' && !document.body.classList.contains('not-logged'), + onClose: () => { + this.setState({ + [`dialog-${av}`]: false + }, () => child && child.safeForceUpdate()); + } + }, JSX_("header", null, cover ? null : JSX_("div", { + className: "graphic" + }, JSX_("i", { + className: "warning sprite-fm-uni icon-warning" + })), JSX_("div", { + className: "info-container" + }, JSX_("h3", { + id: "msgDialog-title" + }, title || l[47]), cover && JSX_("div", { + className: "permissions-warning-cover" + }, JSX_("span", { + className: cover + })), JSX_(_ui_utils_jsx4__ .P9, { + tag: "p", + className: "permissions-warning-info", + content: info + })))); + } + renderPermissionsWarning(av, child) { + const { + errMic, + errCamera + } = this.state; + const dismissed = errMic === errors.dismissed || errCamera === errors.dismissed; + return JSX_("div", { + className: ` + ${this.namespace} + meetings-signal-issue + simpletip + ${dismissed ? 'with-small-area' : ''} + `, + "data-simpletip": l.show_info, + "data-simpletipposition": "top", + "data-simpletipoffset": "5", + "data-simpletip-class": "theme-dark-forced", + onClick: () => dismissed ? null : this.setState({ + [`dialog-${av}`]: true + }, () => { + if (child) { + this.childRef = child; + } + }) + }, JSX_("span", { + className: "signal-issue-background" + }), JSX_("i", { + className: "sprite-fm-mono icon-exclamation-filled" + }), this.state[`dialog-${av}`] && this.renderPermissionsDialog(av, child)); + } + componentWillUnmount() { + super.componentWillUnmount(); + megaChat.unbind(this.observer); + } + componentDidMount() { + super.componentDidMount(); + megaChat.rebind(this.observer, (ev, errAv) => { + this.setState({ + errMic: errAv && errAv.mic ? String(errAv.mic) : this.state.errMic, + errCamera: errAv && errAv.camera ? String(errAv.camera) : this.state.errCamera, + errScreen: errAv && errAv.screen ? String(errAv.screen) : this.state.errScreen + }); + }); + megaChat.rebind(`onLocalMediaQueryError.${this.namespace}`, (ev, { + type, + err + }) => { + if (type === 'screen' && String(err) === errors.system) { + this.setState({ + [`dialog-${Av.Screen}`]: true + }, () => this.safeForceUpdate()); + } + }); + } + render() { + return JSX_(Component, (0,_babel_runtime_helpers_extends0__ .A)({}, this.props, this.state, { + errMic: this.state.errMic, + errCamera: this.state.errCamera, + errScreen: this.state.errScreen, + hasToRenderPermissionsWarning: this.hasToRenderPermissionsWarning, + resetError: this.resetError, + renderPermissionsWarning: this.renderPermissionsWarning + })); + } + }; +}; + + }, + + 3546 +(_, EXP_, REQ_) { + + REQ_.d(EXP_, { + A: () => __WEBPACK_DEFAULT_EXPORT__ + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _mixins_js1__ = REQ_(8264); + const _contacts_jsx2__ = REQ_(8022); + const _utils_jsx3__ = REQ_(3901); + const _button_jsx4__ = REQ_(6740); + const _permissionsObserver_jsx5__ = REQ_(192); + + + + + + +class Preview extends react0___default().Component { + constructor(props) { + super(props); + this.domRef = react0___default().createRef(); + this.videoRef = react0___default().createRef(); + this.stream = null; + this.state = { + audio: false, + video: false, + avatarMeta: undefined + }; + this.getTrackType = type => !type ? 'getTracks' : type === Preview.STREAMS.AUDIO ? 'getAudioTracks' : 'getVideoTracks'; + this.startStream = type => { + this.stopStream(); + const { + audio, + video + } = this.state; + navigator.mediaDevices.getUserMedia({ + audio, + video + }).then(stream => { + const videoRef = this.videoRef.current; + if (videoRef) { + videoRef.srcObject = stream; + this.stream = stream; + if (this.props.onToggle) { + this.props.onToggle(this.state.audio, this.state.video); + } + } + }).catch(ex => { + const stream = type === Preview.STREAMS.AUDIO ? 'audio' : 'video'; + return this.domRef.current && this.setState(state => ({ + [stream]: !state[stream] + }), () => { + megaChat.trigger('onLocalMediaError', { + [type === Preview.STREAMS.AUDIO ? 'mic' : 'camera']: `${ex.name}: ${ex.message}` + }); + console.error(`${ex.name}: ${ex.message}`); + }); + }); + }; + this.stopStream = type => { + if (this.stream) { + const trackType = this.getTrackType(type); + const tracks = this.stream[trackType](); + for (const track of tracks) { + track.stop(); + } + } + }; + this.toggleStream = type => { + let _this$props$resetErro, _this$props; + const stream = type === Preview.STREAMS.AUDIO ? 'audio' : 'video'; + this.setState(state => ({ + [stream]: !state[stream] + }), () => { + if (this.props.onToggle) { + this.props.onToggle(this.state.audio, this.state.video); + } + return this.state[stream] ? this.startStream(type) : this.stopStream(type); + }); + (_this$props$resetErro = (_this$props = this.props).resetError) == null || _this$props$resetErro.call(_this$props, type === Preview.STREAMS.AUDIO ? Av.Audio : Av.Camera); + }; + this.renderAvatar = () => { + if ((0,_utils_jsx3__ .P)()) { + return JSX_("div", { + className: "avatar-guest" + }, JSX_("i", { + className: "sprite-fm-uni icon-owner" + })); + } + if (is_chatlink) { + const { + avatarUrl, + color, + shortName + } = this.state.avatarMeta || {}; + return JSX_("div", { + className: ` + avatar-wrapper + ${color ? `color${color}` : ''} + ` + }, avatarUrl && JSX_("img", { + src: avatarUrl, + alt: "" + }), color && JSX_("span", null, shortName)); + } + return JSX_(_contacts_jsx2__ .eu, { + contact: M.u[u_handle] + }); + }; + this.state.audio = this.props.audio || this.state.audio; + if (this.props.video) { + this.state.video = this.props.video; + this.startStream(Preview.STREAMS.VIDEO); + this.props.onToggle(this.state.audio, this.state.video); + } + } + componentWillUnmount() { + this.stopStream(); + } + componentDidMount() { + if (this.props.onToggle) { + this.props.onToggle(this.state.audio, this.state.video); + } + this.setState({ + avatarMeta: is_chatlink ? generateAvatarMeta(u_handle) : undefined + }); + } + render() { + const { + NAMESPACE + } = Preview; + const { + hasToRenderPermissionsWarning, + renderPermissionsWarning + } = this.props; + const { + audio, + video + } = this.state; + const SIMPLETIP_PROPS = { + label: undefined, + position: 'top', + className: 'theme-dark-forced' + }; + return JSX_("div", { + ref: this.domRef, + className: ` + ${NAMESPACE} + local-stream-mirrored + ` + }, video && JSX_("div", { + className: `${NAMESPACE}-video-overlay` + }), JSX_("video", { + className: video ? 'streaming' : '', + muted: true, + autoPlay: true, + ref: this.videoRef + }), !video && this.renderAvatar(), JSX_("div", { + className: `${NAMESPACE}-controls` + }, JSX_("div", { + className: "preview-control-wrapper" + }, JSX_(_button_jsx4__ .A, { + simpletip: { + ...SIMPLETIP_PROPS, + label: audio ? l[16214] : l[16708] + }, + className: ` + mega-button + round + theme-light-forced + ${NAMESPACE}-control + ${audio ? '' : 'with-fill'} + `, + icon: audio ? 'icon-mic-thin-outline' : 'icon-mic-off-thin-outline', + onClick: () => { + this.toggleStream(Preview.STREAMS.AUDIO); + } + }), JSX_("span", null, l.mic_button), hasToRenderPermissionsWarning(Av.Audio) ? renderPermissionsWarning(Av.Audio) : null), JSX_("div", { + className: "preview-control-wrapper" + }, JSX_(_button_jsx4__ .A, { + simpletip: { + ...SIMPLETIP_PROPS, + label: video ? l[22894] : l[22893] + }, + className: ` + mega-button + round + theme-light-forced + ${NAMESPACE}-control + ${video ? '' : 'with-fill'} + `, + icon: video ? 'icon-video-thin-outline' : 'icon-video-off-thin-outline', + onClick: () => this.toggleStream(Preview.STREAMS.VIDEO) + }), JSX_("span", null, l.camera_button), hasToRenderPermissionsWarning(Av.Camera) ? renderPermissionsWarning(Av.Camera) : null))); + } +} +Preview.NAMESPACE = 'preview-meeting'; +Preview.STREAMS = { + AUDIO: 1, + VIDEO: 2 +}; + const __WEBPACK_DEFAULT_EXPORT__ = (0,_mixins_js1__ .Zz)(_permissionsObserver_jsx5__ .$)(Preview); + + }, + + 7190 +(_, EXP_, REQ_) { + +REQ_.r(EXP_); + REQ_.d(EXP_, { + "default": () => Start + }); + const _babel_runtime_helpers_extends0__ = REQ_(8168); + const react1__ = REQ_(1594); + const react1___default = REQ_.n(react1__); + const _ui_modalDialogs_jsx2__ = REQ_(8120); + const _button_jsx3__ = REQ_(6740); + const _preview_jsx4__ = REQ_(3546); + const _link_jsx5__ = REQ_(4649); + const _ui_utils6__ = REQ_(6411); + +let _Start; + + + + + + +class Start extends react1___default().Component { + constructor(props) { + super(props); + this.inputRef = react1___default().createRef(); + this.defaultTopic = l.default_meeting_topic.replace('%NAME', M.getNameByHandle(u_handle)); + this.state = { + audio: false, + video: false, + editing: false, + previousTopic: undefined, + topic: undefined + }; + this.handleChange = ev => this.setState({ + topic: ev.target.value + }); + this.toggleEdit = () => { + this.setState(state => { + const topic = state.topic.trim() || this.defaultTopic; + return { + editing: !state.editing, + topic, + previousTopic: topic + }; + }, () => onIdle(this.doFocus)); + }; + this.doFocus = () => { + if (this.state.editing) { + const input = this.inputRef.current; + input.focus(); + input.setSelectionRange(0, input.value.length); + } + }; + this.doReset = () => this.setState(state => ({ + editing: false, + topic: state.previousTopic, + previousTopic: undefined + })); + this.bindEvents = () => $(document).rebind(`mousedown.${Start.NAMESPACE}`, ev => { + if (this.state.editing && !ev.target.classList.contains(Start.CLASS_NAMES.EDIT) && !ev.target.classList.contains(Start.CLASS_NAMES.INPUT)) { + this.toggleEdit(); + } + }).rebind(`keyup.${Start.NAMESPACE}`, ({ + keyCode + }) => { + if (this.state.editing) { + const [ENTER, ESCAPE] = [13, 27]; + return keyCode === ENTER ? this.toggleEdit() : keyCode === ESCAPE ? this.doReset() : null; + } + }); + this.Input = () => JSX_("input", { + type: "text", + ref: this.inputRef, + className: Start.CLASS_NAMES.INPUT, + value: this.state.topic, + maxLength: ChatRoom.TOPIC_MAX_LENGTH, + onChange: this.handleChange + }); + this.onStreamToggle = (audio, video) => this.setState({ + audio, + video + }); + this.startMeeting = () => { + const { + onStart + } = this.props; + const { + topic, + audio, + video + } = this.state; + if (onStart) { + onStart(topic.trim() || this.defaultTopic, audio, video); + } + }; + this.state.topic = this.defaultTopic; + } + componentDidMount() { + this.bindEvents(); + if ($.dialog === 'onboardingDialog') { + closeDialog(); + } + M.safeShowDialog(Start.dialogName, () => $(`#${Start.NAMESPACE}`)); + } + componentWillUnmount() { + $(document).unbind(`.${Start.NAMESPACE}`); + if ($.dialog === Start.dialogName) { + closeDialog(); + } + } + render() { + const { + NAMESPACE, + CLASS_NAMES + } = Start; + const { + editing, + topic + } = this.state; + return JSX_(_ui_modalDialogs_jsx2__ .A.ModalDialog, (0,_babel_runtime_helpers_extends0__ .A)({}, this.state, { + id: NAMESPACE, + dialogName: NAMESPACE, + className: NAMESPACE, + stopKeyPropagation: editing, + onClose: () => this.props.onClose() + }), JSX_("div", { + className: `${NAMESPACE}-preview` + }, JSX_(_preview_jsx4__ .A, { + context: NAMESPACE, + onToggle: this.onStreamToggle + })), JSX_("div", { + className: "fm-dialog-body" + }, JSX_("div", { + className: `${NAMESPACE}-title` + }, editing ? JSX_(this.Input, null) : JSX_("h2", { + onClick: this.toggleEdit + }, JSX_(_ui_utils6__ .zT, null, topic)), JSX_(_button_jsx3__ .A, { + className: ` + mega-button + action + small + ${CLASS_NAMES.EDIT} + ${editing ? 'editing' : ''} + `, + icon: "icon-rename", + simpletip: { + label: l[1342], + position: 'top' + }, + onClick: this.toggleEdit + }, JSX_("span", null, l[1342]))), JSX_(_button_jsx3__ .A, { + className: "mega-button positive large start-meeting-button", + onClick: () => { + this.startMeeting(); + eventlog(500235); + } + }, JSX_("span", null, l[7315])), JSX_(_link_jsx5__ .A, { + to: "https://mega.io/chatandmeetings", + target: "_blank" + }, l.how_meetings_work))); + } +} +_Start = Start; +Start.NAMESPACE = 'start-meeting'; +Start.dialogName = `${_Start.NAMESPACE}-dialog`; +Start.CLASS_NAMES = { + EDIT: 'call-title-edit', + INPUT: 'call-title-input' +}; +Start.STREAMS = { + AUDIO: 1, + VIDEO: 2 +}; + + }, + + 5199 +(_, EXP_, REQ_) { + +REQ_.r(EXP_); + REQ_.d(EXP_, { + "default": () => StartGroupChatWizard + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _mixins_js1__ = REQ_(8264); + const _ui_miniui_jsx2__ = REQ_(5009); + const _contacts_jsx3__ = REQ_(8022); + const _ui_modalDialogs_jsx4__ = REQ_(8120); + + + + + +class StartGroupChatWizard extends _mixins_js1__ .w9 { + constructor(props) { + super(props); + this.dialogName = 'start-group-chat'; + this.domRef = react0___default().createRef(); + this.inputContainerRef = react0___default().createRef(); + this.inputRef = react0___default().createRef(); + let haveContacts = false; + const keys = M.u.keys(); + for (let i = 0; i < keys.length; i++) { + if (M.u[keys[i]].c === 1) { + haveContacts = true; + break; + } + } + this.state = { + 'selected': this.props.selected ? this.props.selected : [], + haveContacts, + 'step': this.props.flowType === 2 || !haveContacts ? 1 : 0, + 'keyRotation': false, + 'createChatLink': this.props.flowType === 2, + 'groupName': '', + openInvite: 1 + }; + this.onFinalizeClick = this.onFinalizeClick.bind(this); + this.onSelectClicked = this.onSelectClicked.bind(this); + this.onSelected = this.onSelected.bind(this); + } + onSelected(nodes) { + this.setState({ + 'selected': nodes + }); + if (this.props.onSelected) { + this.props.onSelected(nodes); + } + } + onSelectClicked() { + if (this.props.onSelectClicked) { + this.props.onSelectClicked(); + } + } + onFinalizeClick(e) { + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + const { + groupName, + selected, + keyRotation, + createChatLink, + openInvite + } = this.state; + megaChat.createAndShowGroupRoomFor(selected, groupName.trim(), { + keyRotation, + createChatLink: keyRotation ? false : createChatLink, + openInvite + }); + this.props.onClose(this); + eventlog(500236); + } + componentDidMount() { + super.componentDidMount(); + if (!this.props.subDialog) { + M.safeShowDialog(this.dialogName, nop); + } + } + componentWillUnmount() { + super.componentWillUnmount(); + if ($.dialog === this.dialogName) { + closeDialog(); + } + } + render() { + const self = this; + const classes = `new-group-chat contrast small-footer contact-picker-widget ${ self.props.className}`; + let contacts = M.u; + const {haveContacts} = self.state; + const buttons = []; + let allowNext = false; + let failedToEnableChatlink = self.state.failedToEnableChatlink && self.state.createChatLink === true && !self.state.groupName; + if (self.state.keyRotation) { + failedToEnableChatlink = false; + } + let extraContent; + if (this.props.extraContent) { + self.state.step = 0; + extraContent = JSX_("div", { + className: "content-block imported" + }); + } else if (self.state.step === 0 && haveContacts) { + allowNext = true; + buttons.push({ + "label": self.props.cancelLabel, + "key": "cancel", + "onClick" (e) { + self.props.onClose(self); + e.preventDefault(); + e.stopPropagation(); + } + }); + buttons.push({ + "label": l[556], + "key": "next", + "className": !allowNext ? "disabled positive" : "positive", + "onClick" (e) { + e.preventDefault(); + e.stopPropagation(); + self.setState({ + 'step': 1 + }); + } + }); + } else if (self.state.step === 1) { + allowNext = self.state.createChatLink ? !failedToEnableChatlink : true; + contacts = []; + self.state.selected.forEach((h) => { + if (h in M.u) { + contacts.push(M.u[h]); + } + }); + if (!haveContacts || this.props.flowType === 2) { + buttons.push({ + "label": self.props.cancelLabel, + "key": "cancel", + "onClick" (e) { + self.props.onClose(self); + e.preventDefault(); + e.stopPropagation(); + } + }); + } else { + buttons.push({ + "label": l[822], + "key": "back", + "onClick" (e) { + e.preventDefault(); + e.stopPropagation(); + self.setState({ + 'step': 0 + }); + } + }); + } + buttons.push({ + "label": l[726], + "key": "done", + "className": !allowNext ? "positive disabled" : "positive", + "onClick" (e) { + if (self.state.createChatLink === true && !self.state.groupName) { + self.setState({ + 'failedToEnableChatlink': true + }); + } else { + self.onFinalizeClick(e); + } + } + }); + } + let chatInfoElements; + if (self.state.step === 1) { + let _this$state$groupName; + let checkboxClassName = self.state.createChatLink ? "checkboxOn" : "checkboxOff"; + if (failedToEnableChatlink && self.state.createChatLink) { + checkboxClassName += " intermediate-state"; + } + if (self.state.keyRotation) { + checkboxClassName = "checkboxOff"; + } + chatInfoElements = JSX_(react0___default().Fragment, null, JSX_("div", { + className: ` + contacts-search-header left-aligned top-pad + ${failedToEnableChatlink ? 'failed' : ''} + ` + }, JSX_("div", { + className: ` + mega-input + with-icon + box-style + ${((_this$state$groupName = this.state.groupName) == null ? void 0 : _this$state$groupName.length) > 0 ? 'valued' : ''} + ${failedToEnableChatlink ? 'error msg' : ''} + `, + ref: this.inputContainerRef + }, JSX_("i", { + className: "sprite-fm-mono icon-channel-new" + }), JSX_("input", { + autoFocus: true, + className: "megaInputs", + type: "text", + ref: this.inputRef, + placeholder: l[18509], + value: this.state.groupName, + maxLength: ChatRoom.TOPIC_MAX_LENGTH, + onKeyDown: e => { + const code = e.which || e.keyCode; + if (allowNext && code === 13 && self.state.step === 1) { + this.onFinalizeClick(); + } + }, + onChange: e => { + const containerRef = this.inputContainerRef.current; + const { + value + } = e.target; + containerRef.classList[value.length > 0 ? 'add' : 'remove']('valued'); + this.setState({ + groupName: value, + failedToEnableChatlink: false + }); + } + }))), this.props.flowType === 2 ? null : JSX_("div", { + className: "group-chat-dialog content" + }, JSX_(_ui_miniui_jsx2__ .A.ToggleCheckbox, { + className: "rotation-toggle", + checked: this.state.keyRotation, + onToggle: keyRotation => this.setState({ + keyRotation + }, () => this.inputRef.current.focus()) + }), JSX_("div", { + className: "group-chat-dialog header" + }, l[20576]), JSX_("div", { + className: "group-chat-dialog description" + }, l[20484]), JSX_(_ui_miniui_jsx2__ .A.ToggleCheckbox, { + className: "open-invite-toggle", + checked: this.state.openInvite, + value: this.state.openInvite, + onToggle: openInvite => this.setState({ + openInvite + }, () => this.inputRef.current.focus()) + }), JSX_("div", { + className: "group-chat-dialog header" + }, l.open_invite_label), JSX_("div", { + className: "group-chat-dialog description" + }, l.open_invite_desc), JSX_("div", { + className: ` + group-chat-dialog checkbox + ${this.state.keyRotation ? 'disabled' : ''} + ${failedToEnableChatlink ? 'failed' : ''} + `, + onClick: () => { + delay('chatWizard-createChatLink', () => { + this.setState(state => ({ + createChatLink: !state.createChatLink + })); + this.inputRef.current.focus(); + }, 100); + } + }, JSX_("div", { + className: `checkdiv ${checkboxClassName}` + }, JSX_("input", { + type: "checkbox", + name: "group-encryption", + id: "group-encryption", + className: "checkboxOn hidden" + })), JSX_("label", { + htmlFor: "group-encryption", + className: "radio-txt lato mid" + }, l[20575]), JSX_("div", { + className: "clear" + }))), failedToEnableChatlink ? JSX_("div", { + className: "group-chat-dialog description chatlinks-intermediate-msg" + }, l[20573]) : null); + } + return JSX_(_ui_modalDialogs_jsx4__ .A.ModalDialog, { + step: self.state.step, + title: this.props.flowType === 2 && self.state.createChatLink ? l[20638] : this.props.customDialogTitle || l[19483], + className: classes, + dialogType: "tool", + dialogName: "group-chat-dialog", + showSelectedNum: self.props.showSelectedNum, + selectedNum: self.state.selected.length, + closeDlgOnClickOverlay: self.props.closeDlgOnClickOverlay, + onClose: () => { + self.props.onClose(self); + }, + popupDidMount: elem => { + if (this.props.extraContent) { + let _elem$querySelector; + (_elem$querySelector = elem.querySelector('.content-block.imported')) == null || _elem$querySelector.appendChild(this.props.extraContent); + } + if (this.props.onExtraContentDidMount) { + this.props.onExtraContentDidMount(elem); + } + }, + triggerResizeOnUpdate: true, + buttons + }, JSX_("div", { + ref: this.domRef, + className: "content-block" + }, chatInfoElements, JSX_(_contacts_jsx3__ .hU, { + step: self.state.step, + exclude: self.props.exclude, + contacts, + selectableContacts: "true", + onSelectDone: self.onSelectClicked, + onSelected: self.onSelected, + selected: self.state.selected, + headerClasses: "left-aligned", + multiple: true, + readOnly: self.state.step !== 0, + allowEmpty: true, + showMeAsSelected: self.state.step === 1, + className: self.props.pickerClassName, + disableFrequents: self.props.disableFrequents, + skipMailSearch: self.props.skipMailSearch, + autoFocusSearchField: self.props.autoFocusSearchField, + selectCleanSearchRes: self.props.selectCleanSearchRes, + disableDoubleClick: self.props.disableDoubleClick, + selectedWidthSize: self.props.selectedWidthSize, + emptySelectionMsg: self.props.emptySelectionMsg, + newEmptySearchResult: self.props.newEmptySearchResult, + newNoContact: self.props.newNoContact, + highlightSearchValue: self.props.highlightSearchValue, + emailTooltips: self.props.emailTooltips + })), extraContent); + } +} +StartGroupChatWizard.clickTime = 0; +StartGroupChatWizard.defaultProps = { + 'selectLabel': l[1940], + 'cancelLabel': l.msg_dlg_cancel, + 'hideable': true, + 'flowType': 1, + 'pickerClassName': '', + 'showSelectedNum': false, + 'disableFrequents': false, + 'skipMailSearch': false, + 'autoFocusSearchField': true, + 'selectCleanSearchRes': true, + 'disableDoubleClick': false, + 'newEmptySearchResult': false, + 'newNoContact': false, + 'closeDlgOnClickOverlay': true, + 'emailTooltips': false +}; + + }, + + 5009 +(_, EXP_, REQ_) { + + REQ_.d(EXP_, { + A: () => __WEBPACK_DEFAULT_EXPORT__ + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _chat_mixins1__ = REQ_(8264); + + +class ToggleCheckbox extends _chat_mixins1__ .w9 { + constructor(props) { + super(props); + this.domRef = react0___default().createRef(); + this.onToggle = () => { + const newState = !this.state.value; + this.setState({ + value: newState + }); + if (this.props.onToggle) { + this.props.onToggle(newState); + } + }; + this.state = { + value: this.props.value + }; + } + render() { + return JSX_("div", { + ref: this.domRef, + className: ` + mega-switch + ${this.props.className} + ${this.state.value ? 'toggle-on' : ''} + `, + role: "switch", + "aria-checked": !!this.state.value, + onClick: this.onToggle + }, JSX_("div", { + className: `mega-feature-switch sprite-fm-mono-after + ${this.state.value ? 'icon-check-after' : 'icon-minimise-after'}` + })); + } +} + const __WEBPACK_DEFAULT_EXPORT__ = { + ToggleCheckbox +}; + + } + +}]); \ No newline at end of file diff --git a/js/chat/bundle.waiting-room.js b/js/chat/bundle.waiting-room.js new file mode 100644 index 0000000000..d3b5cc4759 --- /dev/null +++ b/js/chat/bundle.waiting-room.js @@ -0,0 +1,1150 @@ +/** @file automatically generated, do not edit it. */ +"use strict"; +(self.webpackChunk_meganz_webclient = self.webpackChunk_meganz_webclient || []).push([[752],{ + + 192 +(_, EXP_, REQ_) { + + REQ_.d(EXP_, { + $: () => withPermissionsObserver + }); + const _babel_runtime_helpers_extends0__ = REQ_(8168); + const react1__ = REQ_(1594); + const react1___default = REQ_.n(react1__); + const _mixins_js2__ = REQ_(8264); + const _ui_modalDialogs_jsx3__ = REQ_(8120); + const _ui_utils_jsx4__ = REQ_(6411); + + + + + +const errors = { + browser: 'NotAllowedError: Permission denied', + system: 'NotAllowedError: Permission denied by system', + dismissed: 'NotAllowedError: Permission dismissed', + nil: 'NotFoundError: Requested device not found', + sharedCam: 'NotReadableError: Could not start video source', + sharedMic: 'NotReadableError: Could not start audio source', + sharedGeneric: 'NotReadableError: Device in use' +}; +const isUserActionError = error => { + return error && error === errors.browser; +}; +const withPermissionsObserver = Component => { + return class extends _mixins_js2__ .w9 { + constructor(props) { + super(props); + this.namespace = `PO-${Component.NAMESPACE}`; + this.observer = `onLocalMediaError.${this.namespace}`; + this.childRef = undefined; + this.platform = ua.details.os; + this.helpURL = `${l.mega_help_host}/chats-meetings/meetings/enable-audio-video-call-permissions`; + this.macURI = 'x-apple.systempreferences:com.apple.preference.security'; + this.winURI = 'ms-settings'; + this.CONTENT = { + [Av.Audio]: { + system: { + title: l.no_mic_title, + info: this.platform === 'Windows' ? l.no_mic_system_windows.replace('[A]', ``).replace('[/A]', '') : l.no_mic_system_mac.replace('[A]', ``).replace('[/A]', ''), + buttons: [this.platform === 'Apple' || this.platform === 'Windows' ? { + key: 'open-settings', + label: l.open_system_settings, + className: 'positive', + onClick: () => { + window.open(this.platform === 'Apple' ? `${this.macURI}?Privacy_Microphone` : `${this.winURI}:privacy-microphone`, '_blank', 'noopener,noreferrer'); + this.closePermissionsDialog(Av.Audio); + } + } : { + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Audio) + }] + }, + browser: { + title: l.no_mic_title, + cover: 'permissions-mic', + info: l.allow_mic_access.replace('[X]', ''), + buttons: [{ + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Audio) + }] + }, + nil: { + title: l.no_mic_detected_title, + info: l.no_mic_detected_info, + buttons: [{ + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Audio) + }] + }, + shared: { + title: l.no_mic_title, + info: l.shared_mic_err_info.replace('[A]', ``).replace('[/A]', ''), + buttons: [{ + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Audio) + }] + } + }, + [Av.Camera]: { + system: { + title: l.no_camera_title, + info: this.platform === 'Windows' ? l.no_camera_system_windows.replace('[A]', ``).replace('[/A]', '') : l.no_camera_system_mac.replace('[A]', ``).replace('[/A]', ''), + buttons: [this.platform === 'Apple' || this.platform === 'Windows' ? { + key: 'open-settings', + label: l.open_system_settings, + className: 'positive', + onClick: () => { + window.open(this.platform === 'Apple' ? `${this.macURI}?Privacy_Camera` : `${this.winURI}:privacy-webcam`, '_blank', 'noopener,noreferrer'); + this.closePermissionsDialog(Av.Camera); + } + } : { + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Camera) + }] + }, + browser: { + title: l.no_camera_title, + cover: 'permissions-camera', + info: l.allow_camera_access.replace('[X]', ''), + buttons: [{ + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Camera) + }] + }, + nil: { + title: l.no_camera_detected_title, + info: l.no_camera_detected_info, + buttons: [{ + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Camera) + }] + }, + shared: { + title: l.no_camera_title, + info: l.shared_cam_err_info.replace('[A]', ``).replace('[/A]', ''), + buttons: [{ + key: 'ok', + label: l.ok_button, + className: 'positive', + onClick: () => this.closePermissionsDialog(Av.Camera) + }] + } + }, + [Av.Screen]: { + title: l.no_screen_title, + info: l.no_screen_system.replace('[A]', ``).replace('[/A]', ''), + buttons: [{ + key: 'open-settings', + label: l.open_system_settings, + className: 'positive', + onClick: () => { + window.open(`${this.macURI}?Privacy_ScreenCapture`, '_blank', 'noopener,noreferrer'); + this.closePermissionsDialog(Av.Screen); + } + }] + } + }; + this.state = { + errMic: '', + errCamera: '', + errScreen: '', + [`dialog-${Av.Audio}`]: null, + [`dialog-${Av.Camera}`]: null, + [`dialog-${Av.Screen}`]: null + }; + this.getPermissionsDialogContent = () => { + const { + CONTENT, + state + } = this; + const { + errMic, + errCamera + } = state; + const { + browser, + system, + nil, + sharedCam, + sharedMic, + sharedGeneric + } = errors; + return { + [Av.Audio]: { + ...errMic === browser && CONTENT[Av.Audio].browser, + ...errMic === system && CONTENT[Av.Audio].system, + ...errMic === nil && CONTENT[Av.Audio].nil, + ...errMic === sharedMic && CONTENT[Av.Audio].shared, + ...errMic === sharedGeneric && CONTENT[Av.Audio].shared + }, + [Av.Camera]: { + ...errCamera === browser && CONTENT[Av.Camera].browser, + ...errCamera === system && CONTENT[Av.Camera].system, + ...errCamera === nil && CONTENT[Av.Camera].nil, + ...errCamera === sharedCam && CONTENT[Av.Camera].shared, + ...errCamera === sharedGeneric && CONTENT[Av.Camera].shared + }, + [Av.Screen]: CONTENT[Av.Screen] + }; + }; + this.resetError = av => { + this.setState({ + errMic: av === Av.Audio ? '' : this.state.errMic, + errCamera: av === Av.Camera ? '' : this.state.errCamera, + errScreen: av === Av.Screen ? '' : this.state.errScreen + }); + }; + this.hasToRenderPermissionsWarning = this.hasToRenderPermissionsWarning.bind(this); + this.renderPermissionsWarning = this.renderPermissionsWarning.bind(this); + } + hasToRenderPermissionsWarning(av) { + const CONFIG = { + [Av.Audio]: { + showOnUserActionError: true, + err: this.state.errMic + }, + [Av.Camera]: { + showOnUserActionError: true, + err: this.state.errCamera + }, + [Av.Screen]: { + showOnUserActionError: false, + err: this.state.errScreen + } + }; + const current = CONFIG[av]; + if (current) { + return isUserActionError(current.err) ? current.showOnUserActionError : current.err; + } + return false; + } + closePermissionsDialog(av) { + this.setState({ + [`dialog-${av}`]: false + }, () => { + let _this$childRef; + return (_this$childRef = this.childRef) == null ? void 0 : _this$childRef.safeForceUpdate(); + }); + } + renderPermissionsDialog(av, child) { + const content = this.getPermissionsDialogContent(); + const { + title, + info, + buttons, + cover + } = content[av] || {}; + return JSX_(_ui_modalDialogs_jsx3__ .A.ModalDialog, { + dialogName: `${this.namespace}-permissions-${av}`, + className: ` + meetings-permissions-dialog + dialog-template-message + with-close-btn + warning + `, + buttons, + hideOverlay: Component.NAMESPACE === 'preview-meeting' && !document.body.classList.contains('not-logged'), + onClose: () => { + this.setState({ + [`dialog-${av}`]: false + }, () => child && child.safeForceUpdate()); + } + }, JSX_("header", null, cover ? null : JSX_("div", { + className: "graphic" + }, JSX_("i", { + className: "warning sprite-fm-uni icon-warning" + })), JSX_("div", { + className: "info-container" + }, JSX_("h3", { + id: "msgDialog-title" + }, title || l[47]), cover && JSX_("div", { + className: "permissions-warning-cover" + }, JSX_("span", { + className: cover + })), JSX_(_ui_utils_jsx4__ .P9, { + tag: "p", + className: "permissions-warning-info", + content: info + })))); + } + renderPermissionsWarning(av, child) { + const { + errMic, + errCamera + } = this.state; + const dismissed = errMic === errors.dismissed || errCamera === errors.dismissed; + return JSX_("div", { + className: ` + ${this.namespace} + meetings-signal-issue + simpletip + ${dismissed ? 'with-small-area' : ''} + `, + "data-simpletip": l.show_info, + "data-simpletipposition": "top", + "data-simpletipoffset": "5", + "data-simpletip-class": "theme-dark-forced", + onClick: () => dismissed ? null : this.setState({ + [`dialog-${av}`]: true + }, () => { + if (child) { + this.childRef = child; + } + }) + }, JSX_("span", { + className: "signal-issue-background" + }), JSX_("i", { + className: "sprite-fm-mono icon-exclamation-filled" + }), this.state[`dialog-${av}`] && this.renderPermissionsDialog(av, child)); + } + componentWillUnmount() { + super.componentWillUnmount(); + megaChat.unbind(this.observer); + } + componentDidMount() { + super.componentDidMount(); + megaChat.rebind(this.observer, (ev, errAv) => { + this.setState({ + errMic: errAv && errAv.mic ? String(errAv.mic) : this.state.errMic, + errCamera: errAv && errAv.camera ? String(errAv.camera) : this.state.errCamera, + errScreen: errAv && errAv.screen ? String(errAv.screen) : this.state.errScreen + }); + }); + megaChat.rebind(`onLocalMediaQueryError.${this.namespace}`, (ev, { + type, + err + }) => { + if (type === 'screen' && String(err) === errors.system) { + this.setState({ + [`dialog-${Av.Screen}`]: true + }, () => this.safeForceUpdate()); + } + }); + } + render() { + return JSX_(Component, (0,_babel_runtime_helpers_extends0__ .A)({}, this.props, this.state, { + errMic: this.state.errMic, + errCamera: this.state.errCamera, + errScreen: this.state.errScreen, + hasToRenderPermissionsWarning: this.hasToRenderPermissionsWarning, + resetError: this.resetError, + renderPermissionsWarning: this.renderPermissionsWarning + })); + } + }; +}; + + }, + + 3056 +(_, EXP_, REQ_) { + +REQ_.r(EXP_); + REQ_.d(EXP_, { + "default": () => Admit + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _mixins_js1__ = REQ_(8264); + const _ui_utils_jsx2__ = REQ_(6411); + const _contacts_jsx3__ = REQ_(8022); + const _button_jsx4__ = REQ_(6740); + const _ui_perfectScrollbar_jsx5__ = REQ_(1301); + const _link6__ = REQ_(4649); + + + + + + + +const NAMESPACE = 'admit'; +class Admit extends _mixins_js1__ .w9 { + constructor(...args) { + super(...args); + this.domRef = react0___default().createRef(); + this.peersWaitingRef = react0___default().createRef(); + this.state = { + expanded: false + }; + this.doAdmit = peers => { + let _this$props$call; + return (_this$props$call = this.props.call) == null || (_this$props$call = _this$props$call.sfuClient) == null ? void 0 : _this$props$call.wrAllowJoin([peers]); + }; + this.doDeny = peers => { + let _this$props$call2; + return (_this$props$call2 = this.props.call) == null || (_this$props$call2 = _this$props$call2.sfuClient) == null ? void 0 : _this$props$call2.wrKickOut([peers]); + }; + this.Icon = ({ + icon, + label, + onClick + }) => JSX_("i", { + className: ` + sprite-fm-mono + simpletip + ${icon} + `, + "data-simpletip": label, + "data-simpletipposition": "top", + "data-simpletipoffset": "5", + "data-simpletip-class": "theme-dark-forced", + onClick + }); + this.CallLimitBanner = ({ + call + }) => JSX_("div", { + className: `${NAMESPACE}-user-limit-banner` + }, call.organiser === u_handle ? (0,_ui_utils_jsx2__ .lI)(l.admit_limit_banner_organiser, '[A]', _link6__ .A, { + onClick() { + window.open(`${getBaseUrl()}/pro`, '_blank', 'noopener,noreferrer'); + eventlog(500259); + } + }) : l.admit_limit_banner_host); + this.renderPeersList = () => { + const { + peers, + call, + chatRoom + } = this.props; + const disableAdding = call.sfuClient.callLimits && call.sfuClient.callLimits.usr && chatRoom.getCallParticipants().length >= call.sfuClient.callLimits.usr; + return JSX_(_ui_perfectScrollbar_jsx5__ .O, { + ref: this.peersWaitingRef, + options: { + 'suppressScrollX': true + } + }, JSX_("div", { + className: "peers-waiting" + }, this.isUserLimited && JSX_(this.CallLimitBanner, { + call + }), peers.map(handle => { + return JSX_("div", { + key: handle, + className: "peers-waiting-card" + }, JSX_("div", { + className: "peer-avatar" + }, JSX_(_contacts_jsx3__ .eu, { + contact: M.u[handle] + })), JSX_("div", { + className: "peer-name" + }, JSX_(_contacts_jsx3__ .uA, { + contact: M.u[handle], + emoji: true + })), JSX_("div", { + className: "peer-controls" + }, JSX_(this.Icon, { + icon: "icon-close-component", + label: l.wr_deny, + onClick: () => this.doDeny(handle) + }), JSX_(this.Icon, { + icon: `icon-check ${disableAdding ? 'disabled' : ''}`, + label: l.wr_admit, + onClick: () => !disableAdding && this.doAdmit(handle) + }))); + }))); + }; + this.renderMultiplePeersWaiting = () => { + const { + call, + peers, + expanded, + onWrListToggle + } = this.props; + if (peers && peers.length) { + const disableAddAll = this.isUserLimited; + return JSX_(react0___default().Fragment, null, JSX_("div", { + className: `${NAMESPACE}-head` + }, JSX_("h3", null, mega.icu.format(l.wr_peers_waiting, peers.length)), expanded ? JSX_(this.Icon, { + icon: "icon-arrow-up", + onClick: () => onWrListToggle(false) + }) : null), !expanded && disableAddAll && JSX_(this.CallLimitBanner, { + call + }), expanded && JSX_("div", { + className: `${NAMESPACE}-content` + }, this.renderPeersList()), JSX_("div", { + className: `${NAMESPACE}-controls` + }, expanded ? null : JSX_(_button_jsx4__ .A, { + className: "mega-button theme-dark-forced", + onClick: () => onWrListToggle(true) + }, JSX_("span", null, l.wr_see_waiting)), JSX_(_button_jsx4__ .A, { + peers, + className: `mega-button positive theme-dark-forced ${disableAddAll ? 'disabled' : ''}`, + onClick: () => !disableAddAll && call.sfuClient.wrAllowJoin(peers) + }, JSX_("span", null, l.wr_admit_all)))); + } + return null; + }; + this.renderSinglePeerWaiting = () => { + const { + peers, + call + } = this.props; + const peer = peers[0]; + const disableAdding = this.isUserLimited; + if (peer) { + return JSX_(react0___default().Fragment, null, JSX_(_ui_utils_jsx2__ .P9, { + tag: "h3", + content: l.wr_peer_waiting.replace('%s', megaChat.html(M.getNameByHandle(peer))) + }), disableAdding && JSX_(this.CallLimitBanner, { + call + }), JSX_("div", { + className: `${NAMESPACE}-controls` + }, JSX_(_button_jsx4__ .A, { + className: "mega-button theme-dark-forced", + onClick: () => this.doDeny(peer) + }, JSX_("span", null, l.wr_deny)), JSX_(_button_jsx4__ .A, { + className: `mega-button positive theme-dark-forced ${disableAdding ? 'disabled' : ''}`, + onClick: () => !disableAdding && this.doAdmit(peer) + }, JSX_("span", null, l.wr_admit)))); + } + return null; + }; + } + get isUserLimited() { + const { + call, + chatRoom, + peers + } = this.props; + return call.sfuClient.callLimits && call.sfuClient.callLimits.usr && chatRoom.getCallParticipants().length + (peers ? peers.length : 0) > call.sfuClient.callLimits.usr; + } + render() { + const { + chatRoom, + peers + } = this.props; + if (chatRoom.iAmOperator()) { + return JSX_("div", { + ref: this.domRef, + className: ` + ${NAMESPACE} + theme-dark-forced + ` + }, JSX_("div", { + className: `${NAMESPACE}-wrapper` + }, peers && peers.length > 1 ? this.renderMultiplePeersWaiting() : this.renderSinglePeerWaiting())); + } + return null; + } +} + + }, + + 2659 +(_, EXP_, REQ_) { + +REQ_.r(EXP_); + REQ_.d(EXP_, { + VIEW: () => VIEW, + "default": () => WaitingRoom + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _mixins_js1__ = REQ_(8264); + const _ui_utils_jsx2__ = REQ_(6411); + const _workflow_preview_jsx3__ = REQ_(3546); + const _button_jsx4__ = REQ_(6740); + const _link_jsx5__ = REQ_(4649); + + + + + + +const NAMESPACE = 'waiting-room'; +const VIEW = { + INTRO: 0, + ACCOUNT: 1, + GUEST: 2, + AWAIT: 3, + UNSUPPORTED: 4, + REDIRECT: 5 +}; +class WaitingRoom extends _mixins_js1__ .w9 { + constructor(props) { + super(props); + this.domRef = react0___default().createRef(); + this.redirectInterval = undefined; + this.state = { + view: VIEW.ACCOUNT, + call: false, + audio: false, + video: false, + firstName: '', + lastName: '', + countdown: 4, + loading: false + }; + this.renderLeaveDialog = () => msgDialog(`confirmation:!^${l.wr_leave}!${l.wr_do_not_leave}`, null, l.wr_leave_confirmation, '', cb => { + if (cb) { + delay('chat-event-wr-leave', () => eventlog(99938)); + this.doLeave(); + } + }, 1); + this.renderDeniedDialog = () => msgDialog('error', '', l.wr_denied, l.wr_denied_details, this.doLeave); + this.renderTimeoutDialog = () => msgDialog('error', '', l.wr_timeout, l.wr_timeout_details, this.doLeave); + this.renderWaitingRoomInfo = () => { + const { + chatRoom + } = this.props; + const { + nextOccurrenceStart, + nextOccurrenceEnd + } = chatRoom.scheduledMeeting || {}; + return JSX_(react0___default().Fragment, null, JSX_(_ui_utils_jsx2__ .P9, { + tag: "h2", + content: megaChat.html(chatRoom.topic) + }), JSX_("div", { + className: `${NAMESPACE}-schedule` + }, JSX_("span", null, time2date(nextOccurrenceStart / 1000, 20)), JSX_("span", null, toLocaleTime(nextOccurrenceStart), " - ", toLocaleTime(nextOccurrenceEnd)))); + }; + this.doLeave = () => this.setState({ + view: VIEW.REDIRECT + }, () => { + tSleep(this.state.countdown).then(() => this.props.onWaitingRoomLeave()); + this.redirectInterval = setInterval(() => this.setState(({ + countdown + }) => ({ + countdown: countdown > 0 ? countdown - 1 : 0 + })), 1e3); + sessionStorage.removeItem('previewMedia'); + }); + this.setInitialView = () => { + if (u_type || is_eplusplus) { + let _this$props$chatRoom; + return (_this$props$chatRoom = this.props.chatRoom) != null && _this$props$chatRoom.iAmInRoom() ? VIEW.AWAIT : VIEW.ACCOUNT; + } + return VIEW.INTRO; + }; + this.requestJoin = () => { + let _this$props$chatRoom2; + const { + audio, + video + } = this.state; + (_this$props$chatRoom2 = this.props.chatRoom) == null || _this$props$chatRoom2.joinCall(audio, video); + }; + this.Field = ({ + name, + children + }) => { + let _this$state$name; + return JSX_("div", { + className: ` + mega-input + title-ontop + ${(_this$state$name = this.state[name]) != null && _this$state$name.length ? 'valued' : ''} + ` + }, JSX_("div", { + className: "mega-input-title" + }, children, JSX_("span", { + className: "required-red" + }, "*")), JSX_("input", { + type: "text", + name, + className: "titleTop required megaInputs", + placeholder: children, + value: this.state[name] || '', + maxLength: 40, + onChange: ev => this.setState({ + [name]: ev.target.value + }) + })); + }; + this.Card = ({ + className, + children + }) => { + const { + audio, + video + } = this.state; + return JSX_("div", { + className: ` + card + ${className || ''} + ` + }, JSX_("div", { + className: "card-body" + }, children), JSX_("div", { + className: "card-preview" + }, JSX_(_workflow_preview_jsx3__ .A, { + audio, + video, + onToggle: (audio, video) => { + this.setState({ + audio, + video + }, () => { + sessionStorage.previewMedia = JSON.stringify({ + audio, + video + }); + }); + } + }))); + }; + this.Head = ({ + title + }) => { + let _this$props$chatRoom3; + return JSX_("div", { + className: `${NAMESPACE}-head` + }, JSX_("div", { + className: `${NAMESPACE}-logo` + }, JSX_("i", { + className: ` + sprite-fm-illustration-wide + ${mega.ui.isDarkTheme() ? 'mega-logo-dark' : 'img-mega-logo-light'} + ` + })), JSX_("h1", { + className: (megaChat.initialChatId || is_chatlink) && this.state.view !== VIEW.INTRO ? 'hidden' : '' + }, JSX_(_ui_utils_jsx2__ .zT, null, title || l.you_have_invitation.replace('%1', (_this$props$chatRoom3 = this.props.chatRoom) == null ? void 0 : _this$props$chatRoom3.topic)))); + }; + this.Await = () => { + return JSX_(react0___default().Fragment, null, megaChat.initialChatId ? JSX_(this.Head, null) : null, JSX_(this.Card, { + className: megaChat.initialChatId ? '' : 'fit-spacing' + }, this.renderWaitingRoomInfo(), JSX_("div", { + className: `${NAMESPACE}-message` + }, this.state.call ? l.wr_wait_to_admit : l.wr_wait_to_start), JSX_(_button_jsx4__ .A, { + icon: "sprite-fm-mono icon-log-out-thin-solid", + className: `${NAMESPACE}-leave`, + onClick: () => this.renderLeaveDialog() + }, l.wr_leave))); + }; + this.Account = () => { + const { + loading, + audio, + video + } = this.state; + return JSX_(react0___default().Fragment, null, JSX_(this.Head, null), JSX_(this.Card, null, this.renderWaitingRoomInfo(), JSX_(_button_jsx4__ .A, { + className: ` + mega-button + positive + large + ${loading ? 'disabled' : ''} + `, + onClick: () => { + return loading ? null : this.setState({ + loading: true + }, () => { + const { + chatRoom + } = this.props; + const { + chatId, + publicChatHandle, + publicChatKey + } = chatRoom; + if (chatRoom.iAmInRoom()) { + return megaChat.routing.reinitAndOpenExistingChat(chatId, publicChatHandle).then(() => { + megaChat.getChatById(chatId).joinCall(audio, video); + }).catch(ex => console.error(`Failed to open existing room and join call: ${ex}`)); + } + megaChat.routing.reinitAndJoinPublicChat(chatId, publicChatHandle, publicChatKey).then(() => { + delete megaChat.initialPubChatHandle; + }).catch(ex => console.error(`Failed to join room: ${ex}`)); + }); + } + }, l.wr_ask_to_join), JSX_("div", null, JSX_(_link_jsx5__ .A, { + to: "https://mega.io/chatandmeetings", + target: "_blank" + }, l.how_meetings_work)))); + }; + this.Redirect = () => JSX_(react0___default().Fragment, null, JSX_(this.Head, { + title: l.wr_left_heading + }), JSX_("h5", null, l.wr_left_countdown.replace('%1', this.state.countdown))); + this.Guest = () => { + const { + chatRoom + } = this.props; + const { + loading, + firstName, + lastName + } = this.state; + const isDisabled = !firstName.length || !lastName.length; + return JSX_(react0___default().Fragment, null, JSX_(this.Head, null), JSX_(this.Card, null, this.renderWaitingRoomInfo(), JSX_("div", { + className: "card-fields" + }, JSX_(this.Field, { + name: "firstName" + }, l[1096]), JSX_(this.Field, { + name: "lastName" + }, l[1097])), JSX_(_button_jsx4__ .A, { + className: ` + mega-button + positive + large + ${isDisabled || loading ? 'disabled' : ''} + `, + onClick: () => { + if (isDisabled || loading) { + return false; + } + return this.setState({ + loading: true + }, () => { + u_eplusplus(this.state.firstName, this.state.lastName).then(() => { + return megaChat.routing.reinitAndJoinPublicChat(chatRoom.chatId, chatRoom.publicChatHandle, chatRoom.publicChatKey); + }).catch(ex => d && console.error(`E++ account failure: ${ex}`)); + }); + } + }, l.wr_ask_to_join), JSX_("div", null, JSX_(_link_jsx5__ .A, { + to: "https://mega.io/chatandmeetings", + target: "_blank" + }, l.how_meetings_work)))); + }; + this.Intro = () => { + const { + chatRoom + } = this.props; + return JSX_(react0___default().Fragment, null, JSX_(this.Head, null), JSX_("div", { + className: "join-meeting-content" + }, JSX_(_button_jsx4__ .A, { + className: "mega-button positive", + onClick: () => { + megaChat.loginOrRegisterBeforeJoining(chatRoom.publicChatHandle, false, true, undefined, () => this.setState({ + view: VIEW.ACCOUNT + })); + } + }, l[171]), JSX_(_button_jsx4__ .A, { + className: "mega-button", + onClick: () => this.setState({ + view: VIEW.GUEST + }) + }, l.join_as_guest), JSX_("p", null, JSX_(_ui_utils_jsx2__ .P9, { + onClick: e => { + e.preventDefault(); + megaChat.loginOrRegisterBeforeJoining(chatRoom.publicChatHandle, true, undefined, undefined, () => this.setState({ + view: VIEW.ACCOUNT + })); + } + }, l[20635])))); + }; + this.Unsupported = () => { + let _this$props$chatRoom4; + return JSX_(react0___default().Fragment, null, JSX_(this.Head, null), JSX_("h1", null, l.you_have_invitation.replace('%1', (_this$props$chatRoom4 = this.props.chatRoom) == null ? void 0 : _this$props$chatRoom4.topic)), JSX_("div", { + className: "meetings-unsupported-container" + }, JSX_("i", { + className: "sprite-fm-uni icon-error" + }), JSX_("div", { + className: "unsupported-info" + }, JSX_("h3", null, l.heading_unsupported_browser), JSX_("h3", null, l.join_meeting_methods), JSX_("ul", null, JSX_("li", null, l.join_via_link), JSX_("li", null, JSX_(_ui_utils_jsx2__ .P9, null, l.join_via_mobile.replace('[A]', '').replace('[/A]', ''))))))); + }; + this.renderView = view => { + switch (view) { + default: + return this.Await(); + case VIEW.INTRO: + return this.Intro(); + case VIEW.GUEST: + return this.Guest(); + case VIEW.ACCOUNT: + return this.Account(); + case VIEW.REDIRECT: + return this.Redirect(); + case VIEW.UNSUPPORTED: + return this.Unsupported(); + } + }; + this.state.call = this.props.havePendingCall; + this.state.view = megaChat.hasSupportForCalls ? this.setInitialView() : VIEW.UNSUPPORTED; + if (sessionStorage.previewMedia) { + const { + audio, + video + } = JSON.parse(sessionStorage.previewMedia); + this.state.audio = audio; + this.state.video = video; + sessionStorage.removeItem('previewMedia'); + } + } + UNSAFE_componentWillReceiveProps(nextProps) { + if (this.props.havePendingCall !== nextProps.havePendingCall) { + this.setState({ + call: nextProps.havePendingCall + }, () => this.state.view === VIEW.AWAIT && nextProps.havePendingCall && this.requestJoin()); + } + } + componentWillUnmount() { + super.componentWillUnmount(); + clearInterval(this.redirectInterval); + this.props.chatRoom.unbind(`onCallLeft.${NAMESPACE}`); + this.props.chatRoom.unbind(`onModeratorAdd.${NAMESPACE}`); + } + componentDidMount() { + super.componentDidMount(); + const { + chatRoom + } = this.props; + const { + call, + view + } = this.state; + if (call && view === VIEW.AWAIT) { + this.requestJoin(); + } + chatRoom.rebind(`onCallLeft.${NAMESPACE}`, (ev, { + termCode + }) => { + if (termCode === SfuClient.TermCode.kKickedFromWaitingRoom) { + return this.renderDeniedDialog(); + } + if (termCode === SfuClient.TermCode.kWaitingRoomAllowTimeout) { + delay('chat-event-wr-timeout', () => eventlog(99939)); + return this.renderTimeoutDialog(); + } + }); + chatRoom.rebind(`onModeratorAdd.${NAMESPACE}`, (ev, user) => { + if (user === u_handle) { + chatRoom.meetingsLoading = false; + this.requestJoin(); + } + }); + } + render() { + const { + view + } = this.state; + return JSX_(_ui_utils_jsx2__ .Ay.RenderTo, { + element: document.body + }, JSX_("div", { + ref: this.domRef, + className: ` + ${NAMESPACE} + join-meeting + ${view === VIEW.AWAIT ? `${NAMESPACE}--await` : ''} + ${view === VIEW.AWAIT && !megaChat.initialChatId ? 'theme-dark-forced' : ''} + ${view === VIEW.REDIRECT ? `${NAMESPACE}--redirect` : ''} + ${megaChat.initialChatId || is_chatlink ? `${NAMESPACE}--chatlink-landing` : ''} + ` + }, this.renderView(view))); + } +} + + }, + + 3546 +(_, EXP_, REQ_) { + + REQ_.d(EXP_, { + A: () => __WEBPACK_DEFAULT_EXPORT__ + }); + const react0__ = REQ_(1594); + const react0___default = REQ_.n(react0__); + const _mixins_js1__ = REQ_(8264); + const _contacts_jsx2__ = REQ_(8022); + const _utils_jsx3__ = REQ_(3901); + const _button_jsx4__ = REQ_(6740); + const _permissionsObserver_jsx5__ = REQ_(192); + + + + + + +class Preview extends react0___default().Component { + constructor(props) { + super(props); + this.domRef = react0___default().createRef(); + this.videoRef = react0___default().createRef(); + this.stream = null; + this.state = { + audio: false, + video: false, + avatarMeta: undefined + }; + this.getTrackType = type => !type ? 'getTracks' : type === Preview.STREAMS.AUDIO ? 'getAudioTracks' : 'getVideoTracks'; + this.startStream = type => { + this.stopStream(); + const { + audio, + video + } = this.state; + navigator.mediaDevices.getUserMedia({ + audio, + video + }).then(stream => { + const videoRef = this.videoRef.current; + if (videoRef) { + videoRef.srcObject = stream; + this.stream = stream; + if (this.props.onToggle) { + this.props.onToggle(this.state.audio, this.state.video); + } + } + }).catch(ex => { + const stream = type === Preview.STREAMS.AUDIO ? 'audio' : 'video'; + return this.domRef.current && this.setState(state => ({ + [stream]: !state[stream] + }), () => { + megaChat.trigger('onLocalMediaError', { + [type === Preview.STREAMS.AUDIO ? 'mic' : 'camera']: `${ex.name}: ${ex.message}` + }); + console.error(`${ex.name}: ${ex.message}`); + }); + }); + }; + this.stopStream = type => { + if (this.stream) { + const trackType = this.getTrackType(type); + const tracks = this.stream[trackType](); + for (const track of tracks) { + track.stop(); + } + } + }; + this.toggleStream = type => { + let _this$props$resetErro, _this$props; + const stream = type === Preview.STREAMS.AUDIO ? 'audio' : 'video'; + this.setState(state => ({ + [stream]: !state[stream] + }), () => { + if (this.props.onToggle) { + this.props.onToggle(this.state.audio, this.state.video); + } + return this.state[stream] ? this.startStream(type) : this.stopStream(type); + }); + (_this$props$resetErro = (_this$props = this.props).resetError) == null || _this$props$resetErro.call(_this$props, type === Preview.STREAMS.AUDIO ? Av.Audio : Av.Camera); + }; + this.renderAvatar = () => { + if ((0,_utils_jsx3__ .P)()) { + return JSX_("div", { + className: "avatar-guest" + }, JSX_("i", { + className: "sprite-fm-uni icon-owner" + })); + } + if (is_chatlink) { + const { + avatarUrl, + color, + shortName + } = this.state.avatarMeta || {}; + return JSX_("div", { + className: ` + avatar-wrapper + ${color ? `color${color}` : ''} + ` + }, avatarUrl && JSX_("img", { + src: avatarUrl, + alt: "" + }), color && JSX_("span", null, shortName)); + } + return JSX_(_contacts_jsx2__ .eu, { + contact: M.u[u_handle] + }); + }; + this.state.audio = this.props.audio || this.state.audio; + if (this.props.video) { + this.state.video = this.props.video; + this.startStream(Preview.STREAMS.VIDEO); + this.props.onToggle(this.state.audio, this.state.video); + } + } + componentWillUnmount() { + this.stopStream(); + } + componentDidMount() { + if (this.props.onToggle) { + this.props.onToggle(this.state.audio, this.state.video); + } + this.setState({ + avatarMeta: is_chatlink ? generateAvatarMeta(u_handle) : undefined + }); + } + render() { + const { + NAMESPACE + } = Preview; + const { + hasToRenderPermissionsWarning, + renderPermissionsWarning + } = this.props; + const { + audio, + video + } = this.state; + const SIMPLETIP_PROPS = { + label: undefined, + position: 'top', + className: 'theme-dark-forced' + }; + return JSX_("div", { + ref: this.domRef, + className: ` + ${NAMESPACE} + local-stream-mirrored + ` + }, video && JSX_("div", { + className: `${NAMESPACE}-video-overlay` + }), JSX_("video", { + className: video ? 'streaming' : '', + muted: true, + autoPlay: true, + ref: this.videoRef + }), !video && this.renderAvatar(), JSX_("div", { + className: `${NAMESPACE}-controls` + }, JSX_("div", { + className: "preview-control-wrapper" + }, JSX_(_button_jsx4__ .A, { + simpletip: { + ...SIMPLETIP_PROPS, + label: audio ? l[16214] : l[16708] + }, + className: ` + mega-button + round + theme-light-forced + ${NAMESPACE}-control + ${audio ? '' : 'with-fill'} + `, + icon: audio ? 'icon-mic-thin-outline' : 'icon-mic-off-thin-outline', + onClick: () => { + this.toggleStream(Preview.STREAMS.AUDIO); + } + }), JSX_("span", null, l.mic_button), hasToRenderPermissionsWarning(Av.Audio) ? renderPermissionsWarning(Av.Audio) : null), JSX_("div", { + className: "preview-control-wrapper" + }, JSX_(_button_jsx4__ .A, { + simpletip: { + ...SIMPLETIP_PROPS, + label: video ? l[22894] : l[22893] + }, + className: ` + mega-button + round + theme-light-forced + ${NAMESPACE}-control + ${video ? '' : 'with-fill'} + `, + icon: video ? 'icon-video-thin-outline' : 'icon-video-off-thin-outline', + onClick: () => this.toggleStream(Preview.STREAMS.VIDEO) + }), JSX_("span", null, l.camera_button), hasToRenderPermissionsWarning(Av.Camera) ? renderPermissionsWarning(Av.Camera) : null))); + } +} +Preview.NAMESPACE = 'preview-meeting'; +Preview.STREAMS = { + AUDIO: 1, + VIDEO: 2 +}; + const __WEBPACK_DEFAULT_EXPORT__ = (0,_mixins_js1__ .Zz)(_permissionsObserver_jsx5__ .$)(Preview); + + } + +}]); \ No newline at end of file diff --git a/js/chat/chat.jsx b/js/chat/chat.jsx index 6a0df21a98..42adcfece4 100644 --- a/js/chat/chat.jsx +++ b/js/chat/chat.jsx @@ -1,19 +1,42 @@ -import React from 'react'; +import React, { lazy } from 'react'; import { createRoot } from 'react-dom'; -import ConversationsUI from './ui/conversations.jsx'; -require("./chatGlobalEventManager.jsx"); -// load chatRoom.jsx, so that its included in bundle.js, despite that ChatRoom is legacy ES ""class"" -require("./chatRoom.jsx"); +import './chatRoom.jsx'; +import ConversationsApp from './ui/conversations.jsx'; -// ensure that the Incoming dialog is included, so that it would be added to the window scope for accessing from -// vanilla JS -require("./ui/meetings/workflow/incoming.jsx"); - -import ChatRouting from "./chatRouting.jsx"; +import ChatRouting from './chatRouting.jsx'; import MeetingsManager from './meetingsManager.jsx'; -import { ChatOnboarding } from "./chatOnboarding.jsx"; -import { inProgressAlert } from "./ui/meetings/call.jsx"; +import { ChatOnboarding } from './chatOnboarding.jsx'; +import { inProgressAlert } from './ui/meetings/utils.jsx'; + +import { chatGlobalEventManager } from './chatGlobalEventManager'; +window.chatGlobalEventManager = chatGlobalEventManager; + +import { getMessageString } from './ui/messages/utils.jsx'; +mega.ui = mega.ui || {}; +mega.ui.chat = mega.ui.chat || {}; +mega.ui.chat.getMessageString = getMessageString; + +import { withSuspense } from './utils.jsx'; +import Incoming from './ui/meetings/workflow/incoming.jsx'; + +const ScheduleMeeting = + lazy(() => import(/* webpackChunkName: "schedule-meeting" */ './ui/meetings/schedule/schedule.jsx')); +const StartMeeting = + lazy(() => import(/* webpackChunkName: "start-conversation" */ './ui/meetings/workflow/start.jsx')); +const ContactSelectorDialog = + lazy(() => import(/* webpackChunkName: "start-conversation" */ './ui/contactSelectorDialog.jsx')); +const StartGroupChatWizard = + lazy(() => import(/* webpackChunkName: "start-conversation" */ './ui/startGroupChatWizard.jsx')); +const CloudBrowserDialog = + lazy(() => import(/* webpackChunkName: "cloud-browser" */ '../ui/cloudBrowserModalDialog.jsx')); + +window.ChatCallIncomingDialog = withSuspense(Incoming); +window.ScheduleMeetingDialogUI = { Schedule: withSuspense(ScheduleMeeting) }; +window.StartMeetingDialogUI = { Start: withSuspense(StartMeeting) }; +window.ContactSelectorDialogUI = { ContactSelectorDialog: withSuspense(ContactSelectorDialog) }; +window.StartGroupChatDialogUI = { StartGroupChatWizard: withSuspense(StartGroupChatWizard) }; +Object.defineProperty(mega, 'CloudBrowserDialog', { value: withSuspense(CloudBrowserDialog) }); const EMOJI_DATASET_VERSION = 5; const CHAT_ONHISTDECR_RECNT = "onHistoryDecrypted.recent"; @@ -323,7 +346,7 @@ Chat.prototype.init = promisify(function(resolve, reject) { const rootDOMNode = this.rootDOMNode = document.querySelector(selector); const $$root = this.$$root = createRoot(rootDOMNode); $$root.render( - this.triggered(ev)); - $(window).rebind('resize.chatGlobalEventManager', ev => this.triggered(ev)); +/** + * Called internally to actually do the resize binding when needed. + * + * @private + * @returns {undefined} + * @name listeners + * @memberOf ChatGlobalEventManager.prototype + */ +lazy(ChatGlobalEventManager.prototype, 'listeners', function() { + window.addEventListener('hashchange', ev => this.triggered(ev)); + $(window).rebind('resize.chatGlobalEventManager', ev => this.triggered(ev)); - var listeners = Object.create(null); - listeners.resize = Object.create(null); - listeners.hashchange = Object.create(null); - return listeners; - }); + var listeners = Object.create(null); + listeners.resize = Object.create(null); + listeners.hashchange = Object.create(null); + return listeners; +}); - /** - * Add an `cb` event listener for `eventName` with namespace `namespace` - * - * @param {String} eventName eventType/Name - * @param {String} namespace the namespace to use for this listener - * @param {Function} cb callback to be called for this listener - * - * @returns {undefined} - */ - ChatGlobalEventManager.prototype.addEventListener = function(eventName, namespace, cb) { - this.listeners[eventName][namespace] = this.listeners[namespace] || cb; - }; +/** + * Add an `cb` event listener for `eventName` with namespace `namespace` + * + * @param {String} eventName eventType/Name + * @param {String} namespace the namespace to use for this listener + * @param {Function} cb callback to be called for this listener + * + * @returns {undefined} + */ +ChatGlobalEventManager.prototype.addEventListener = function(eventName, namespace, cb) { + this.listeners[eventName][namespace] = this.listeners[namespace] || cb; +}; - /** - * Remove listener with namespace `namespace` - * - * @param {String} eventName eventType/Name - * @param {String} namespace the namespace to use for this listener - * @returns {undefined} - */ - ChatGlobalEventManager.prototype.removeEventListener = function(eventName, namespace) { - delete this.listeners[eventName][namespace]; - }; +/** + * Remove listener with namespace `namespace` + * + * @param {String} eventName eventType/Name + * @param {String} namespace the namespace to use for this listener + * @returns {undefined} + */ +ChatGlobalEventManager.prototype.removeEventListener = function(eventName, namespace) { + delete this.listeners[eventName][namespace]; +}; - /** - * Called by the onResize/hashchange - * - * @param {String} eventName the eventType/name - * @param {Event} e the actual Event object - * - * @returns {undefined} - */ - ChatGlobalEventManager.prototype.triggered = SoonFc(140, function _chatEVDispatcher(ev) { - if (M.chat) { - var listeners = this.listeners[ev.type]; +/** + * Called by the onResize/hashchange + * + * @param {String} eventName the eventType/name + * @param {Event} e the actual Event object + * + * @returns {undefined} + */ +ChatGlobalEventManager.prototype.triggered = SoonFc(140, function _chatEVDispatcher(ev) { + if (M.chat) { + var listeners = this.listeners[ev.type]; - // eslint-disable-next-line guard-for-in - for (var k in listeners) { - listeners[k](ev); - } + // eslint-disable-next-line guard-for-in + for (var k in listeners) { + listeners[k](ev); } - }); + } +}); - // init globally. will be initialized only when first .addEventListener is called - window.chatGlobalEventManager = new ChatGlobalEventManager(); -})(); +// Create and export the singleton instance +export const chatGlobalEventManager = new ChatGlobalEventManager(); diff --git a/js/chat/chatOnboarding.jsx b/js/chat/chatOnboarding.jsx index 649668422b..a83eda408d 100644 --- a/js/chat/chatOnboarding.jsx +++ b/js/chat/chatOnboarding.jsx @@ -1,5 +1,4 @@ -import { SoonFcWrap } from "./mixins.js"; -import { VIEWS as CONVERSATIONS_APP_VIEWS, EVENTS as CONVERSATIONS_APP_EVENTS } from "./ui/conversations.jsx"; +import { SoonFcWrap } from './mixins.js'; class ChatOnboarding { diff --git a/js/chat/megaChunkLoader.jsx b/js/chat/megaChunkLoader.jsx new file mode 100644 index 0000000000..8f4c949a5d --- /dev/null +++ b/js/chat/megaChunkLoader.jsx @@ -0,0 +1,75 @@ +/** + * `MegaChunkLoader` + * --------------------------------------------------------------------------------------------------------------------- + * Overrides the default `webpack` chunk loading mechanism (`__webpack_require__.l`) to integrate + * `React.lazy`/`React.Suspense` with the `secureboot` architecture of the `webclient`, which ensures lazy-loaded chunks + * go through MEGA's XHR + hash verification pipeline. + * + * 1) What + * --------------------------------------------------------------------------------------------------------------------- + * By default `webpack` loads chunks by injecting `script` tags with standard URLs. MEGA's security model requires all + * code to be i) loaded via XHR (as to create blob URL entities) and ii) verified against a pre-computed SHA-256 hash. + * + * The default `webpack` loader behavior bypasses these integrity checks, which is not optimal. Additionally, the + * `webpack` Hot Module Replacement (HMR) relies on injecting unverified code at runtime, which is incompatible with + * the `webclient` integrity model. + * + * 2) How + * --------------------------------------------------------------------------------------------------------------------- + * This loader intercepts the `import()` calls generated by `React.lazy()`: + * + * i) `webpack` calls `__webpack_require__.l(url, done)` to load a chunk; we capture this call and derive the `url` + * from `chunkFilename` in `webpack.config.js` (ex.: `js/chat/bundle.call.js`) + * ii) we parse the URL as to extract the logical chunk name (ex.: `js/chat/bundle.call.js` -> `call` -> `chat:call_js`) + * iii) we request the resource via `M.require('chat:call_js')` call, as `secureboot.js` maintains the respective + * registry (jsl2) by mapping this logical key to the physical, hashed filename in production. + * iv) `M.require` handles the XHR download and performs the SHA-256 hash check; if the hash does not match the + * build-time manifest, the load is blocked to prevent tampering. + * v) we call the `done()` callback on success, which allows the `React.Suspense` boundary to render the component. + * + * 3) Constraints + * --------------------------------------------------------------------------------------------------------------------- + * - the original `__webpack_require__.l` loader is intentionally not preserved as a fallback -- if a chunk fails the + * integrity check (no `jsl2` entry) chunk loading fails securely rather than bypassing hash verification via the + * default `script` tag injection. + * - `splitChunks` is disabled; we want deterministic, manually defined chunks (via `webpackChunkName`) to + * ensure 1:1 mapping with the `secureboot` registry. + * - `hot` (HMR) is disabled; development can rely on standard page reloads in favor of ensuring the dev environment + * mirrors the production security pipeline. + */ + +const webpackRequire = typeof __webpack_require__ !== 'undefined' ? __webpack_require__ : null; + +if (webpackRequire && webpackRequire.l) { + const CHUNK_LOADER_DEBUG = d && localStorage.chunkLoaderDebug; + const logger = MegaLogger.getLogger('MegaChunkLoader', { levelColors: { 'DEBUG': 'olive', 'ERROR': 'tomato' } }); + + webpackRequire.l = (url, done) => { + const startTime = CHUNK_LOADER_DEBUG ? performance.now() : 0; + + const match = url.match(/bundle\.([^.]+)\.js$/); + const chunkName = match?.[1]; + + const jslName = chunkName ? `chat:${chunkName.replace(/-/g, '_')}_js` : null; + const hasJslEntry = jslName && jsl2[jslName]; + + if (hasJslEntry) { + M.require(jslName) + .then(() => { + if (CHUNK_LOADER_DEBUG) { + const duration = (performance.now() - startTime).toFixed(1); + logger.debug(`Loaded '${chunkName}' chunk in ${duration}ms`, { chunkName, jslName, url }); + } + done({ type: 'load' }); + }) + .catch(ex => { + logger.error(`Failed to load ${jslName}`, { error: ex, chunkName, jslName, url }); + done({ type: 'error', target: { src: url } }); + }); + } + else { + logger.error(`Blocked insecure chunk load ('jsl2' entry missing)`, { chunkName, jslName, url }); + done({ type: 'error', target: { src: url } }); + } + }; +} diff --git a/js/chat/mixins.js b/js/chat/mixins.js index 52101728e2..0ae3f7d61a 100644 --- a/js/chat/mixins.js +++ b/js/chat/mixins.js @@ -1,6 +1,3 @@ -import ReactDOM from "react-dom"; -import React from "react"; - var INTERSECTION_OBSERVER_AVAILABLE = typeof IntersectionObserver !== 'undefined'; var RESIZE_OBSERVER_AVAILABLE = typeof ResizeObserver !== 'undefined'; diff --git a/js/chat/ui/chatToaster.jsx b/js/chat/ui/chatToaster.jsx index 6b10afb265..a119a7ed1e 100644 --- a/js/chat/ui/chatToaster.jsx +++ b/js/chat/ui/chatToaster.jsx @@ -1,6 +1,6 @@ import React from 'react'; import { getUniqueId } from "../mixins"; -import Call from './meetings/call.jsx'; +import { isExpanded } from './meetings/utils.jsx'; import { Button } from "../../ui/buttons"; const NAMESPACE = 'chat-toast'; @@ -48,7 +48,7 @@ export default class ChatToaster extends React.Component { const { isRootToaster, showDualNotifications, onShownToast } = this.props; const now = Date.now(); if (this.toasts.length + this.persistentToasts.length) { - if (this.domRef.current && (!isRootToaster && Call.isExpanded() || M.chat)) { + if (this.domRef.current && (!isRootToaster && isExpanded() || M.chat)) { if (this.toasts.length && !shownToast) { this.dispatchToast(this.toasts.shift(), now); } diff --git a/js/chat/ui/contactSelectorDialog.jsx b/js/chat/ui/contactSelectorDialog.jsx index 1c971b329b..b4d7f1c38f 100644 --- a/js/chat/ui/contactSelectorDialog.jsx +++ b/js/chat/ui/contactSelectorDialog.jsx @@ -75,9 +75,4 @@ class ContactSelectorDialog extends MegaRenderMixin { } } - -window.ContactSelectorDialogUI = { - ContactSelectorDialog -}; - export default ContactSelectorDialog; diff --git a/js/chat/ui/contacts.jsx b/js/chat/ui/contacts.jsx index ab737b1882..fa68374aaf 100644 --- a/js/chat/ui/contacts.jsx +++ b/js/chat/ui/contacts.jsx @@ -5,10 +5,10 @@ import { Emoji, ParsedHTML, OFlowEmoji, reactStringWrap } from '../../ui/utils.j import { PerfectScrollbar } from '../../ui/perfectScrollbar.jsx'; import { Button } from '../../ui/buttons.jsx'; import { Dropdown, DropdownItem } from '../../ui/dropdowns.jsx'; -import ContactsPanel from './contactsPanel/contactsPanel.jsx'; import ModalDialogs from '../../ui/modalDialogs'; import Link from './link.jsx'; import { withUpdateObserver } from './updateObserver.jsx'; +import { hasContacts } from './contactsPanel/utils.jsx'; export const MAX_FREQUENTS = 3; @@ -1399,7 +1399,7 @@ export class ContactPickerWidget extends MegaRenderMixin { self._eventuallyAddContact(M.u[u_handle], contacts, selectableContacts, true); } var noOtherContacts = false; - if (contacts.length === 0 || !ContactsPanel.hasContacts() && this.props.step !== 1) { + if (contacts.length === 0 || !hasContacts() && this.props.step !== 1) { noOtherContacts = true; var noContactsMsg = ""; if (M.u.length < 2) { @@ -1598,7 +1598,7 @@ export class ContactPickerWidget extends MegaRenderMixin { } {this.props.participantsList ? this.renderParticipantsList() : contactsList} {selectFooter} - {this.props.showAddContact && ContactsPanel.hasContacts() ? + {this.props.showAddContact && hasContacts() ?
@@ -167,7 +174,7 @@ export default class ContactProfile extends MegaRenderMixin { return ; } - const HAS_RELATIONSHIP = ContactsPanel.hasRelationship(contact); + const HAS_RELATIONSHIP = hasRelationship(contact); return (
M.u.some(contact => contact.c === 1); - - static hasRelationship = contact => contact && contact.c === 1; - - static isVerified = contact => { - if (contact && contact.u) { - const { u: handle } = contact; - const verificationState = u_authring.Ed25519[handle] || {}; - return verificationState.method >= authring.AUTHENTICATION_METHOD.FINGERPRINT_COMPARISON; - } - return null; - }; - - static verifyCredentials = contact => { - if (contact.c === 1 && u_authring && u_authring.Ed25519) { - const verifyState = u_authring.Ed25519[contact.u] || {}; - if (typeof verifyState.method === "undefined" || - verifyState.method < authring.AUTHENTICATION_METHOD.FINGERPRINT_COMPARISON) { - fingerprintDialog(contact.u); - } - } - }; - - static resetCredentials = contact => { - if (M.isInvalidUserStatus()) { - return; - } - authring.resetFingerprintsForUser(contact.u) - .then(() => contact.trackDataChange()) - .catch(dump); - }; - - static getUserFingerprint = handle => { - const $$FINGERPRINT = []; - userFingerprint(handle, fingerprints => { - for (let i = 0; i < fingerprints.length; i++) { - $$FINGERPRINT.push( - {fingerprints[i]} - ); - } - }); - return $$FINGERPRINT; - }; - get view() { switch (megaChat.routingSubSection) { case null: - return ContactsPanel.VIEW.CONTACTS; + return VIEW.CONTACTS; case "contact": - return ContactsPanel.VIEW.PROFILE; + return VIEW.PROFILE; case "received": - return ContactsPanel.VIEW.RECEIVED_REQUESTS; + return VIEW.RECEIVED_REQUESTS; case "sent": - return ContactsPanel.VIEW.SENT_REQUESTS; + return VIEW.SENT_REQUESTS; default: console.error("Shouldn't happen."); return false; @@ -124,20 +64,20 @@ export default class ContactsPanel extends MegaRenderMixin { const { view } = this; switch (view) { - case ContactsPanel.VIEW.CONTACTS: + case VIEW.CONTACTS: return ; - case ContactsPanel.VIEW.PROFILE: - return ; - case ContactsPanel.VIEW.RECEIVED_REQUESTS: + case VIEW.PROFILE: + return ; + case VIEW.RECEIVED_REQUESTS: return ; - case ContactsPanel.VIEW.SENT_REQUESTS: + case VIEW.SENT_REQUESTS: return ; } }; componentWillUnmount() { super.componentWillUnmount(); - document.documentElement.removeEventListener(ContactsPanel.EVENTS.KEYDOWN, this.handleToggle); + document.documentElement.removeEventListener(EVENTS.KEYDOWN, this.handleToggle); if (this.requestReceivedListener) { mBroadcaster.removeListener(this.requestReceivedListener); } @@ -145,7 +85,7 @@ export default class ContactsPanel extends MegaRenderMixin { componentDidMount() { super.componentDidMount(); - document.documentElement.addEventListener(ContactsPanel.EVENTS.KEYDOWN, this.handleToggle); + document.documentElement.addEventListener(EVENTS.KEYDOWN, this.handleToggle); this.requestReceivedListener = mBroadcaster.addListener('fmViewUpdate:ipc', () => this.setState({ receivedRequestsCount: Object.keys(M.ipc).length }) ); @@ -165,9 +105,9 @@ export default class ContactsPanel extends MegaRenderMixin { receivedRequestsCount={receivedRequestsCount} /> - {view !== ContactsPanel.VIEW.PROFILE && ( + {view !== VIEW.PROFILE && (
- {view === ContactsPanel.VIEW.RECEIVED_REQUESTS && receivedRequestsCount > 1 && ( + {view === VIEW.RECEIVED_REQUESTS && receivedRequestsCount > 1 && (
-
-
-
- } - - : - null - )} - - ); - } - - return {l.no_occurrences_remain}; - } - - render() { - return ( -
- { - this.contactsListScroll = ref; - }} - disableCheckingVisibility={true} - onUserScroll={ps => ps.isCloseToBottom(30) && this.loadOccurrences()} - isVisible={this.isCurrentlyActive} - options={{ suppressScrollX: true }}> -
{this.renderOccurrences()}
-
-
- ); - } -} - -export class ConversationRightArea extends MegaRenderMixin { +class ConversationRightArea extends MegaRenderMixin { domRef = React.createRef(); static defaultProps = { @@ -1415,7 +1260,7 @@ export class ConversationRightArea extends MegaRenderMixin { } } -export class ConversationPanel extends MegaRenderMixin { +class ConversationPanel extends MegaRenderMixin { domRef = React.createRef(); messagesBlockRef = React.createRef(); @@ -1499,7 +1344,7 @@ export class ConversationPanel extends MegaRenderMixin { if (this.props.onToggleExpandedFlag) { this.props.onToggleExpandedFlag(); } - return document.body.classList[Call.isExpanded() ? 'remove' : 'add'](EXPANDED_FLAG); + return document.body.classList[isExpanded() ? 'remove' : 'add'](EXPANDED_FLAG); } startCall(type, scheduled) { @@ -1590,31 +1435,6 @@ export class ConversationPanel extends MegaRenderMixin { ); }; - CloudBrowserDialog = () => { - const { chatRoom } = this.props; - - return ( - { - this.selectedNodes = nodes; - }} - onAttachClicked={() => { - this.setState({ attachCloudDialog: false }, () => { - chatRoom.scrolledToBottom = true; - chatRoom.attachNodes(this.selectedNodes).catch(dump); - }); - }} - onClose={() => { - this.setState({ attachCloudDialog: false }, () => { - this.selectedNodes = []; - }); - }} - /> - ); - }; - SelectContactDialog = () => { const { chatRoom } = this.props; const excludedContacts = chatRoom.getParticipantsExceptMe().filter(userHandle => userHandle in M.u); @@ -2356,120 +2176,123 @@ export class ConversationPanel extends MegaRenderMixin { `} onMouseMove={() => self.onMouseMove()} data-room-id={self.props.chatRoom.chatId}> - {room.meetingsLoading && } - {room.call && ( - { - return this.state.callMinimized ? - null : - this.setState({ callMinimized: true }, () => { - this.toggleExpandedFlag(); - this.safeForceUpdate(); - }); - }} - onCallExpand={() => { - return this.state.callMinimized && - this.setState({ callMinimized: false }, () => { - $.hideTopMenu(); - if ($.dialog) { - closeDialog(); - } - loadSubPage('fm/chat'); - room.show(); - this.toggleExpandedFlag(); - }); - }} - didMount={() => { - this.toggleExpandedFlag(); - if (room.isMeeting) { - room.updatePublicHandle().catch(dump); + }> + {room.meetingsLoading && } + + {room.call && ( + { + return this.state.callMinimized ? + null : + this.setState({ callMinimized: true }, () => { + this.toggleExpandedFlag(); + this.safeForceUpdate(); + }); + }} + onCallExpand={() => { + return this.state.callMinimized && + this.setState({ callMinimized: false }, () => { + $.hideTopMenu(); + if ($.dialog) { + closeDialog(); + } + loadSubPage('fm/chat'); + room.show(); + this.toggleExpandedFlag(); + }); + }} + didMount={() => { + this.toggleExpandedFlag(); + if (room.isMeeting) { + room.updatePublicHandle().catch(dump); + } + }} + willUnmount={minimised => + this.setState({ callMinimized: false }, () => + minimised ? null : this.toggleExpandedFlag() + ) } - }} - willUnmount={minimised => - this.setState({ callMinimized: false }, () => - minimised ? null : this.toggleExpandedFlag() - ) - } - onCallEnd={() => this.safeForceUpdate()} - onDeleteMessage={msg => this.handleDeleteDialog(msg)} - onTypingAreaChanged={this.updateTypingAreaText} - parent={this} - /> - )} - {megaChat.initialPubChatHandle && room.publicChatHandle === megaChat.initialPubChatHandle && - !room.call && ( - room.isMeeting && !room.call && room.activeCallIds.length > 0 - ) && - { - room.meetingsLoading = l.joining; - u_eplusplus(firstName, lastName) - .then(() => { - return megaChat.routing.reinitAndJoinPublicChat( - room.chatId, - room.publicChatHandle, - room.publicChatKey - ); - }) - .then(() => { - delete megaChat.initialPubChatHandle; - return megaChat.getChatById(room.chatId).joinCall(audioFlag, videoFlag); - }) - .catch((ex) => { - if (d) { - console.error('E++ account failure!', ex); - } + onCallEnd={() => this.safeForceUpdate()} + onDeleteMessage={msg => this.handleDeleteDialog(msg)} + onTypingAreaChanged={this.updateTypingAreaText} + parent={this} + /> + )} - setTimeout(() => { - msgDialog( - 'warninga', - l[135], - /* Failed to create E++ account. Please try again later. */ - l.eplusplus_create_failed, - escapeHTML(api_strerror(ex) || ex) + {megaChat.initialPubChatHandle && room.publicChatHandle === megaChat.initialPubChatHandle && + !room.call && (room.isMeeting && !room.call && room.activeCallIds.length > 0) && + { + room.meetingsLoading = l.joining; + u_eplusplus(firstName, lastName) + .then(() => { + return megaChat.routing.reinitAndJoinPublicChat( + room.chatId, + room.publicChatHandle, + room.publicChatKey ); - }, 1234); - - eventlog(99745, JSON.stringify([1, String(ex).split('\n')[0]])); - }); - }} - onJoinClick={(audioFlag, videoFlag) => { - const chatId = room.chatId; - - if (room.members[u_handle]) { - delete megaChat.initialPubChatHandle; - - megaChat.routing.reinitAndOpenExistingChat(chatId, room.publicChatHandle) + }) .then(() => { - return megaChat.getChatById(chatId).joinCall(audioFlag, videoFlag); + delete megaChat.initialPubChatHandle; + return megaChat.getChatById(room.chatId).joinCall(audioFlag, videoFlag); }) .catch((ex) => { - console.error("Failed to open existing room and join call:", ex); + if (d) { + console.error('E++ account failure!', ex); + } + + setTimeout(() => { + msgDialog( + 'warninga', + l[135], + /* Failed to create E++ account. Please try again later. */ + l.eplusplus_create_failed, + escapeHTML(api_strerror(ex) || ex) + ); + }, 1234); + + eventlog(99745, JSON.stringify([1, String(ex).split('\n')[0]])); }); - } - else { - megaChat.routing.reinitAndJoinPublicChat( - chatId, - room.publicChatHandle, - room.publicChatKey - ).then(() => { + }} + onJoinClick={(audioFlag, videoFlag) => { + const chatId = room.chatId; + + if (room.members[u_handle]) { delete megaChat.initialPubChatHandle; - return megaChat.getChatById(chatId).joinCall(audioFlag, videoFlag); - }).catch((ex) => { - console.error("Failed to join room:", ex); - }); - } - }} - /> - } + megaChat.routing.reinitAndOpenExistingChat(chatId, room.publicChatHandle) + .then(() => { + return megaChat.getChatById(chatId).joinCall(audioFlag, videoFlag); + }) + .catch((ex) => { + console.error("Failed to open existing room and join call:", ex); + }); + } + else { + megaChat.routing.reinitAndJoinPublicChat( + chatId, + room.publicChatHandle, + room.publicChatKey + ).then(() => { + delete megaChat.initialPubChatHandle; + return megaChat.getChatById(chatId).joinCall(audioFlag, videoFlag); + }).catch((ex) => { + console.error("Failed to join room:", ex); + }); + } + + }} + /> + } + +
} - {this.state.attachCloudDialog && } {this.state.sendContactDialog && } {this.state.descriptionDialog && } {this.state.pushSettingsDialog && } + }> + {this.state.attachCloudDialog && + { + this.selectedNodes = nodes; + }} + onAttachClicked={() => { + this.setState({ attachCloudDialog: false }, () => { + chatRoom.scrolledToBottom = true; + chatRoom.attachNodes(this.selectedNodes).catch(dump); + }); + }} + onClose={() => { + this.setState({ attachCloudDialog: false }, () => { + this.selectedNodes = []; + }); + }} + /> + } + {privateChatDialog} {nonLoggedInJoinChatDialog} @@ -2984,7 +2828,7 @@ export class ConversationPanels extends MegaRenderMixin { ${isEmpty ? 'empty-state' : ''} unsupported-call-alert `} - content={Call.getUnsupportedBrowserMessage()} + content={getUnsupportedBrowserMessage()} ref={ref => { this.noSupportRef = ref; }} @@ -3020,146 +2864,6 @@ export class ConversationPanels extends MegaRenderMixin { } } -export class EmptyConvPanel extends React.Component { - domRef = React.createRef(); - - state = { - linkData: '', - }; - - componentDidMount() { - (M.account && M.account.contactLink ? Promise.resolve(M.account.contactLink) : api.send('clc')) - .then(res => { - if (this.domRef?.current && typeof res === 'string') { - const prefix = res.startsWith('C!') ? '' : 'C!'; - this.setState({ linkData: `${getBaseUrl()}/${prefix}${res}` }); - } - }) - .catch(dump); - } - - Tile = ({ title, desc, imgClass, buttonPrimary, buttonSecondary, onClickPrimary, onClickSecondary }) => -
- -
-

{title}

-
{desc}
-
-
; - - render() { - const { isMeeting, onNewChat, onStartMeeting, onScheduleMeeting } = this.props; - const { linkData } = this.state; - - return ( -
-
-

{ - isMeeting ? - l.meetings_empty_header : /* `Get together with MEGA meetings` */ - l.chat_empty_header /* `Keep in touch with MEGA chat` */ - }

-

- {reactStringWrap( - isMeeting ? - l.meetings_empty_subheader : /* `Voice and video calls, protected with...` */ - l.chat_empty_subheader, /* `Direct messaging, group chats and calling anyone, on...` */ - '[A]', - Link, - { - onClick: () => { - window.open('https://mega.io/chatandmeetings', '_blank', 'noopener,noreferrer'); - eventlog(this.props.isMeeting ? 500281 : 500280); - } - } - )} -

-
-
- { - if (isMeeting) { - onStartMeeting(); - eventlog(500275); - } - else { - contactAddDialog(); - eventlog(500276); - } - }} - onClickSecondary={() => { - /* `Copied to clipboard` */ - copyToClipboard(linkData, `${l[371]}${linkData}`); - delay('chat-event-copy-contact-link', () => eventlog(500277)); - }} - /> - { - if (isMeeting) { - onScheduleMeeting(); - eventlog(500278); - } - else { - onNewChat(); - eventlog(500279); - } - }} - /> -
-
- ); - } -} - function isStartCallDisabled(room) { if (isGuest()) { return true; diff --git a/js/chat/ui/conversations.jsx b/js/chat/ui/conversations.jsx index d8956be544..f60c92a4e4 100644 --- a/js/chat/ui/conversations.jsx +++ b/js/chat/ui/conversations.jsx @@ -1,17 +1,38 @@ -import { hot } from 'react-hot-loader/root'; -import React from 'react'; +import React, { Suspense, lazy } from 'react'; import { MegaRenderMixin } from '../mixins.js'; -import { ConversationPanels, EmptyConvPanel } from './conversationpanel.jsx'; -import ContactsPanel from './contactsPanel/contactsPanel.jsx'; -import { Start as StartMeetingDialog } from './meetings/workflow/start.jsx'; -import { Schedule as ScheduleMeetingDialog } from './meetings/schedule/schedule.jsx'; -import { Edit as ScheduleOccurrenceDialog } from './meetings/schedule/recurring.jsx'; -import { StartGroupChatWizard } from './startGroupChatWizard.jsx'; -import Call, { inProgressAlert } from './meetings/call.jsx'; -import ChatToaster from './chatToaster.jsx'; -import LeftPanel from './leftPanel/leftPanel.jsx'; +import { inProgressAlert, isExpanded } from './meetings/utils.jsx'; import { FreeCallEnded as FreeCallEndedDialog } from './meetings/workflow/freeCallEnded.jsx'; -import ContactSelectorDialog from './contactSelectorDialog.jsx'; +import { hasContacts } from './contactsPanel/utils.jsx'; +import { NAMESPACE } from './leftPanel/utils.jsx'; +import ErrorBoundary from './errorBoundary.jsx'; +import Fallback from './fallback.jsx'; + +const LeftPanel = + lazy(() => import(/* webpackChunkName: "core-ui" */ './leftPanel/leftPanel.jsx')); +const EmptyConversationsPanel = + lazy(() => import(/* webpackChunkName: "core-ui" */ './emptyConversationsPanel.jsx')); +const ChatToaster = + lazy(() => import(/* webpackChunkName: "core-ui" */ './chatToaster.jsx')); +const ConversationPanels = + lazy(() => + import(/* webpackChunkName: "core-ui" */ './conversationpanel.jsx') + .then(m => ({ default: m.ConversationPanels })) + ); + +const ContactsPanel = + lazy(() => import(/* webpackChunkName: "contacts-panel" */ './contactsPanel/contactsPanel.jsx')); + +const ScheduleMeetingDialog = + lazy(() => import(/* webpackChunkName: "schedule-meeting" */ './meetings/schedule/schedule.jsx')); +const ScheduleOccurrenceDialog = + lazy(() => import(/* webpackChunkName: "schedule-meeting" */ './meetings/schedule/edit.jsx')); + +const ContactSelectorDialog = + lazy(() => import(/* webpackChunkName: "start-conversation" */ './contactSelectorDialog.jsx')); +const StartGroupChatWizard = + lazy(() => import(/* webpackChunkName: "start-conversation" */ './startGroupChatWizard.jsx')); +const StartMeetingDialog = + lazy(() => import(/* webpackChunkName: "start-conversation" */ './meetings/workflow/start.jsx')); export const VIEWS = { CHATS: 0x00, @@ -279,163 +300,165 @@ class ConversationsApp extends MegaRenderMixin { in-chat ${is_chatlink ? 'chatlink' : ''} `}> - {!isLoading && } - {!isLoading && routingSection === 'contacts' && ( - - )} + }> + {!isLoading && } + {!isLoading && routingSection === 'contacts' && ( + + )} + {!isLoading && + this.setState(() => ({ callExpanded: isExpanded() }))} + onMount={() => { + const chatRoom = megaChat.getCurrentRoom(); + const view = chatRoom && chatRoom.isMeeting ? MEETINGS : CHATS; + this.setState({ view }, () => { + megaChat.currentlyOpenedView = view; + }); + }} + /> + } + {!isLoading && isEmpty && + this.setState({ contactSelectorDialog: true })} + onStartMeeting={() => this.startMeeting()} + onScheduleMeeting={() => this.setState({ scheduleMeetingDialog: true })} + /> + } + {!isLoading && routingSection === 'notFound' &&
Section not found
} - {!isLoading && isEmpty && - this.setState({ contactSelectorDialog: true })} - onStartMeeting={() => this.startMeeting()} - onScheduleMeeting={() => this.setState({ scheduleMeetingDialog: true })} - /> - } - {!isLoading && - this.setState(() => ({ callExpanded: Call.isExpanded() }))} - onMount={() => { - const chatRoom = megaChat.getCurrentRoom(); - const view = chatRoom && chatRoom.isMeeting ? MEETINGS : CHATS; - this.setState({ view }, () => { - megaChat.currentlyOpenedView = view; - }); - }} - /> - }
); const noteChat = megaChat.getNoteChat(); return ( -
- {contactSelectorDialog && ( - - this.setState({ startGroupChatDialog: true, contactSelectorDialog: false }) - }, - ...megaChat.WITH_SELF_NOTE ? - ContactsPanel.hasContacts() || noteChat && noteChat.hasMessages() ? [] : [{ - key: 'noteChat', - title: l.note_label, - icon: 'sprite-fm-mono icon-file-text-thin-outline note-chat-icon', - onClick: () => { - closeDialog(); - loadSubPage(`fm/chat/p/${u_handle}`); + +
+ }> + {startMeetingDialog && ( + { + megaChat.createAndStartMeeting(topic, audio, video); + this.setState({ startMeetingDialog: false }); + }} + onClose={() => this.setState({ startMeetingDialog: false })} + /> + )} + + {startGroupChatDialog && ( + this.setState({ startGroupChatDialog: false })} + onConfirmClicked={() => this.setState({ startGroupChatDialog: false })} + /> + )} + + {scheduleMeetingDialog && ( + { + this.setState({ scheduleMeetingDialog: false }, () => { + this.chatRoomRef = null; + }); + }} + /> + )} + + {scheduleOccurrenceDialog && ( + { + this.setState({ scheduleOccurrenceDialog: false }, () => { + this.occurrenceRef = null; + }); + }} + /> + )} + + {contactSelectorDialog && ( + + this.setState({ startGroupChatDialog: true, contactSelectorDialog: false }) + }, + ...megaChat.WITH_SELF_NOTE ? + hasContacts() || noteChat && noteChat.hasMessages() ? [] : [{ + key: 'noteChat', + title: l.note_label, + icon: 'sprite-fm-mono icon-file-text-thin-outline note-chat-icon', + onClick: () => { + closeDialog(); + loadSubPage(`fm/chat/p/${u_handle}`); + } + }] : + [] + ]} + showAddContact={hasContacts()} + onClose={() => this.setState({ contactSelectorDialog: false })} + onSelectDone={selected => { + if (selected.length === 1) { + return megaChat.createAndShowPrivateRoom(selected[0]) + .then(room => room.setActive()); } - }] : - [] - ]} - showAddContact={ContactsPanel.hasContacts()} - onClose={() => this.setState({ contactSelectorDialog: false })} - onSelectDone={selected => { - if (selected.length === 1) { - return megaChat.createAndShowPrivateRoom(selected[0]) - .then(room => room.setActive()); - } - megaChat.createAndShowGroupRoomFor(selected); - }} - /> - )} - - {startGroupChatDialog && ( - this.setState({ startGroupChatDialog: false })} - onConfirmClicked={() => this.setState({ startGroupChatDialog: false })} - /> - )} - - {startMeetingDialog && ( - { - megaChat.createAndStartMeeting(topic, audio, video); - this.setState({ startMeetingDialog: false }); - }} - onClose={() => this.setState({ startMeetingDialog: false })} - /> - )} - - {scheduleMeetingDialog && ( - { - this.setState({ scheduleMeetingDialog: false }, () => { - this.chatRoomRef = null; - }); - }} - /> - )} - - {scheduleOccurrenceDialog && ( - { - this.setState({ scheduleOccurrenceDialog: false }, () => { - this.occurrenceRef = null; - }); - }} - /> - )} - - {freeCallEndedDialog && ( - { - this.setState({ freeCallEndedDialog: false }); - }} - /> - )} - - this.renderView(view)} - startMeeting={() => { - this.startMeeting(); - eventlog(500293); - }} - scheduleMeeting={() => { - this.setState({ scheduleMeetingDialog: true }); - delay('chat-event-sm-button-main', () => eventlog(99918)); - }} - createNewChat={() => this.setState({ contactSelectorDialog: true })} - /> - - {rightPane} -
+ megaChat.createAndShowGroupRoomFor(selected); + }} + /> + )} + + + }> + {routingSection && + this.renderView(view)} + startMeeting={() => { + this.startMeeting(); + eventlog(500293); + }} + scheduleMeeting={() => { + this.setState({ scheduleMeetingDialog: true }); + delay('chat-event-sm-button-main', () => eventlog(99918)); + }} + createNewChat={() => this.setState({ contactSelectorDialog: true })} + /> + } + + + {freeCallEndedDialog && ( + { + this.setState({ freeCallEndedDialog: false }); + }} + /> + )} + + {rightPane} +
+ ); } } - -if (module.hot) { - module.hot.accept(); - ConversationsApp = hot(ConversationsApp); -} - -export default { - ConversationsApp: ConversationsApp -}; +export default ConversationsApp; diff --git a/js/chat/ui/emptyConversationsPanel.jsx b/js/chat/ui/emptyConversationsPanel.jsx new file mode 100644 index 0000000000..12e5c35f16 --- /dev/null +++ b/js/chat/ui/emptyConversationsPanel.jsx @@ -0,0 +1,145 @@ +import React from 'react'; +import { Button } from '../../ui/buttons.jsx'; +import Link from './link.jsx'; +import { reactStringWrap } from '../../ui/utils.jsx'; + +const Tile = ({ title, desc, imgClass, buttonPrimary, buttonSecondary, onClickPrimary, onClickSecondary }) => +
+ +
+

{title}

+
{desc}
+
+
; + + +export default class EmptyConversationsPanel extends React.Component { + domRef = React.createRef(); + + state = { + linkData: '', + }; + + componentDidMount() { + (M.account && M.account.contactLink ? Promise.resolve(M.account.contactLink) : api.send('clc')) + .then(res => { + if (this.domRef?.current && typeof res === 'string') { + const prefix = res.startsWith('C!') ? '' : 'C!'; + this.setState({ linkData: `${getBaseUrl()}/${prefix}${res}` }); + } + }) + .catch(dump); + } + + render() { + const { isMeeting, onNewChat, onStartMeeting, onScheduleMeeting } = this.props; + const { linkData } = this.state; + + return ( +
+
+

{ + isMeeting ? + l.meetings_empty_header : /* `Get together with MEGA meetings` */ + l.chat_empty_header /* `Keep in touch with MEGA chat` */ + }

+

+ {reactStringWrap( + isMeeting ? + l.meetings_empty_subheader : /* `Voice and video calls, protected with...` */ + l.chat_empty_subheader, /* `Direct messaging, group chats and calling anyone, on...` */ + '[A]', + Link, + { + onClick: () => { + window.open('https://mega.io/chatandmeetings', '_blank', 'noopener,noreferrer'); + eventlog(this.props.isMeeting ? 500281 : 500280); + } + } + )} +

+
+
+ { + if (isMeeting) { + onStartMeeting(); + eventlog(500275); + } + else { + contactAddDialog(); + eventlog(500276); + } + }} + onClickSecondary={() => { + /* `Copied to clipboard` */ + copyToClipboard(linkData, `${l[371]}${linkData}`); + delay('chat-event-copy-contact-link', () => eventlog(500277)); + }} + /> + { + if (isMeeting) { + onScheduleMeeting(); + eventlog(500278); + } + else { + onNewChat(); + eventlog(500279); + } + }} + /> +
+
+ ); + } +} diff --git a/js/chat/ui/errorBoundary.jsx b/js/chat/ui/errorBoundary.jsx new file mode 100644 index 0000000000..c172fef2e5 --- /dev/null +++ b/js/chat/ui/errorBoundary.jsx @@ -0,0 +1,51 @@ +import React from 'react'; +import Link from './link.jsx'; + +export default class ErrorBoundary extends React.Component { + state = { hasError: false, error: null }; + + static getDerivedStateFromError(error) { + return { hasError: true, error }; + } + + componentDidCatch(error, errorInfo) { + console.error(error, errorInfo); + } + + handleRetry = () => this.setState({ hasError: false, error: null }); + + render() { + const { hasError, error } = this.state; + + if (hasError) { + // TODO: add translation strings + return ( +
+
+ + +

{l[200]}

+ + + Please try again or  + location.reload()}>reload the page. + + + {d && +
+ {error.toString()} +
+ } +
+
+ ); + } + + return this.props.children; + } +} diff --git a/js/chat/ui/fallback.jsx b/js/chat/ui/fallback.jsx new file mode 100644 index 0000000000..6e5f17f7aa --- /dev/null +++ b/js/chat/ui/fallback.jsx @@ -0,0 +1,11 @@ +import React from 'react'; + +export default class Fallback extends React.Component { + render() { + return ( +
+
+
+ ); + } +} diff --git a/js/chat/ui/gifPanel/gifPanel.jsx b/js/chat/ui/gifPanel/gifPanel.jsx index c411ef13ac..816ed98874 100644 --- a/js/chat/ui/gifPanel/gifPanel.jsx +++ b/js/chat/ui/gifPanel/gifPanel.jsx @@ -2,42 +2,7 @@ import React from 'react'; import { PerfectScrollbar } from '../../../ui/perfectScrollbar.jsx'; import SearchField from './searchField.jsx'; import ResultContainer from './resultContainer.jsx'; - -const GIF_PANEL_CLASS = 'gif-panel-wrapper'; -const MAX_HEIGHT = 550; - -export const API = { - HOSTNAME: 'https://giphy.mega.nz/', - ENDPOINT: 'v1/gifs', - SCHEME: 'giphy://', - convert: path => { - if (path && typeof path === 'string') { - const FORMAT = [API.SCHEME, API.HOSTNAME]; - if (path.indexOf(API.SCHEME) === 0 || path.indexOf(API.HOSTNAME) === 0) { - return ( - String.prototype.replace.apply(path, path.indexOf(API.SCHEME) === 0 ? FORMAT : FORMAT.reverse()) - ); - } - } - }, - LIMIT: 50, - OFFSET: 50 -}; - -export const LABELS = freeze({ - get SEARCH() { - return l[24025]; - }, - get NO_RESULTS() { - return l[24050]; - }, - get NOT_AVAILABLE() { - return l[24512]; - }, - get END_OF_RESULTS() { - return l[24156]; - } -}); +import { API, GIF_PANEL_CLASS, MAX_HEIGHT } from './utils.jsx'; export default class GifPanel extends React.Component { domRef = React.createRef(); diff --git a/js/chat/ui/gifPanel/resultContainer.jsx b/js/chat/ui/gifPanel/resultContainer.jsx index 7f092a117a..bfecee06df 100644 --- a/js/chat/ui/gifPanel/resultContainer.jsx +++ b/js/chat/ui/gifPanel/resultContainer.jsx @@ -1,6 +1,6 @@ import React from 'react'; import Result from './result.jsx'; -import { API, LABELS } from './gifPanel.jsx'; +import { API, LABELS } from './utils.jsx'; export const HAS_INTERSECTION_OBSERVER = typeof IntersectionObserver !== 'undefined'; export const NODE_CONTAINER_CLASS = 'node-container'; diff --git a/js/chat/ui/gifPanel/searchField.jsx b/js/chat/ui/gifPanel/searchField.jsx index 43b586feb6..b7df5bc64a 100644 --- a/js/chat/ui/gifPanel/searchField.jsx +++ b/js/chat/ui/gifPanel/searchField.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import { LABELS } from './gifPanel.jsx'; +import { LABELS } from './utils.jsx'; export default class SearchField extends React.Component { static inputRef = React.createRef(); diff --git a/js/chat/ui/gifPanel/utils.jsx b/js/chat/ui/gifPanel/utils.jsx new file mode 100644 index 0000000000..2d78b3996a --- /dev/null +++ b/js/chat/ui/gifPanel/utils.jsx @@ -0,0 +1,35 @@ +export const GIF_PANEL_CLASS = 'gif-panel-wrapper'; +export const MAX_HEIGHT = 550; + +export const API = { + HOSTNAME: 'https://giphy.mega.nz/', + ENDPOINT: 'v1/gifs', + SCHEME: 'giphy://', + convert: path => { + if (path && typeof path === 'string') { + const FORMAT = [API.SCHEME, API.HOSTNAME]; + if (path.indexOf(API.SCHEME) === 0 || path.indexOf(API.HOSTNAME) === 0) { + return ( + String.prototype.replace.apply(path, path.indexOf(API.SCHEME) === 0 ? FORMAT : FORMAT.reverse()) + ); + } + } + }, + LIMIT: 50, + OFFSET: 50 +}; + +export const LABELS = freeze({ + get SEARCH() { + return l[24025]; + }, + get NO_RESULTS() { + return l[24050]; + }, + get NOT_AVAILABLE() { + return l[24512]; + }, + get END_OF_RESULTS() { + return l[24156]; + } +}); diff --git a/js/chat/ui/historyPanel.jsx b/js/chat/ui/historyPanel.jsx index 04a7786956..af289f6ca8 100644 --- a/js/chat/ui/historyPanel.jsx +++ b/js/chat/ui/historyPanel.jsx @@ -10,7 +10,7 @@ import { ChatHandleMessage } from "./messages/chatHandle.jsx"; import GenericConversationMessage from "./messages/generic.jsx"; import { PerfectScrollbar } from "../../ui/perfectScrollbar.jsx"; import { RetentionChange } from "./messages/retentionChange.jsx"; -import Call from './meetings/call.jsx'; +import { isExpanded } from './meetings/utils.jsx'; import ScheduleMetaChange from "./messages/scheduleMetaChange.jsx"; export default class HistoryPanel extends MegaRenderMixin { @@ -240,7 +240,7 @@ export default class HistoryPanel extends MegaRenderMixin { return; } - if (Call.isExpanded()) { + if (isExpanded()) { const $container = $('.meetings-call'); const $messages = $('.js-messages-scroll-area', $container); const $textarea = $('.chat-textarea-block', $container); diff --git a/js/chat/ui/incomingSharesAccordionPanel.jsx b/js/chat/ui/incomingSharesAccordionPanel.jsx index 663191a031..9b28a85254 100644 --- a/js/chat/ui/incomingSharesAccordionPanel.jsx +++ b/js/chat/ui/incomingSharesAccordionPanel.jsx @@ -1,4 +1,4 @@ -var React = require("react"); +import React from 'react'; import {MegaRenderMixin} from "../mixins"; const SharedFolderItem = ({ node, isLoading }) => { diff --git a/js/chat/ui/inviteParticipantsPanel.jsx b/js/chat/ui/inviteParticipantsPanel.jsx index 2682d6e645..d96dd5e5fc 100644 --- a/js/chat/ui/inviteParticipantsPanel.jsx +++ b/js/chat/ui/inviteParticipantsPanel.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import Call from './meetings/call.jsx'; +import { isExpanded } from './meetings/utils.jsx'; import MiniUI from '../../ui/miniui.jsx'; import { Button } from '../../ui/buttons.jsx'; import { Dropdown, DropdownItem } from '../../ui/dropdowns.jsx'; @@ -71,7 +71,7 @@ export class InviteParticipantsPanel extends React.Component { render() { const { chatRoom, disableLinkToggle, onAddParticipants } = this.props; const { link, copied } = this.state; - const inCall = Call.isExpanded(); + const inCall = isExpanded(); if (this.loading) { return ( diff --git a/js/chat/ui/leftPanel/actions.jsx b/js/chat/ui/leftPanel/actions.jsx index afd14a4ef8..56c28802ee 100644 --- a/js/chat/ui/leftPanel/actions.jsx +++ b/js/chat/ui/leftPanel/actions.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { Button } from '../../../ui/buttons.jsx'; import { Dropdown, DropdownItem } from '../../../ui/dropdowns.jsx'; -import { FILTER, NAMESPACE } from './leftPanel.jsx'; +import { FILTER, NAMESPACE } from './utils.jsx'; const Actions = ({ view, diff --git a/js/chat/ui/leftPanel/conversationsList.jsx b/js/chat/ui/leftPanel/conversationsList.jsx index e4288515e9..6c4677da5c 100644 --- a/js/chat/ui/leftPanel/conversationsList.jsx +++ b/js/chat/ui/leftPanel/conversationsList.jsx @@ -2,7 +2,7 @@ import React from 'react'; import { MegaRenderMixin } from '../../mixins.js'; import { PerfectScrollbar } from '../../../ui/perfectScrollbar.jsx'; import ConversationsListItem from './conversationsListItem.jsx'; -import { FILTER, NAMESPACE } from './leftPanel.jsx'; +import { FILTER, NAMESPACE } from './utils.jsx'; import Button from '../meetings/button.jsx'; export const ConversationsList = ({ conversations, className, children }) => { diff --git a/js/chat/ui/leftPanel/leftPanel.jsx b/js/chat/ui/leftPanel/leftPanel.jsx index eb03c63ea9..55637dd32b 100644 --- a/js/chat/ui/leftPanel/leftPanel.jsx +++ b/js/chat/ui/leftPanel/leftPanel.jsx @@ -5,13 +5,7 @@ import { Navigation } from './navigation.jsx'; import Actions from './actions.jsx'; import { Chats, Meetings, Archived } from './conversationsList.jsx'; import { withUpdateObserver } from '../updateObserver.jsx'; - -export const NAMESPACE = 'lhp'; - -export const FILTER = { - MUTED: 'muted', - UNREAD: 'unread' -}; +import { NAMESPACE } from './utils.jsx'; class LeftPanel extends MegaRenderMixin { domRef = React.createRef(); diff --git a/js/chat/ui/leftPanel/navigation.jsx b/js/chat/ui/leftPanel/navigation.jsx index 43ed11f681..deda553a3c 100644 --- a/js/chat/ui/leftPanel/navigation.jsx +++ b/js/chat/ui/leftPanel/navigation.jsx @@ -1,6 +1,6 @@ import React from 'react'; import Button from '../meetings/button.jsx'; -import { NAMESPACE } from './leftPanel.jsx'; +import { NAMESPACE } from './utils.jsx'; export const Navigation = ({ view, views: { CHATS, MEETINGS }, routingSection, unreadChats, unreadMeetings, contactRequests, renderView }) => diff --git a/js/chat/ui/leftPanel/utils.jsx b/js/chat/ui/leftPanel/utils.jsx new file mode 100644 index 0000000000..effe59489a --- /dev/null +++ b/js/chat/ui/leftPanel/utils.jsx @@ -0,0 +1,6 @@ +export const NAMESPACE = 'lhp'; + +export const FILTER = { + MUTED: 'muted', + UNREAD: 'unread' +}; diff --git a/js/chat/ui/meetings/call.jsx b/js/chat/ui/meetings/call.jsx index 7a5832e6d3..e44b4bc639 100644 --- a/js/chat/ui/meetings/call.jsx +++ b/js/chat/ui/meetings/call.jsx @@ -1,6 +1,6 @@ import React from 'react'; import { MegaRenderMixin } from '../../mixins.js'; -import Stream, { STREAM_ACTIONS, MAX_STREAMS } from './stream.jsx'; +import Stream from './stream.jsx'; import Sidebar from './sidebar.jsx'; import Invite from './workflow/invite/invite.jsx'; import Ephemeral from './workflow/ephemeral.jsx'; @@ -14,121 +14,11 @@ import { ParsedHTML } from '../../../ui/utils.jsx'; import Link from '../link.jsx'; import { InviteParticipantsPanel } from '../inviteParticipantsPanel.jsx'; import { Dropdown } from '../../../ui/dropdowns.jsx'; +import { isExpanded, isGuest, isModerator, MODE, VIEW, STREAM_ACTIONS, MAX_STREAMS } from './utils.jsx'; const NAMESPACE = 'meetings-call'; -export const EXPANDED_FLAG = 'in-call'; const MOUSE_OUT_DELAY = 2500; -/** - * MODE - * @description Describes the available call modes. - * @enum {number} - * @property {number} THUMBNAIL - * @property {number} MAIN - * @readonly - */ - -export const MODE = { - THUMBNAIL: 1, - MAIN: 2, - MINI: 3 -}; - -/** - * VIEW - * @description Describes the available view states. - * @enum {number} - * @property {number} DEFAULT - * @property {number} CHAT - * @property {number} PARTICIPANTS - * @readonly - */ - -export const VIEW = { - DEFAULT: 0, - CHAT: 1, - PARTICIPANTS: 2 -}; - -/** - * TYPE - * @description Describes the available call types. - * @type {{VIDEO: number, AUDIO: number}} - * @enum {number} - * @property {number} AUDIO - * @property {number} VIDEO - * @readonly - */ - -export const TYPE = { - AUDIO: 1, - VIDEO: 2 -}; - -/** - * isGuest - * @description Returns the true if the current user is a guest. - * @returns {boolean} - */ - -export const isGuest = () => !u_type; - -/** - * inProgressAlert - * @description Renders conditionally message dialog if there is another active call currently. Attached to the - * audio/video call controls on various places across the UI. - * @returns {Promise} - */ - -export const inProgressAlert = (isJoin, chatRoom) => { - return new Promise((resolve, reject) => { - if (megaChat.haveAnyActiveCall()) { - if (window.sfuClient) { - // Active call w/ the current client - const { chatRoom: activeCallRoom } = megaChat.activeCall; - const peers = activeCallRoom ? - activeCallRoom.getParticipantsExceptMe(activeCallRoom.getCallParticipants()) - .map(h => M.getNameByHandle(h)) : - []; - let body = isJoin ? l.cancel_to_join : l.cancel_to_start; - if (peers.length) { - body = mega.utils.trans.listToString( - peers, - isJoin ? l.cancel_with_to_join : l.cancel_with_to_start - ); - } - msgDialog('warningb', null, l.call_in_progress, body, null, 1); - return reject(); - } - - // Active call on another client; incl. current user already being in the call -> - // skip warning notification - if (chatRoom.getCallParticipants().includes(u_handle)) { - return resolve(); - } - - // Active call on another client - return ( - msgDialog( - `warningb:!^${l[2005]}!${isJoin ? l.join_call_anyway : l.start_call_anyway}`, - null, - isJoin ? l.join_multiple_calls_title : l.start_multiple_calls_title, - isJoin ? l.join_multiple_calls_text : l.start_multiple_calls_text, - join => { - if (join) { - return resolve(); - } - return reject(); - }, - 1 - ) - ); - } - resolve(); - }); -}; -window.inProgressAlert = inProgressAlert; - class RecordingConsentDialog extends React.Component { static dialogName = `${NAMESPACE}-consent`; @@ -255,44 +145,6 @@ export default class Call extends MegaRenderMixin { activeElement: false }; - /** - * isModerator - * @description Given `chatRoom` and `handle` -- returns true if the user is moderator. - * @param chatRoom {ChatRoom} - * @param handle {string} - * @returns {boolean} - */ - - static isModerator = (chatRoom, handle) => { - if (chatRoom && handle) { - return chatRoom.members[handle] === ChatRoom.MembersSet.PRIVILEGE_STATE.OPERATOR; - } - return false; - }; - - /** - * isExpanded - * @description Returns true if the in-call UI is expanded; false when minimized. - * @returns {boolean} - */ - - static isExpanded = () => document.body.classList.contains(EXPANDED_FLAG); - - /** - * getUnsupportedBrowserMessage - * @description Returns conditionally message for unsupported browser; used along w/ feature detection within - * `megaChat.hasSupportForCalls`. The two message variants concern a) outdated browser version (e.g. Chromium-based) - * or b) completely unsupported browsers, such as Safari/Firefox. - * @see megaChat.hasSupportForCalls - * @see Alert - * @returns {String} - */ - - static getUnsupportedBrowserMessage = () => - navigator.userAgent.match(/Chrom(e|ium)\/(\d+)\./) ? - l.alert_unsupported_browser_version : - l.alert_unsupported_browser; - constructor(props) { super(props); const { SOUNDS } = megaChat; @@ -1018,7 +870,7 @@ export default class Call extends MegaRenderMixin { renderRecordingControl = () => { const { chatRoom, call, peers } = this.props; const { recorderCid, recordingTooltip, recordingActivePeer } = this.state; - const isModerator = Call.isModerator(chatRoom, u_handle); + const userIsModerator = isModerator(chatRoom, u_handle); const $$CONTAINER = ({ className, onClick, children }) =>
; if (recorderCid) { - const isRecorder = isModerator && recorderCid === call.sfuClient.cid; + const isRecorder = userIsModerator && recorderCid === call.sfuClient.cid; const recordingPeer = peers[recorderCid]; return ( @@ -1096,7 +948,7 @@ export default class Call extends MegaRenderMixin { const isOnHold = !!(call?.av & Av.onHold); return ( - isModerator && + userIsModerator && <$$CONTAINER className={isOnHold ? 'disabled' : ''} onClick={() => { @@ -1237,7 +1089,7 @@ export default class Call extends MegaRenderMixin { this.pageChangeListener = mBroadcaster.addListener('pagechange', () => { const currentRoom = megaChat.getCurrentRoom(); - if (Call.isExpanded() && (!M.chat || currentRoom && currentRoom.chatId !== chatRoom.chatId)) { + if (isExpanded() && (!M.chat || currentRoom && currentRoom.chatId !== chatRoom.chatId)) { this.handleCallMinimize(); } }); @@ -1483,7 +1335,7 @@ export default class Call extends MegaRenderMixin {
} - {onboardingRecording && Call.isModerator(chatRoom, u_handle) && + {onboardingRecording && isModerator(chatRoom, u_handle) &&
{`${name} ${l.me}`} : } - {Call.isModerator(chatRoom, handle) && + {isModerator(chatRoom, handle) && @@ -104,7 +104,7 @@ class Participant extends MegaRenderMixin {
    - {hasRelationship ? + {isRelated ?
  • } - {hasRelationship ? + {isRelated ?
  • +
+ + + {!(overlayed || callExpanded) && closeDialog && + this.setState({ closeDialog: false })} + onClose={onClose} + /> + } + + ); + } +} diff --git a/js/chat/ui/meetings/schedule/occurrences.jsx b/js/chat/ui/meetings/schedule/occurrences.jsx new file mode 100644 index 0000000000..60cc165005 --- /dev/null +++ b/js/chat/ui/meetings/schedule/occurrences.jsx @@ -0,0 +1,179 @@ +import React from 'react'; +import { MegaRenderMixin } from '../../../mixins.js'; +import { isToday, isTomorrow } from './helpers.jsx'; +import { Button } from '../../../../ui/buttons.jsx'; +import { PerfectScrollbar } from '../../../../ui/perfectScrollbar.jsx'; + +export default class Occurrences extends MegaRenderMixin { + domRef = React.createRef(); + loadingMore = false; + + state = { + editDialog: false, + occurrenceId: undefined + }; + + loadOccurrences() { + if (!this.loadingMore) { + const { scheduledMeeting, occurrences } = this.props; + const occurrenceItems = Object.values(occurrences || {}); + const lastOccurrence = occurrenceItems[occurrenceItems.length - 1]; + + if (lastOccurrence) { + this.loadingMore = true; + scheduledMeeting.getOccurrences({ from: lastOccurrence.start }) + .catch(dump) + .finally(() => { + this.loadingMore = false; + }); + } + } + } + + renderCancelConfirmation(occurrence) { + const { scheduledMeeting, chatRoom } = this.props; + const nextOccurrences = Object.values(scheduledMeeting.occurrences).filter(o => o.isUpcoming); + + if (nextOccurrences.length > 1) { + return ( + msgDialog( + `confirmation:!^${l.cancel_meeting_occurrence_button}!${l.schedule_cancel_abort}`, + 'cancel-occurrence', + l.schedule_cancel_occur_dlg_title, + l.schedule_cancel_occur_dlg_text, + cb => cb && occurrence.cancel(), + 1 + ) + ); + } + + return chatRoom.hasMessages(true) ? + msgDialog( + `confirmation:!^${l.cancel_meeting_button}!${l.schedule_cancel_abort}`, + 'cancel-occurrence', + l.schedule_cancel_all_dialog_title, + l.schedule_cancel_all_dialog_move, + cb => cb && megaChat.plugins.meetingsManager.cancelMeeting(scheduledMeeting, scheduledMeeting.chatId), + 1 + ) : + msgDialog( + `confirmation:!^${l.cancel_meeting_button}!${l.schedule_cancel_abort}`, + 'cancel-occurrence', + l.schedule_cancel_all_dialog_title, + l.schedule_cancel_all_dialog_archive, + cb => cb && megaChat.plugins.meetingsManager.cancelMeeting(scheduledMeeting, scheduledMeeting.chatId), + 1 + ); + } + + renderLoading() { + return ( +
+ {Array.from({ length: 10 }, (el, i) => { + return ( +
+
+
+
+
+
+
+ ); + })} +
+ ); + } + + renderOccurrences() { + const { chatRoom, occurrences, occurrencesLoading, scheduledMeeting } = this.props; + + if (occurrencesLoading) { + return this.renderLoading(); + } + + if (occurrences && occurrences.length > 0) { + const sortedOccurrences = Object.values(occurrences).sort((a, b) => a.start - b.start); + return ( + <> + {sortedOccurrences.map(occurrence => + occurrence.isUpcoming ? +
+
+ {isToday(occurrence.start) && {l.today_occurrence_label} -} + {isTomorrow(occurrence.start) && {l.tomorrow_occurrence_label} -} + {time2date(occurrence.start / 1000, 19)} +
+
+
{scheduledMeeting.title}
+
+ {toLocaleTime(occurrence.start)} - +   + {toLocaleTime(occurrence.end)} +
+ {chatRoom.iAmOperator() && +
+
+
+
+
+
+ } +
+
: + null + )} + + ); + } + + return {l.no_occurrences_remain}; + } + + render() { + return ( +
+ { + this.contactsListScroll = ref; + }} + disableCheckingVisibility={true} + onUserScroll={ps => ps.isCloseToBottom(30) && this.loadOccurrences()} + isVisible={this.isCurrentlyActive} + options={{ suppressScrollX: true }}> +
{this.renderOccurrences()}
+
+
+ ); + } +} diff --git a/js/chat/ui/meetings/schedule/recurring.jsx b/js/chat/ui/meetings/schedule/recurring.jsx index 48cbd0e702..0447c73740 100644 --- a/js/chat/ui/meetings/schedule/recurring.jsx +++ b/js/chat/ui/meetings/schedule/recurring.jsx @@ -1,14 +1,11 @@ import React from 'react'; -import { MegaRenderMixin } from '../../../mixins'; +import { MegaRenderMixin } from '../../../mixins.js'; import Button from '../button.jsx'; -import { CloseDialog, Column, Row, Schedule, UpgradeNotice } from './schedule.jsx'; +import { Column, Row } from './utils.jsx'; import Datepicker from './datepicker.jsx'; import Select from './select.jsx'; -import ModalDialogsUI from '../../../../ui/modalDialogs.jsx'; -import { addMonths, getTimeIntervals, isSameDay } from './helpers.jsx'; -import Link from '../../link.jsx'; +import { addMonths, isSameDay } from './helpers.jsx'; import { reactStringWrap } from "../../../../ui/utils.jsx"; -import { DateTime } from './datetime.jsx'; export default class Recurring extends MegaRenderMixin { static NAMESPACE = 'meetings-recurring'; @@ -661,242 +658,3 @@ export default class Recurring extends MegaRenderMixin { ); } } - -// -- - -export class Edit extends MegaRenderMixin { - occurrenceRef = null; - datepickerRefs = []; - - interval = ChatRoom.SCHEDULED_MEETINGS_INTERVAL; - incomingCallListener = 'onPrepareIncomingCallDialog.recurringEdit'; - - state = { - startDateTime: undefined, - endDateTime: undefined, - isDirty: false, - closeDialog: false, - overlayed: false, - }; - - constructor(props) { - super(props); - - // -- - - const { scheduledMeeting, occurrenceId } = this.props; - this.occurrenceRef = scheduledMeeting.occurrences[occurrenceId]; - if (this.occurrenceRef) { - this.state.startDateTime = this.occurrenceRef.start; - this.state.endDateTime = this.occurrenceRef.end; - } - } - - onStartDateSelect = startDateTime => { - this.setState({ startDateTime, isDirty: true }, () => { - this.datepickerRefs.endDateTime.selectDate(new Date(startDateTime + this.interval)); - }); - }; - - onEndDateSelect = endDateTime => { - this.setState({ endDateTime, isDirty: true }, () => { - const { startDateTime, endDateTime } = this.state; - if (endDateTime < startDateTime) { - if (endDateTime < Date.now()) { - return this.setState({ endDateTime: startDateTime + this.interval }); - } - this.handleTimeSelect({ startDateTime: endDateTime - this.interval }); - } - }); - }; - - // [...] TODO: unify w/ the behavior on `Schedule` re: date/time selection handing - handleTimeSelect = ({ startDateTime, endDateTime }) => { - startDateTime = startDateTime || this.state.startDateTime; - endDateTime = endDateTime || this.state.endDateTime; - this.setState(state => { - return { - startDateTime: endDateTime <= state.startDateTime ? endDateTime - this.interval : startDateTime, - endDateTime: startDateTime >= state.endDateTime ? startDateTime + this.interval : endDateTime, - isDirty: true - }; - }); - }; - - componentWillUnmount() { - super.componentWillUnmount(); - if (this.incomingCallListener) { - megaChat.off(this.incomingCallListener); - } - if ($.dialog === Schedule.dialogName) { - closeDialog(); - } - } - - componentDidMount() { - super.componentDidMount(); - M.safeShowDialog(Schedule.dialogName, () => { - if (!this.isMounted()) { - throw Error(`Edit dialog: component not mounted.`); - } - - megaChat.rebind(this.incomingCallListener, () => { - // If the incoming call dialog will show mark this as overlayed. - if (this.isMounted()) { - this.setState({ overlayed: true, closeDialog: false }); - // Clear when ringing stops. - megaChat.plugins.callManager2.rebind('onRingingStopped.recurringEdit', () => { - megaChat.plugins.callManager2.off('onRingingStopped.recurringEdit'); - this.setState({ overlayed: false }); - fm_showoverlay(); - }); - } - }); - - return $(`#${Schedule.NAMESPACE}`); - }); - } - - componentDidUpdate(prevProps) { - if (prevProps.callExpanded && !this.props.callExpanded) { - if (!$.dialog) { - // The call opening clears $.dialog so since the dialog is still mounted update it. - M.safeShowDialog(Schedule.dialogName, `#${Schedule.NAMESPACE}`); - } - fm_showoverlay(); - this.setState({ closeDialog: false }); - } - if (!prevProps.callExpanded && this.props.callExpanded) { - this.setState({ closeDialog: false }); - } - } - - render() { - const { chatRoom, callExpanded, onClose } = this.props; - const { startDateTime, endDateTime, isDirty, closeDialog, overlayed } = this.state; - - const dialogClasses = ['fluid']; - if (closeDialog) { - dialogClasses.push('with-confirmation-dialog'); - } - if (callExpanded || overlayed) { - dialogClasses.push('hidden'); - } - const withUpgrade = !u_attr.p && endDateTime - startDateTime > 36e5; - if (withUpgrade) { - dialogClasses.push('upgrade'); - } - - return ( - { - return isDirty ? this.setState({ closeDialog: true }) : onClose(); - }}> -
-

{l.edit_meeting_title}

-
-
- -
-
- {reactStringWrap(l.scheduled_edit_occurrence_note, '[A]', Link, { - onClick: () => { - onClose(); - megaChat.trigger(megaChat.plugins.meetingsManager.EVENTS.EDIT, chatRoom); - } - })} -
-
-
- - - - -
- { - this.datepickerRefs.startDateTime = datepicker; - }} - onSelectDate={startDateTime => this.onStartDateSelect(startDateTime)} - onSelectTime={({ value: startDateTime }) => this.handleTimeSelect({ startDateTime })} - onChange={value => this.setState({ startDateTime: value })} - onBlur={timestamp => { - if (timestamp) { - timestamp = timestamp < Date.now() ? this.occurrenceRef.start : timestamp; - this.onStartDateSelect(timestamp); - } - }} - /> - - { - this.datepickerRefs.endDateTime = datepicker; - }} - onSelectDate={endDateTime => this.onEndDateSelect(endDateTime)} - onSelectTime={({ value: endDateTime }) => this.handleTimeSelect({ endDateTime })} - onChange={timestamp => this.setState({ endDateTime: timestamp })} - onBlur={timestamp => timestamp && this.onEndDateSelect(timestamp)} - /> -
-
- - { - withUpgrade && - { - onClose(); - loadSubPage('pro'); - eventlog(500257); - }} - /> - } - -
-
-
- -
-
- - {!(overlayed || callExpanded) && closeDialog && - this.setState({ closeDialog: false })} - onClose={onClose} - /> - } -
- ); - } -} diff --git a/js/chat/ui/meetings/schedule/schedule.jsx b/js/chat/ui/meetings/schedule/schedule.jsx index 1d865dc0a0..19e82d3c74 100644 --- a/js/chat/ui/meetings/schedule/schedule.jsx +++ b/js/chat/ui/meetings/schedule/schedule.jsx @@ -5,16 +5,25 @@ import Button from '../button.jsx'; import { PerfectScrollbar } from '../../../../ui/perfectScrollbar.jsx'; import Invite from './invite.jsx'; import { getTimeIntervals, getNearestHalfHour, getUserTimezone, addMonths } from './helpers.jsx'; -import Recurring from './recurring.jsx'; import { DateTime } from './datetime.jsx'; import { MCO_FLAGS } from '../../../chatRoom.jsx'; import { ParsedHTML } from '../../../../ui/utils'; import { EVENTS, VIEWS } from '../../conversations.jsx'; - -export class Schedule extends MegaRenderMixin { - static NAMESPACE = 'schedule-dialog'; - static dialogName = `meetings-${Schedule.NAMESPACE}`; - +import Recurring from './recurring.jsx'; +import { + Checkbox, + CloseDialog, + Column, + dialogName, + Input, + NAMESPACE, + Row, + Switch, + Textarea, + UpgradeNotice +} from './utils.jsx'; + +export default class Schedule extends MegaRenderMixin { domRef = React.createRef(); scheduledMeetingRef = null; localStreamRef = '.float-video'; @@ -348,11 +357,11 @@ export class Schedule extends MegaRenderMixin { componentWillUnmount() { super.componentWillUnmount(); - if ($.dialog === Schedule.dialogName) { + if ($.dialog === dialogName) { closeDialog(); } - [document, this.localStreamRef].map(el => $(el).unbind(`.${Schedule.NAMESPACE}`)); + [document, this.localStreamRef].map(el => $(el).unbind(`.${NAMESPACE}`)); megaChat.off(this.incomingCallListener); } @@ -386,14 +395,14 @@ export class Schedule extends MegaRenderMixin { closeDialog(); } - M.safeShowDialog(Schedule.dialogName, () => { + M.safeShowDialog(dialogName, () => { if (!this.isMounted()) { - throw new Error(`${Schedule.dialogName} dialog: component ${Schedule.NAMESPACE} not mounted.`); + throw new Error(`${dialogName} dialog: component ${NAMESPACE} not mounted.`); } // Invoke submit on hitting enter, excl. while typing in the `description` text area or // if the confirmation dialog is currently shown - $(document).rebind(`keyup.${Schedule.NAMESPACE}`, ({ keyCode, target }) => { + $(document).rebind(`keyup.${NAMESPACE}`, ({ keyCode, target }) => { return this.state.closeDialog || target instanceof HTMLTextAreaElement ? null : keyCode === 13 /* Enter */ && this.handleSubmit(); @@ -402,7 +411,7 @@ export class Schedule extends MegaRenderMixin { // Clicked on the `Local` component (the call's mini view) while the `Schedule meeting` // dialog is opened -> ask for close confirmation if any changes have been done or close the dialog // immediately - $(this.localStreamRef).rebind(`click.${Schedule.NAMESPACE}`, () => { + $(this.localStreamRef).rebind(`click.${NAMESPACE}`, () => { if (this.state.isDirty) { this.handleToggle('closeDialog'); return false; @@ -423,7 +432,7 @@ export class Schedule extends MegaRenderMixin { } }); - return $(`#${Schedule.NAMESPACE}`); + return $(`#${NAMESPACE}`); }); } @@ -431,7 +440,7 @@ export class Schedule extends MegaRenderMixin { if (prevProps.callExpanded && !this.props.callExpanded) { if (!$.dialog) { // The call opening clears $.dialog so since the dialog is still mounted update it. - M.safeShowDialog(Schedule.dialogName, `#${Schedule.NAMESPACE}`); + M.safeShowDialog(dialogName, `#${NAMESPACE}`); } fm_showoverlay(); this.setState({ closeDialog: false }); @@ -466,12 +475,12 @@ export class Schedule extends MegaRenderMixin { return ( isDirty ? this.handleToggle('closeDialog') : this.props.onClose()}>
{ - return ( - <> - onToggle('closeDialog') }, - { key: 'y', label: l.schedule_discard_confirm, className: 'positive', onClick: onClose } - ]} - noCloseOnClickOutside={true} - stopKeyPropagation={true} - hideOverlay={true} - onClose={() => onToggle('closeDialog')} - /> -
onToggle('closeDialog')} - /> - - ); -}; - -export const Row = ({ children, className }) => -
- {children} -
; - -export const Column = ({ children, className }) => -
- {children} -
; - /** * Header * @param chatRoom @@ -800,192 +756,6 @@ const Header = ({ chatRoom }) => { return $$container(l.schedule_meeting_title); }; -/** - * Input - * @param name - * @param placeholder - * @param value - * @param invalid - * @param invalidMessage - * @param autoFocus - * @param isLoading - * @param onFocus - * @param onChange - * @return {React.Element} - */ - -const Input = ({ name, placeholder, value, invalid, invalidMessage, autoFocus, isLoading, onFocus, onChange }) => { - return ( - - - - - -
- onChange(target.value)} - /> - {invalid && -
- {invalidMessage} -
- } -
-
-
- ); -}; - -/** - * Checkbox - * @param name - * @param className - * @param checked - * @param label - * @param subLabel - * @param onToggle - * @param isLoading - * @return {React.Element} - */ - -const Checkbox = ({ name, className, checked, label, subLabel, isLoading, onToggle }) => { - return ( - - -
- onToggle(name)} - /> -
-
- - - {subLabel &&
{subLabel}
} -
-
- ); -}; - -/** - * Switch - * @param name - * @param toggled - * @param label - * @param isLoading - * @param subLabel - * @param onToggle - * @return {React.Element} - */ - -const Switch = ({ name, toggled, label, isLoading, subLabel, onToggle }) => { - const className = `${Schedule.NAMESPACE}-switch`; - return ( - - - - - - isLoading ? null : onToggle(name)}> - {label} - -
isLoading ? null : onToggle(name)}> -
-
- {subLabel &&
{subLabel}
} - - - ); -}; - -/** - * Textarea - * @param name - * @param placeholder - * @param isLoading - * @param value - * @param invalid - * @param onChange - * @param onFocus - * @return {React.Element} - */ - -const Textarea = ({ name, placeholder, isLoading, value, invalid, onChange, onFocus }) => { - return ( - - - - - -
-