diff --git a/index-dev.html b/index-dev.html index 543b8bbc..ba927e59 100644 --- a/index-dev.html +++ b/index-dev.html @@ -28,38 +28,11 @@ - - - - - - - - - - -
- - - - - - + diff --git a/package.json b/package.json index ca82790e..caa6ff98 100644 --- a/package.json +++ b/package.json @@ -22,9 +22,9 @@ "build:prod": "browserify ./src/index.js --standalone Webapp | uglifyjs --compress --mangle > ./umd/index.prod.js", "build:dev": "browserify ./src/index.js --standalone Webapp > ./umd/index.dev.js", "build:css": "postcss css/base.css > css/base.min.css", - "wp-build:dev": "webpack --mode development --output ./umd/index.dev.js", - "wp-build:prod": "webpack --mode production --output ./umd/index.prod.js", - "vers": "echo \"{\\\"version\\\": \\\"`node -p -e \"require('./package.json').version\"`\\\"}\" > version.json" + "wp-build:dev": "webpack --mode development --output-library-target umd --output ./umd/index.dev.js", + "wp-build:prod": "webpack --mode production --output-library-target umd --output ./umd/index.prod.js", + "vers": "echo \"export const PACKAGE_VERSION = \\\"`node -p -e \"require('./package.json').version\"`\\\";\" > src/version.js" }, "files": [ "src/index.js", diff --git a/service-worker.js b/service-worker.js index 561c84e4..291de24f 100644 --- a/service-worker.js +++ b/service-worker.js @@ -1,7 +1,7 @@ // Must be located at the root. importScripts("https://unpkg.com/firebase@5.5.5/firebase-app.js"); importScripts("https://unpkg.com/firebase@5.5.5/firebase-messaging.js"); -importScripts("/firebase-init.js") +importScripts("/src/firebase-init.js") firebase.initializeApp(FIREBASE_INIT); diff --git a/src/config.js b/src/config.js index 58b97526..cbaa603e 100644 --- a/src/config.js +++ b/src/config.js @@ -1,5 +1,7 @@ +import { PACKAGE_VERSION } from './version.js'; + // Name of this application, used in the User-Agent. -export const APP_NAME = 'TinodeWeb/' + (package_version || '0.15'); +export const APP_NAME = 'TinodeWeb/' + (PACKAGE_VERSION || '0.15'); // API key. Use https://github.com/tinode/chat/tree/master/keygen to generate your own export const API_KEY = 'AQEAAAABAAD_rAp4DJh05a1HAwFT3A6K'; diff --git a/firebase-init.js b/src/firebase-init.js similarity index 100% rename from firebase-init.js rename to src/firebase-init.js diff --git a/src/index.js b/src/index.js index 0f070213..bdde1523 100644 --- a/src/index.js +++ b/src/index.js @@ -3,9 +3,14 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import { IntlProvider } from 'react-intl'; + import TinodeWeb from './views/tinode-web.jsx'; ReactDOM.render( - React.createElement(TinodeWeb, null), + + + , document.getElementById('mountPoint') ); diff --git a/src/lib/host-name.js b/src/lib/host-name.js index 22047f54..294887fc 100644 --- a/src/lib/host-name.js +++ b/src/lib/host-name.js @@ -1,7 +1,7 @@ -import {KNOWN_HOSTS, DEFAULT_HOST} from '../config.js'; +import { KNOWN_HOSTS, DEFAULT_HOST } from '../config.js'; // Detect server address from the URL -function detectServerAddress() { +export function detectServerAddress() { let host = DEFAULT_HOST; if (typeof window.location == 'object') { if (window.location.protocol == 'file:' || window.location.hostname == 'localhost') { @@ -14,14 +14,14 @@ function detectServerAddress() { } // Detect if the page is served over HTTPS. -function isSecureConnection() { +export function isSecureConnection() { if (typeof window.location == 'object') { return window.location.protocol == 'https:'; } return false; } -function isLocalHost() { +export function isLocalHost() { if (typeof window.location == 'object') { return window.location.hostname == 'localhost'; } diff --git a/src/lib/local-storage.js b/src/lib/local-storage.js index e69de29b..9f134872 100644 --- a/src/lib/local-storage.js +++ b/src/lib/local-storage.js @@ -0,0 +1,21 @@ +// Helper functions for storing values in localStorage. +// By default localStorage can store only strings, not objects or other types. + +export default class LocalStorageUtil { + // Replace old object with the new one. + static setObject(key, value) { + localStorage.setItem(key, JSON.stringify(value)); + } + + // Get stored object. + static getObject(key) { + let value = localStorage.getItem(key); + return value && JSON.parse(value); + } + + // Partially or wholly update stored object. + static updateObject(key, value) { + let oldVal = localStorage.getObject(key); + localStorage.setObject(key, Object.assign(oldVal || {}, value)); + } +} diff --git a/src/lib/navigation.js b/src/lib/navigation.js index ecd4d3cc..870aa945 100644 --- a/src/lib/navigation.js +++ b/src/lib/navigation.js @@ -1,67 +1,68 @@ -// Helper functions for hash navigation. +// Utility class for hash navigation. // Parse hash as in http://www.example.com/path#hash as if it were // path and arguments. -export function parseUrlHash(hash) { - // Split path from args, path -> parts[0], args->path[1] - let parts = hash.split('?', 2); - let params = {}; - let path = []; - if (parts[0]) { - path = parts[0].substr(1).split('/'); - } - if (parts[1]) { - parts[1].split('&').forEach(function(part) { - let item = part.split('='); - if (item[0]) { - params[decodeURIComponent(item[0])] = decodeURIComponent(item[1]); - } - }); - } - return {path: path, params: params}; -} - -export function navigateTo(url) { - window.location.hash = url; -} - -export function composeUrlHash(path, params) { - var url = path.join('/'); - var args = []; - for (var key in params) { - if (params.hasOwnProperty(key)) { - args.push(key + '=' + params[key]); +export default class HashNavigation { + static parseUrlHash(hash) { + // Split path from args, path -> parts[0], args->path[1] + let parts = hash.split('?', 2); + let params = {}; + let path = []; + if (parts[0]) { + path = parts[0].substr(1).split('/'); } + if (parts[1]) { + parts[1].split('&').forEach(function(part) { + let item = part.split('='); + if (item[0]) { + params[decodeURIComponent(item[0])] = decodeURIComponent(item[1]); + } + }); + } + return {path: path, params: params}; } - if (args.length > 0) { - url += '?' + args.join('&'); + + static navigateTo(url) { + window.location.hash = url; } - return url; -} -export function addUrlParam(hash, key, value) { - var parsed = parseUrlHash(hash); - parsed.params[key] = value; - return composeUrlHash(parsed.path, parsed.params); -} + static composeUrlHash(path, params) { + var url = path.join('/'); + var args = []; + for (var key in params) { + if (params.hasOwnProperty(key)) { + args.push(key + '=' + params[key]); + } + } + if (args.length > 0) { + url += '?' + args.join('&'); + } + return url; + } -export function removeUrlParam(hash, key) { - var parsed = parseUrlHash(hash); - delete parsed.params[key]; - return composeUrlHash(parsed.path, parsed.params); -} + static addUrlParam(hash, key, value) { + var parsed = parseUrlHash(hash); + parsed.params[key] = value; + return composeUrlHash(parsed.path, parsed.params); + } -export function setUrlSidePanel(hash, sidepanel) { - var parsed = parseUrlHash(hash); - parsed.path[0] = sidepanel; - return composeUrlHash(parsed.path, parsed.params); -} + static removeUrlParam(hash, key) { + var parsed = parseUrlHash(hash); + delete parsed.params[key]; + return composeUrlHash(parsed.path, parsed.params); + } -export function setUrlTopic(hash, topic) { - var parsed = parseUrlHash(hash); - parsed.path[1] = topic; - // Close InfoView on topic change. - delete parsed.params.info; - return composeUrlHash(parsed.path, parsed.params); + static setUrlSidePanel(hash, sidepanel) { + var parsed = parseUrlHash(hash); + parsed.path[0] = sidepanel; + return composeUrlHash(parsed.path, parsed.params); + } + + static setUrlTopic(hash, topic) { + var parsed = parseUrlHash(hash); + parsed.path[1] = topic; + // Close InfoView on topic change. + delete parsed.params.info; + return composeUrlHash(parsed.path, parsed.params); + } } -// END of hash navigation helper functions diff --git a/src/lib/strformat.js b/src/lib/strformat.js index d5e2d4fd..933ff18c 100644 --- a/src/lib/strformat.js +++ b/src/lib/strformat.js @@ -1,5 +1,5 @@ // Short representation of time in the past. -function shortDateFormat(then) { +export function shortDateFormat(then) { var locale = window.navigator.userLanguage || window.navigator.language; var now = new Date(); if (then.getFullYear() == now.getFullYear()) { @@ -15,7 +15,7 @@ function shortDateFormat(then) { } // Convert a number of bytes to human-readable format. -function bytesToHumanSize(bytes) { +export function bytesToHumanSize(bytes) { if (!bytes || bytes == 0) { return '0 Bytes'; } @@ -29,7 +29,7 @@ function bytesToHumanSize(bytes) { // Get 32 bit integer hash value for a string. Ideally it should produce the same value // as Java's String#hash(). -function stringHash(value) { +export function stringHash(value) { var hash = 0; value = '' + value; for (var i = 0; i < value.length; i++) { diff --git a/src/views/login-view.jsx b/src/views/login-view.jsx index 41ec5d8a..aef390fe 100644 --- a/src/views/login-view.jsx +++ b/src/views/login-view.jsx @@ -1,11 +1,13 @@ // Login form import React from 'react'; -import {FormattedMessage} from 'react-intl'; +import { FormattedMessage } from 'react-intl'; import CheckBox from '../widgets/checkbox.jsx'; import VisiblePassword from '../widgets/visible-password.jsx'; +import LocalStorageUtil from '../lib/local-storage.js'; + export default class LoginView extends React.Component { constructor(props) { super(props); @@ -14,7 +16,7 @@ export default class LoginView extends React.Component { login: props.login, password: '', hostName: props.serverAddress, - saveToken: localStorage.getObject('keep-logged-in') + saveToken: LocalStorageUtil.getObject('keep-logged-in') }; this.handleLoginChange = this.handleLoginChange.bind(this); this.handlePasswordChange = this.handlePasswordChange.bind(this); @@ -31,7 +33,7 @@ export default class LoginView extends React.Component { } handleToggleSaveToken() { - localStorage.setObject('keep-logged-in', !this.state.saveToken); + LocalStorageUtil.setObject('keep-logged-in', !this.state.saveToken); this.setState({saveToken: !this.state.saveToken}); } diff --git a/src/views/logo-view.jsx b/src/views/logo-view.jsx index ac7c37bb..ea4300f6 100644 --- a/src/views/logo-view.jsx +++ b/src/views/logo-view.jsx @@ -1,7 +1,10 @@ /* This is just a static page to display when no conversation is selected. */ - import React from 'react'; +import Tinode from 'tinode-sdk'; + +import { APP_NAME } from '../config.js'; + export default class LogoView extends React.PureComponent { render() { var version = APP_NAME + ' (' + Tinode.getLibrary() + ')'; diff --git a/src/views/password-reset-view.jsx b/src/views/password-reset-view.jsx index 0ee6606e..38ff9946 100644 --- a/src/views/password-reset-view.jsx +++ b/src/views/password-reset-view.jsx @@ -1,5 +1,4 @@ /* A password reset form */ - import React from 'react'; import VisiblePassword from '../widgets/visible-password.jsx'; diff --git a/src/views/tinode-web.jsx b/src/views/tinode-web.jsx index 39dfdb62..1632564a 100644 --- a/src/views/tinode-web.jsx +++ b/src/views/tinode-web.jsx @@ -3,7 +3,9 @@ import React from 'react'; import 'firebase/app'; import 'firebase/messaging'; -import { FIREBASE_INIT } from '../../firebase-init.js' +import { FIREBASE_INIT } from '../firebase-init.js' + +import Tinode from 'tinode-sdk'; import ContextMenu from '../widgets/context-menu.jsx'; @@ -11,6 +13,11 @@ import InfoView from './info-view.jsx'; import MessagesView from './messages-view.jsx'; import SidepanelView from './sidepanel-view.jsx'; +import { API_KEY, APP_NAME, MEDIA_BREAKPOINT, RECEIVED_DELAY } from '../config.js'; +import { isLocalHost, isSecureConnection } from '../lib/host-name.js'; +import LocalStorageUtil from '../lib/local-storage.js'; +import HashNavigation from '../lib/navigation.js'; + // Sound to play on message received. export const POP_SOUND = new Audio('audio/msg.mp3'); @@ -76,7 +83,7 @@ export default class TinodeWeb extends React.Component { } getBlankState() { - let settings = localStorage.getObject("settings") || {}; + let settings = LocalStorageUtil.getObject("settings") || {}; return { connected: false, @@ -89,7 +96,7 @@ export default class TinodeWeb extends React.Component { desktopAlertsEnabled: (isSecureConnection() || isLocalHost()) && (typeof firebase != 'undefined') && (typeof navigator != 'undefined') && (typeof FIREBASE_INIT != 'undefined'), - firebaseToken: localStorage.getObject("firebase-token"), + firebaseToken: LocalStorageUtil.getObject("firebase-token"), errorText: '', errorLevel: null, @@ -169,10 +176,10 @@ export default class TinodeWeb extends React.Component { } } - let token = localStorage.getObject('keep-logged-in') ? - localStorage.getObject('auth-token') : undefined; + let token = LocalStorageUtil.getObject('keep-logged-in') ? + LocalStorageUtil.getObject('auth-token') : undefined; - let parsedNav = parseUrlHash(window.location.hash); + let parsedNav = HashNavigation.parseUrlHash(window.location.hash); if (token) { // When reading from storage, date is returned as string. token.expires = new Date(token.expires); @@ -184,9 +191,9 @@ export default class TinodeWeb extends React.Component { delete parsedNav.params.info; delete parsedNav.params.tab; parsedNav.path[0] = ''; - navigateTo(composeUrlHash(parsedNav.path, parsedNav.params)); + HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsedNav.path, parsedNav.params)); } else if (!parsedNav.params.token) { - navigateTo(''); + HashNavigation.navigateTo(''); } this.readTimer = null; @@ -218,7 +225,7 @@ export default class TinodeWeb extends React.Component { // Handle for hashchange event: display appropriate panels. handleHashRoute() { - var hash = parseUrlHash(window.location.hash); + var hash = HashNavigation.parseUrlHash(window.location.hash); if (hash.path && hash.path.length > 0) { // Left-side panel selector. if (['register','settings','edit','cred','reset','newtpk','contacts',''].includes(hash.path[0])) { @@ -315,7 +322,7 @@ export default class TinodeWeb extends React.Component { doLogin(login, password, cred) { if (this.tinode.isAuthenticated()) { // Already logged in. Go to default screen. - navigateTo(""); + HashNavigation.navigateTo(''); return; } // Sanitize and package credentail. @@ -344,30 +351,30 @@ export default class TinodeWeb extends React.Component { // Login failed, report error. this.setState({loginDisabled: false, credMethod: undefined, credCode: undefined}); this.handleError(err.message, 'err'); - localStorage.removeItem('auth-token'); - navigateTo(''); + LocalStorageUtil.removeItem('auth-token'); + HashNavigation.navigateTo(''); }); } else { // No login credentials provided. // Make sure we are on the login page. - navigateTo(''); + HashNavigation.navigateTo(''); this.setState({loginDisabled: false}); } } handleCredentialsRequest(params) { - var parsed = parseUrlHash(window.location.hash); + var parsed = HashNavigation.parseUrlHash(window.location.hash); parsed.path[0] = 'cred'; parsed.params['method'] = params.cred[0]; - navigateTo(composeUrlHash(parsed.path, parsed.params)); + HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsed.path, parsed.params)); } handleLoginSuccessful() { this.handleError('', null); // Refresh authentication token. - if (localStorage.getObject('keep-logged-in')) { - localStorage.setObject('auth-token', this.tinode.getAuthToken()); + if (LocalStorageUtil.getObject('keep-logged-in')) { + LocalStorageUtil.setObject('auth-token', this.tinode.getAuthToken()); } // Logged in fine, subscribe to 'me' attaching callbacks from the contacts view. var me = this.tinode.getMeTopic(); @@ -387,11 +394,11 @@ export default class TinodeWeb extends React.Component { withDesc(). build() ).catch((err) => { - localStorage.removeItem('auth-token'); + LocalStorageUtil.removeItem('auth-token'); this.handleError(err.message, 'err'); - navigateTo(''); + HashNavigation.navigateTo(''); }); - navigateTo(setUrlSidePanel(window.location.hash, 'contacts')); + HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, 'contacts')); } handleDisconnect(err) { @@ -575,7 +582,7 @@ export default class TinodeWeb extends React.Component { topicSelectedOnline: online, topicSelectedAcs: acs }); - navigateTo(setUrlTopic('', topicName)); + HashNavigation.navigateTo(HashNavigation.setUrlTopic('', topicName)); } } else { // Currently selected contact deleted @@ -588,7 +595,7 @@ export default class TinodeWeb extends React.Component { showInfoPanel: false }); if (this.state.topicSelected) { - navigateTo(setUrlTopic('', null)); + HashNavigation.navigateTo(HashNavigation.setUrlTopic('', null)); } } } @@ -598,7 +605,7 @@ export default class TinodeWeb extends React.Component { this.setState({ mobilePanel: 'sidepanel' }); - navigateTo(setUrlTopic(window.location.hash, null)); + HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, null)); } // User is sending a message, either plain text or a drafty object, possibly @@ -631,7 +638,7 @@ export default class TinodeWeb extends React.Component { // User chose a Sign Up menu item. handleNewAccount() { - navigateTo(setUrlSidePanel(window.location.hash, 'register')); + HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, 'register')); } // Actual registration of a new account. @@ -673,7 +680,8 @@ export default class TinodeWeb extends React.Component { // User chose Settings menu item. handleSettings() { - navigateTo(setUrlSidePanel(window.location.hash, this.state.myUserId ? 'edit' : 'settings')); + HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, + this.state.myUserId ? 'edit' : 'settings')); } // User updated global parameters. @@ -692,12 +700,12 @@ export default class TinodeWeb extends React.Component { serverAddress: serverAddress, transport: transport }); - localStorage.setObject('settings', { + LocalStorageUtil.setObject('settings', { serverAddress: serverAddress, transport: transport }); - navigateTo(setUrlSidePanel(window.location.hash, '')); + HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, '')); } // Initialize desktop alerts = push notifications. @@ -722,24 +730,24 @@ export default class TinodeWeb extends React.Component { }).catch((err) => { this.handleError(err.message, 'err'); this.setState({desktopAlerts: false, firebaseToken: null}); - localStorage.updateObject('settings', {desktopAlerts: false}); + LocalStorageUtil.updateObject('settings', {desktopAlerts: false}); console.log("Failed to get permission to notify.", err); }); } else { this.setState({desktopAlerts: true}); - localStorage.updateObject('settings', {desktopAlerts: true}); + LocalStorageUtil.updateObject('settings', {desktopAlerts: true}); } } else if (this.state.firebaseToken) { this.fbPush.deleteToken(this.state.firebaseToken).catch((err) => { console.log("Unable to delete token.", err); }).finally(() => { - localStorage.updateObject('settings', {desktopAlerts: false}); - localStorage.removeItem('firebase-token'); + LocalStorageUtil.updateObject('settings', {desktopAlerts: false}); + LocalStorageUtil.removeItem('firebase-token'); this.setState({desktopAlerts: false, firebaseToken: null}); }); } else { this.setState({desktopAlerts: false, firebaseToken: null}); - localStorage.updateObject('settings', {desktopAlerts: false}); + LocalStorageUtil.updateObject('settings', {desktopAlerts: false}); } } @@ -747,10 +755,10 @@ export default class TinodeWeb extends React.Component { this.fbPush.getToken().then((refreshedToken) => { if (refreshedToken != this.state.firebaseToken) { this.tinode.setDeviceToken(refreshedToken, sendToServer); - localStorage.setObject('firebase-token', refreshedToken); + LocalStorageUtil.setObject('firebase-token', refreshedToken); } this.setState({firebaseToken: refreshedToken, desktopAlerts: true}); - localStorage.updateObject('settings', {desktopAlerts: true}); + LocalStorageUtil.updateObject('settings', {desktopAlerts: true}); }).catch((err) => { this.handleError(err.message, 'err'); console.log("Failed to retrieve firebase token", err); @@ -759,27 +767,27 @@ export default class TinodeWeb extends React.Component { handleToggleMessageSounds(enabled) { this.setState({messageSounds: enabled}); - localStorage.updateObject('settings', { + LocalStorageUtil.updateObject('settings', { messageSoundsOff: !enabled }); } // User clicked Cancel button in Setting or Sign Up panel. handleSidepanelCancel() { - var parsed = parseUrlHash(window.location.hash); + var parsed = HashNavigation.parseUrlHash(window.location.hash); parsed.path[0] = this.state.myUserId ? 'contacts' : ''; if (parsed.params) { delete parsed.params.code; delete parsed.params.method; delete parsed.params.tab; } - navigateTo(composeUrlHash(parsed.path, parsed.params)); + HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsed.path, parsed.params)); this.setState({errorText: '', errorLevel: null}); } // User clicked a (+) menu item. handleNewTopic() { - navigateTo(setUrlSidePanel(window.location.hash, 'newtpk')); + HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, 'newtpk')); } // Request to start a new topic. New P2P topic requires peer's name. @@ -801,7 +809,9 @@ export default class TinodeWeb extends React.Component { // If the current URl contains the old topic name, replace it with new. // Update the name of the selected topic first so the navigator doen't clear // the state. - this.setState({topicSelected: newName}, () => { navigateTo(setUrlTopic('', newName)); }); + this.setState({topicSelected: newName}, () => { + HashNavigation.navigateTo(HashNavigation.setUrlTopic('', newName)); + }); } } @@ -853,7 +863,7 @@ export default class TinodeWeb extends React.Component { handleLogout() { updateFavicon(0); - localStorage.removeItem('auth-token'); + LocalStorageUtil.removeItem('auth-token'); if (this.tinode) { this.tinode.onDisconnect = undefined; this.tinode.disconnect(); @@ -862,7 +872,7 @@ export default class TinodeWeb extends React.Component { this.tinode = TinodeWeb.tnSetup(this.state.serverAddress, this.state.transport); this.tinode.onConnect = this.handleConnected; this.tinode.onDisconnect = this.handleDisconnect; - navigateTo(''); + HashNavigation.navigateTo(''); } handleLeaveUnsubRequest(topicName) { @@ -873,7 +883,7 @@ export default class TinodeWeb extends React.Component { topic.leave(true).then((ctrl) => { // Hide MessagesView and InfoView panels. - navigateTo(setUrlTopic(window.location.hash, '')); + HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, '')); }).catch((err) => { this.handleError(err.message, 'err'); }); @@ -926,12 +936,12 @@ export default class TinodeWeb extends React.Component { } handleShowInfoView() { - navigateTo(addUrlParam(window.location.hash, 'info', true)); + HashNavigation.navigateTo(HashNavigation.addUrlParam(window.location.hash, 'info', true)); this.setState({showInfoPanel: true}); } handleHideInfoView() { - navigateTo(removeUrlParam(window.location.hash, 'info')); + HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'info')); this.setState({showInfoPanel: false}); } diff --git a/umd/index.dev.js b/umd/index.dev.js index 9939d29d..bf03f47e 100644 --- a/umd/index.dev.js +++ b/umd/index.dev.js @@ -1,4 +1,14 @@ -/******/ (function(modules) { // webpackBootstrap +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else { + var a = factory(); + for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i]; + } +})(window, function() { +return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ @@ -86,18 +96,6 @@ /************************************************************************/ /******/ ({ -/***/ "./firebase-init.js": -/*!**************************!*\ - !*** ./firebase-init.js ***! - \**************************/ -/*! exports provided: FIREBASE_INIT */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"FIREBASE_INIT\", function() { return FIREBASE_INIT; });\n// Get these values for your project from the https://console.firebase.google.com/\nconst FIREBASE_INIT = {\n // Project Settings -> Cloud Messaging, \"Sender ID\".\n messagingSenderId: \"114126160546\",\n // Project Settings -> Cloud Messaging -> Web configuration -> Web Push certificates.\n // This value IS NOT included in the generated js config you can download from\n // the firebase console. It needs to be added separately.\n messagingVapidKey: \"BOgQVPOMzIMXUpsYGpbVkZoEBc0ifKY_f2kSU5DNDGYI6i6CoKqqxDd7w7PJ3FaGRBgVGJffldETumOx831jl58\"\n};\n\n\n//# sourceURL=webpack:///./firebase-init.js?"); - -/***/ }), - /***/ "./node_modules/@firebase/app/dist/index.cjs.js": /*!******************************************************!*\ !*** ./node_modules/@firebase/app/dist/index.cjs.js ***! @@ -655,6 +653,30 @@ eval("var g;\n\n// This works in non-strict mode\ng = (function() {\n\treturn th /***/ }), +/***/ "./src/config.js": +/*!***********************!*\ + !*** ./src/config.js ***! + \***********************/ +/*! exports provided: APP_NAME, API_KEY, KNOWN_HOSTS, DEFAULT_HOST, KEYPRESS_DELAY, RECEIVED_DELAY, READ_DELAY, MIN_TAG_LENGTH, MEDIA_BREAKPOINT, REM_SIZE, AVATAR_SIZE, MESSAGES_PAGE, MAX_INBAND_ATTACHMENT_SIZE, MAX_EXTERN_ATTACHMENT_SIZE, MAX_IMAGE_DIM, SUPPORTED_IMAGE_FORMATS, MIME_EXTENSIONS */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"APP_NAME\", function() { return APP_NAME; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"API_KEY\", function() { return API_KEY; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"KNOWN_HOSTS\", function() { return KNOWN_HOSTS; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"DEFAULT_HOST\", function() { return DEFAULT_HOST; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"KEYPRESS_DELAY\", function() { return KEYPRESS_DELAY; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"RECEIVED_DELAY\", function() { return RECEIVED_DELAY; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"READ_DELAY\", function() { return READ_DELAY; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"MIN_TAG_LENGTH\", function() { return MIN_TAG_LENGTH; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"MEDIA_BREAKPOINT\", function() { return MEDIA_BREAKPOINT; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"REM_SIZE\", function() { return REM_SIZE; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"AVATAR_SIZE\", function() { return AVATAR_SIZE; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"MESSAGES_PAGE\", function() { return MESSAGES_PAGE; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"MAX_INBAND_ATTACHMENT_SIZE\", function() { return MAX_INBAND_ATTACHMENT_SIZE; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"MAX_EXTERN_ATTACHMENT_SIZE\", function() { return MAX_EXTERN_ATTACHMENT_SIZE; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"MAX_IMAGE_DIM\", function() { return MAX_IMAGE_DIM; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"SUPPORTED_IMAGE_FORMATS\", function() { return SUPPORTED_IMAGE_FORMATS; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"MIME_EXTENSIONS\", function() { return MIME_EXTENSIONS; });\n/* harmony import */ var _version_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./version.js */ \"./src/version.js\");\n // Name of this application, used in the User-Agent.\n\nvar APP_NAME = 'TinodeWeb/' + (_version_js__WEBPACK_IMPORTED_MODULE_0__[\"PACKAGE_VERSION\"] || '0.15'); // API key. Use https://github.com/tinode/chat/tree/master/keygen to generate your own\n\nvar API_KEY = 'AQEAAAABAAD_rAp4DJh05a1HAwFT3A6K'; // The array of possible hosts to connect to.\n\nvar KNOWN_HOSTS = {\n hosted: 'api.tinode.co',\n local: 'localhost:6060'\n}; // Default host name and port to connect to.\n\nvar DEFAULT_HOST = KNOWN_HOSTS.hosted; // Minimum time between two keypress notifications, milliseconds.\n\nvar KEYPRESS_DELAY = 3 * 1000; // Delay before sending a {note} for reciving a message, milliseconds.\n\nvar RECEIVED_DELAY = 500; // Delay before sending a read notification, milliseconds.\n\nvar READ_DELAY = 1000; // The shortest allowed tag length. Matches the value on the server.\n\nvar MIN_TAG_LENGTH = 4; // Mediaquery breakpoint between desktop and mobile, in px. Should match the value\n// in @media (max-size: 640px) in base.css\n\nvar MEDIA_BREAKPOINT = 640; // Size of css 'rem' unit in pixels. Default 1rem = 10pt = 13px.\n\nvar REM_SIZE = 13; // Size of the avatar image: When an avatar image is uploaded, it's resized to\n// a square of this size.\n\nvar AVATAR_SIZE = 128; // Number of chat messages to fetch in one call.\n\nvar MESSAGES_PAGE = 24; // Maximum in-band (included directly into the message) attachment size which fits into\n// a message of 256K in size, assuming base64 encoding and 1024 bytes of overhead.\n// This is size of an object *before* base64 encoding is applied.\n// Increase this limit to a greater value in production, if desired. Also increase\n// max_message_size in server config.\n// MAX_INBAND_ATTACHMENT_SIZE = base64DecodedLen(max_message_size - overhead);\n\nvar MAX_INBAND_ATTACHMENT_SIZE = 195840; // Absolute maximum attachment size to be used with the server = 8MB. Increase to\n// something like 100MB in production.\n\nvar MAX_EXTERN_ATTACHMENT_SIZE = 1 << 23; // Maximum allowed linear dimension of an inline image in pixels. You may want\n// to adjust it to 1600 or 2400 for production.\n\nvar MAX_IMAGE_DIM = 768; // Supported image MIME types and corresponding file extensions.\n\nvar SUPPORTED_IMAGE_FORMATS = ['image/jpeg', 'image/gif', 'image/png', 'image/svg', 'image/svg+xml'];\nvar MIME_EXTENSIONS = ['jpg', 'gif', 'png', 'svg', 'svg'];\n\n//# sourceURL=webpack:///./src/config.js?"); + +/***/ }), + +/***/ "./src/firebase-init.js": +/*!******************************!*\ + !*** ./src/firebase-init.js ***! + \******************************/ +/*! exports provided: FIREBASE_INIT */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"FIREBASE_INIT\", function() { return FIREBASE_INIT; });\n// Get these values for your project from the https://console.firebase.google.com/\nvar FIREBASE_INIT = {\n // Project Settings -> Cloud Messaging, \"Sender ID\".\n messagingSenderId: \"114126160546\",\n // Project Settings -> Cloud Messaging -> Web configuration -> Web Push certificates.\n // This value IS NOT included in the generated js config you can download from\n // the firebase console. It needs to be added separately.\n messagingVapidKey: \"BOgQVPOMzIMXUpsYGpbVkZoEBc0ifKY_f2kSU5DNDGYI6i6CoKqqxDd7w7PJ3FaGRBgVGJffldETumOx831jl58\"\n};\n\n//# sourceURL=webpack:///./src/firebase-init.js?"); + +/***/ }), + /***/ "./src/index.js": /*!**********************!*\ !*** ./src/index.js ***! @@ -663,7 +685,55 @@ eval("var g;\n\n// This works in non-strict mode\ng = (function() {\n\treturn th /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-dom */ \"./node_modules/react-dom/index.js\");\n/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var _views_tinode_web_jsx__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./views/tinode-web.jsx */ \"./src/views/tinode-web.jsx\");\n// Put all packages together.\n// Used to generate umd/index.prod.js\n\n\n\nreact_dom__WEBPACK_IMPORTED_MODULE_1___default.a.render(react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_views_tinode_web_jsx__WEBPACK_IMPORTED_MODULE_2__[\"default\"], null), document.getElementById('mountPoint'));\n\n//# sourceURL=webpack:///./src/index.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-dom */ \"./node_modules/react-dom/index.js\");\n/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! react-intl */ \"./node_modules/react-intl/lib/index.es.js\");\n/* harmony import */ var _views_tinode_web_jsx__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./views/tinode-web.jsx */ \"./src/views/tinode-web.jsx\");\n// Put all packages together.\n// Used to generate umd/index.prod.js\n\n\n\n\nreact_dom__WEBPACK_IMPORTED_MODULE_1___default.a.render(react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_2__[\"IntlProvider\"], {\n locale: navigator.language\n}, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_views_tinode_web_jsx__WEBPACK_IMPORTED_MODULE_3__[\"default\"], null)), document.getElementById('mountPoint'));\n\n//# sourceURL=webpack:///./src/index.js?"); + +/***/ }), + +/***/ "./src/lib/host-name.js": +/*!******************************!*\ + !*** ./src/lib/host-name.js ***! + \******************************/ +/*! exports provided: detectServerAddress, isSecureConnection, isLocalHost */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"detectServerAddress\", function() { return detectServerAddress; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"isSecureConnection\", function() { return isSecureConnection; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"isLocalHost\", function() { return isLocalHost; });\n/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../config.js */ \"./src/config.js\");\nfunction _typeof(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n // Detect server address from the URL\n\nfunction detectServerAddress() {\n var host = _config_js__WEBPACK_IMPORTED_MODULE_0__[\"DEFAULT_HOST\"];\n\n if (_typeof(window.location) == 'object') {\n if (window.location.protocol == 'file:' || window.location.hostname == 'localhost') {\n host = _config_js__WEBPACK_IMPORTED_MODULE_0__[\"KNOWN_HOSTS\"].local;\n } else if (window.location.hostname) {\n host = window.location.hostname + (window.location.port ? ':' + window.location.port : '');\n }\n }\n\n return host;\n} // Detect if the page is served over HTTPS.\n\nfunction isSecureConnection() {\n if (_typeof(window.location) == 'object') {\n return window.location.protocol == 'https:';\n }\n\n return false;\n}\nfunction isLocalHost() {\n if (_typeof(window.location) == 'object') {\n return window.location.hostname == 'localhost';\n }\n\n return false;\n}\n\n//# sourceURL=webpack:///./src/lib/host-name.js?"); + +/***/ }), + +/***/ "./src/lib/local-storage.js": +/*!**********************************!*\ + !*** ./src/lib/local-storage.js ***! + \**********************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return LocalStorageUtil; });\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\n// Helper functions for storing values in localStorage.\n// By default localStorage can store only strings, not objects or other types.\nvar LocalStorageUtil =\n/*#__PURE__*/\nfunction () {\n function LocalStorageUtil() {\n _classCallCheck(this, LocalStorageUtil);\n }\n\n _createClass(LocalStorageUtil, null, [{\n key: \"setObject\",\n // Replace old object with the new one.\n value: function setObject(key, value) {\n localStorage.setItem(key, JSON.stringify(value));\n } // Get stored object.\n\n }, {\n key: \"getObject\",\n value: function getObject(key) {\n var value = localStorage.getItem(key);\n return value && JSON.parse(value);\n } // Partially or wholly update stored object.\n\n }, {\n key: \"updateObject\",\n value: function updateObject(key, value) {\n var oldVal = localStorage.getObject(key);\n localStorage.setObject(key, Object.assign(oldVal || {}, value));\n }\n }]);\n\n return LocalStorageUtil;\n}();\n\n\n\n//# sourceURL=webpack:///./src/lib/local-storage.js?"); + +/***/ }), + +/***/ "./src/lib/navigation.js": +/*!*******************************!*\ + !*** ./src/lib/navigation.js ***! + \*******************************/ +/*! exports provided: default */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return HashNavigation; });\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\n// Utility class for hash navigation.\n// Parse hash as in http://www.example.com/path#hash as if it were\n// path and arguments.\nvar HashNavigation =\n/*#__PURE__*/\nfunction () {\n function HashNavigation() {\n _classCallCheck(this, HashNavigation);\n }\n\n _createClass(HashNavigation, null, [{\n key: \"parseUrlHash\",\n value: function parseUrlHash(hash) {\n // Split path from args, path -> parts[0], args->path[1]\n var parts = hash.split('?', 2);\n var params = {};\n var path = [];\n\n if (parts[0]) {\n path = parts[0].substr(1).split('/');\n }\n\n if (parts[1]) {\n parts[1].split('&').forEach(function (part) {\n var item = part.split('=');\n\n if (item[0]) {\n params[decodeURIComponent(item[0])] = decodeURIComponent(item[1]);\n }\n });\n }\n\n return {\n path: path,\n params: params\n };\n }\n }, {\n key: \"navigateTo\",\n value: function navigateTo(url) {\n window.location.hash = url;\n }\n }, {\n key: \"composeUrlHash\",\n value: function composeUrlHash(path, params) {\n var url = path.join('/');\n var args = [];\n\n for (var key in params) {\n if (params.hasOwnProperty(key)) {\n args.push(key + '=' + params[key]);\n }\n }\n\n if (args.length > 0) {\n url += '?' + args.join('&');\n }\n\n return url;\n }\n }, {\n key: \"addUrlParam\",\n value: function addUrlParam(hash, key, value) {\n var parsed = parseUrlHash(hash);\n parsed.params[key] = value;\n return composeUrlHash(parsed.path, parsed.params);\n }\n }, {\n key: \"removeUrlParam\",\n value: function removeUrlParam(hash, key) {\n var parsed = parseUrlHash(hash);\n delete parsed.params[key];\n return composeUrlHash(parsed.path, parsed.params);\n }\n }, {\n key: \"setUrlSidePanel\",\n value: function setUrlSidePanel(hash, sidepanel) {\n var parsed = parseUrlHash(hash);\n parsed.path[0] = sidepanel;\n return composeUrlHash(parsed.path, parsed.params);\n }\n }, {\n key: \"setUrlTopic\",\n value: function setUrlTopic(hash, topic) {\n var parsed = parseUrlHash(hash);\n parsed.path[1] = topic; // Close InfoView on topic change.\n\n delete parsed.params.info;\n return composeUrlHash(parsed.path, parsed.params);\n }\n }]);\n\n return HashNavigation;\n}();\n\n\n\n//# sourceURL=webpack:///./src/lib/navigation.js?"); + +/***/ }), + +/***/ "./src/version.js": +/*!************************!*\ + !*** ./src/version.js ***! + \************************/ +/*! exports provided: PACKAGE_VERSION */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"PACKAGE_VERSION\", function() { return PACKAGE_VERSION; });\nvar PACKAGE_VERSION = \"0.15.8-rc1\";\n\n//# sourceURL=webpack:///./src/version.js?"); /***/ }), @@ -723,7 +793,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) * /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return LoginView; });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-intl */ \"./node_modules/react-intl/lib/index.es.js\");\n/* harmony import */ var _widgets_checkbox_jsx__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../widgets/checkbox.jsx */ \"./src/widgets/checkbox.jsx\");\n/* harmony import */ var _widgets_visible_password_jsx__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../widgets/visible-password.jsx */ \"./src/widgets/visible-password.jsx\");\nfunction _typeof(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\n// Login form\n\n\n\n\n\nvar LoginView =\n/*#__PURE__*/\nfunction (_React$Component) {\n _inherits(LoginView, _React$Component);\n\n function LoginView(props) {\n var _this;\n\n _classCallCheck(this, LoginView);\n\n _this = _possibleConstructorReturn(this, _getPrototypeOf(LoginView).call(this, props));\n _this.state = {\n login: props.login,\n password: '',\n hostName: props.serverAddress,\n saveToken: localStorage.getObject('keep-logged-in')\n };\n _this.handleLoginChange = _this.handleLoginChange.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handlePasswordChange = _this.handlePasswordChange.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleToggleSaveToken = _this.handleToggleSaveToken.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleSubmit = _this.handleSubmit.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n return _this;\n }\n\n _createClass(LoginView, [{\n key: \"handleLoginChange\",\n value: function handleLoginChange(e) {\n this.setState({\n login: e.target.value\n });\n }\n }, {\n key: \"handlePasswordChange\",\n value: function handlePasswordChange(e) {\n this.setState({\n password: e.target.value\n });\n }\n }, {\n key: \"handleToggleSaveToken\",\n value: function handleToggleSaveToken() {\n localStorage.setObject('keep-logged-in', !this.state.saveToken);\n this.setState({\n saveToken: !this.state.saveToken\n });\n }\n }, {\n key: \"handleSubmit\",\n value: function handleSubmit(e) {\n e.preventDefault();\n this.props.onLogin(this.state.login.trim(), this.state.password.trim());\n }\n }, {\n key: \"render\",\n value: function render() {\n var _this2 = this;\n\n var submitClasses = 'blue';\n\n if (this.props.disabled) {\n submitClasses += ' disabled';\n }\n\n return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"form\", {\n id: \"login-form\",\n onSubmit: this.handleSubmit\n }, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__[\"FormattedMessage\"], {\n id: \"login.login_prompt\",\n defaultMessage: \"Login\"\n }, function (login_prompt) {\n return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"input\", {\n type: \"text\",\n id: \"inputLogin\",\n placeholder: login_prompt,\n autoComplete: \"username\",\n value: _this2.state.login,\n onChange: _this2.handleLoginChange,\n required: true,\n autoFocus: true\n });\n }), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__[\"FormattedMessage\"], {\n id: \"login.password_prompt\",\n defaultMessage: \"Password\"\n }, function (password_prompt) {\n return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_widgets_visible_password_jsx__WEBPACK_IMPORTED_MODULE_3__[\"default\"], {\n type: \"password\",\n id: \"inputPassword\",\n placeholder: password_prompt,\n autoComplete: \"current-password\",\n value: _this2.state.password,\n onChange: _this2.handlePasswordChange,\n required: true\n });\n }), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"div\", {\n className: \"panel-form-row\"\n }, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_widgets_checkbox_jsx__WEBPACK_IMPORTED_MODULE_2__[\"default\"], {\n id: \"save-token\",\n name: \"save-token\",\n checked: this.state.saveToken,\n onChange: this.handleToggleSaveToken\n }), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"label\", {\n htmlFor: \"save-token\"\n }, \"\\xA0\", react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__[\"FormattedMessage\"], {\n id: \"login.stay_logged_in\",\n defaultMessage: \"Stay logged in\"\n })), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"a\", {\n href: \"#reset\"\n }, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__[\"FormattedMessage\"], {\n id: \"login.forgot_password_link\",\n defaultMessage: \"Forgot password?\"\n }))), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"div\", {\n className: \"dialog-buttons\"\n }, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"button\", {\n className: submitClasses,\n type: \"submit\"\n }, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__[\"FormattedMessage\"], {\n id: \"login.sign_in_button\",\n defaultMessage: \"Sign in\"\n }))));\n }\n }]);\n\n return LoginView;\n}(react__WEBPACK_IMPORTED_MODULE_0___default.a.Component);\n\n\n;\n/* END Login */\n\n//# sourceURL=webpack:///./src/views/login-view.jsx?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return LoginView; });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var react_intl__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! react-intl */ \"./node_modules/react-intl/lib/index.es.js\");\n/* harmony import */ var _widgets_checkbox_jsx__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../widgets/checkbox.jsx */ \"./src/widgets/checkbox.jsx\");\n/* harmony import */ var _widgets_visible_password_jsx__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../widgets/visible-password.jsx */ \"./src/widgets/visible-password.jsx\");\n/* harmony import */ var _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../lib/local-storage.js */ \"./src/lib/local-storage.js\");\nfunction _typeof(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\n// Login form\n\n\n\n\n\n\nvar LoginView =\n/*#__PURE__*/\nfunction (_React$Component) {\n _inherits(LoginView, _React$Component);\n\n function LoginView(props) {\n var _this;\n\n _classCallCheck(this, LoginView);\n\n _this = _possibleConstructorReturn(this, _getPrototypeOf(LoginView).call(this, props));\n _this.state = {\n login: props.login,\n password: '',\n hostName: props.serverAddress,\n saveToken: _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_4__[\"default\"].getObject('keep-logged-in')\n };\n _this.handleLoginChange = _this.handleLoginChange.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handlePasswordChange = _this.handlePasswordChange.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleToggleSaveToken = _this.handleToggleSaveToken.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleSubmit = _this.handleSubmit.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n return _this;\n }\n\n _createClass(LoginView, [{\n key: \"handleLoginChange\",\n value: function handleLoginChange(e) {\n this.setState({\n login: e.target.value\n });\n }\n }, {\n key: \"handlePasswordChange\",\n value: function handlePasswordChange(e) {\n this.setState({\n password: e.target.value\n });\n }\n }, {\n key: \"handleToggleSaveToken\",\n value: function handleToggleSaveToken() {\n _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_4__[\"default\"].setObject('keep-logged-in', !this.state.saveToken);\n this.setState({\n saveToken: !this.state.saveToken\n });\n }\n }, {\n key: \"handleSubmit\",\n value: function handleSubmit(e) {\n e.preventDefault();\n this.props.onLogin(this.state.login.trim(), this.state.password.trim());\n }\n }, {\n key: \"render\",\n value: function render() {\n var _this2 = this;\n\n var submitClasses = 'blue';\n\n if (this.props.disabled) {\n submitClasses += ' disabled';\n }\n\n return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"form\", {\n id: \"login-form\",\n onSubmit: this.handleSubmit\n }, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__[\"FormattedMessage\"], {\n id: \"login.login_prompt\",\n defaultMessage: \"Login\"\n }, function (login_prompt) {\n return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"input\", {\n type: \"text\",\n id: \"inputLogin\",\n placeholder: login_prompt,\n autoComplete: \"username\",\n value: _this2.state.login,\n onChange: _this2.handleLoginChange,\n required: true,\n autoFocus: true\n });\n }), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__[\"FormattedMessage\"], {\n id: \"login.password_prompt\",\n defaultMessage: \"Password\"\n }, function (password_prompt) {\n return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_widgets_visible_password_jsx__WEBPACK_IMPORTED_MODULE_3__[\"default\"], {\n type: \"password\",\n id: \"inputPassword\",\n placeholder: password_prompt,\n autoComplete: \"current-password\",\n value: _this2.state.password,\n onChange: _this2.handlePasswordChange,\n required: true\n });\n }), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"div\", {\n className: \"panel-form-row\"\n }, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_widgets_checkbox_jsx__WEBPACK_IMPORTED_MODULE_2__[\"default\"], {\n id: \"save-token\",\n name: \"save-token\",\n checked: this.state.saveToken,\n onChange: this.handleToggleSaveToken\n }), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"label\", {\n htmlFor: \"save-token\"\n }, \"\\xA0\", react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__[\"FormattedMessage\"], {\n id: \"login.stay_logged_in\",\n defaultMessage: \"Stay logged in\"\n })), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"a\", {\n href: \"#reset\"\n }, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__[\"FormattedMessage\"], {\n id: \"login.forgot_password_link\",\n defaultMessage: \"Forgot password?\"\n }))), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"div\", {\n className: \"dialog-buttons\"\n }, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"button\", {\n className: submitClasses,\n type: \"submit\"\n }, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react_intl__WEBPACK_IMPORTED_MODULE_1__[\"FormattedMessage\"], {\n id: \"login.sign_in_button\",\n defaultMessage: \"Sign in\"\n }))));\n }\n }]);\n\n return LoginView;\n}(react__WEBPACK_IMPORTED_MODULE_0___default.a.Component);\n\n\n;\n/* END Login */\n\n//# sourceURL=webpack:///./src/views/login-view.jsx?"); /***/ }), @@ -735,7 +805,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) * /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return LogoView; });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\nfunction _typeof(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\n/* This is just a static page to display when no conversation is selected. */\n\n\nvar LogoView =\n/*#__PURE__*/\nfunction (_React$PureComponent) {\n _inherits(LogoView, _React$PureComponent);\n\n function LogoView() {\n _classCallCheck(this, LogoView);\n\n return _possibleConstructorReturn(this, _getPrototypeOf(LogoView).apply(this, arguments));\n }\n\n _createClass(LogoView, [{\n key: \"render\",\n value: function render() {\n var version = APP_NAME + ' (' + Tinode.getLibrary() + ')';\n return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"div\", {\n id: \"dummy-view\",\n className: this.props.hideSelf ? 'nodisplay' : null\n }, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"div\", null, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"a\", {\n href: \"https://github.com/tinode/chat/\"\n }, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"img\", {\n id: \"logo\",\n alt: \"logo\",\n src: \"img/logo.svg\"\n }), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"h2\", null, \"Tinode Web\")), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"p\", null, \"Client: \", version), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"p\", null, \"Server: \", this.props.serverVersion, \" (\", this.props.serverAddress, \")\")));\n }\n }]);\n\n return LogoView;\n}(react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent);\n\n\n;\n\n//# sourceURL=webpack:///./src/views/logo-view.jsx?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return LogoView; });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! tinode-sdk */ \"./node_modules/tinode-sdk/umd/tinode.prod.js\");\n/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(tinode_sdk__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../config.js */ \"./src/config.js\");\nfunction _typeof(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\n/* This is just a static page to display when no conversation is selected. */\n\n\n\n\nvar LogoView =\n/*#__PURE__*/\nfunction (_React$PureComponent) {\n _inherits(LogoView, _React$PureComponent);\n\n function LogoView() {\n _classCallCheck(this, LogoView);\n\n return _possibleConstructorReturn(this, _getPrototypeOf(LogoView).apply(this, arguments));\n }\n\n _createClass(LogoView, [{\n key: \"render\",\n value: function render() {\n var version = _config_js__WEBPACK_IMPORTED_MODULE_2__[\"APP_NAME\"] + ' (' + tinode_sdk__WEBPACK_IMPORTED_MODULE_1___default.a.getLibrary() + ')';\n return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"div\", {\n id: \"dummy-view\",\n className: this.props.hideSelf ? 'nodisplay' : null\n }, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"div\", null, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"a\", {\n href: \"https://github.com/tinode/chat/\"\n }, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"img\", {\n id: \"logo\",\n alt: \"logo\",\n src: \"img/logo.svg\"\n }), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"h2\", null, \"Tinode Web\")), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"p\", null, \"Client: \", version), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"p\", null, \"Server: \", this.props.serverVersion, \" (\", this.props.serverAddress, \")\")));\n }\n }]);\n\n return LogoView;\n}(react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent);\n\n\n;\n\n//# sourceURL=webpack:///./src/views/logo-view.jsx?"); /***/ }), @@ -807,7 +877,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) * /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"POP_SOUND\", function() { return POP_SOUND; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return TinodeWeb; });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var firebase_app__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! firebase/app */ \"./node_modules/firebase/app/dist/index.cjs.js\");\n/* harmony import */ var firebase_app__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(firebase_app__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var firebase_messaging__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! firebase/messaging */ \"./node_modules/firebase/messaging/dist/index.esm.js\");\n/* harmony import */ var _firebase_init_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../firebase-init.js */ \"./firebase-init.js\");\n/* harmony import */ var _widgets_context_menu_jsx__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../widgets/context-menu.jsx */ \"./src/widgets/context-menu.jsx\");\n/* harmony import */ var _info_view_jsx__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./info-view.jsx */ \"./src/views/info-view.jsx\");\n/* harmony import */ var _messages_view_jsx__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./messages-view.jsx */ \"./src/views/messages-view.jsx\");\n/* harmony import */ var _sidepanel_view_jsx__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./sidepanel-view.jsx */ \"./src/views/sidepanel-view.jsx\");\nfunction _typeof(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\n// The top-level class to hold all functionality together.\n\n\n\n\n\n\n\n // Sound to play on message received.\n\nvar POP_SOUND = new Audio('audio/msg.mp3');\n\nvar TinodeWeb =\n/*#__PURE__*/\nfunction (_React$Component) {\n _inherits(TinodeWeb, _React$Component);\n\n function TinodeWeb(props) {\n var _this;\n\n _classCallCheck(this, TinodeWeb);\n\n _this = _possibleConstructorReturn(this, _getPrototypeOf(TinodeWeb).call(this, props));\n _this.state = _this.getBlankState();\n _this.handleResize = _this.handleResize.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleHashRoute = _this.handleHashRoute.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleOnline = _this.handleOnline.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleAppVisibility = _this.handleAppVisibility.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleReadTimer = _this.handleReadTimer.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleVisibilityEvent = _this.handleVisibilityEvent.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleError = _this.handleError.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleLoginRequest = _this.handleLoginRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleConnected = _this.handleConnected.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.doLogin = _this.doLogin.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleCredentialsRequest = _this.handleCredentialsRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleLoginSuccessful = _this.handleLoginSuccessful.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleDisconnect = _this.handleDisconnect.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.tnMeMetaDesc = _this.tnMeMetaDesc.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.tnMeContactUpdate = _this.tnMeContactUpdate.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.tnMeSubsUpdated = _this.tnMeSubsUpdated.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.resetContactList = _this.resetContactList.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.tnData = _this.tnData.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.tnInitFind = _this.tnInitFind.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.tnFndSubsUpdated = _this.tnFndSubsUpdated.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleSearchContacts = _this.handleSearchContacts.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleTopicSelected = _this.handleTopicSelected.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleHideMessagesView = _this.handleHideMessagesView.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleSendMessage = _this.handleSendMessage.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleNewAccount = _this.handleNewAccount.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleNewAccountRequest = _this.handleNewAccountRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleUpdateAccountRequest = _this.handleUpdateAccountRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleUpdateAccountTagsRequest = _this.handleUpdateAccountTagsRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleSettings = _this.handleSettings.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleGlobalSettings = _this.handleGlobalSettings.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleToggleMessageSounds = _this.handleToggleMessageSounds.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.initDesktopAlerts = _this.initDesktopAlerts.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.togglePushToken = _this.togglePushToken.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.requestPushToken = _this.requestPushToken.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleSidepanelCancel = _this.handleSidepanelCancel.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleNewTopic = _this.handleNewTopic.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleNewTopicRequest = _this.handleNewTopicRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleNewTopicCreated = _this.handleNewTopicCreated.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleTopicUpdateRequest = _this.handleTopicUpdateRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleChangePermissions = _this.handleChangePermissions.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleTagsUpdated = _this.handleTagsUpdated.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleLogout = _this.handleLogout.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleLeaveUnsubRequest = _this.handleLeaveUnsubRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleDialogCancel = _this.handleDialogCancel.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleShowContextMenu = _this.handleShowContextMenu.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.defaultTopicContextMenu = _this.defaultTopicContextMenu.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleHideContextMenu = _this.handleHideContextMenu.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleShowInfoView = _this.handleShowInfoView.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleHideInfoView = _this.handleHideInfoView.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleMemberUpdateRequest = _this.handleMemberUpdateRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleValidateCredentialsRequest = _this.handleValidateCredentialsRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handlePasswordResetRequest = _this.handlePasswordResetRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleResetPassword = _this.handleResetPassword.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n return _this;\n }\n\n _createClass(TinodeWeb, [{\n key: \"getBlankState\",\n value: function getBlankState() {\n var _ref;\n\n var settings = localStorage.getObject(\"settings\") || {};\n return _ref = {\n connected: false,\n transport: settings.transport || null,\n serverAddress: settings.serverAddress || detectServerAddress(),\n serverVersion: \"no connection\",\n // \"On\" is the default, so saving the \"off\" state.\n messageSounds: !settings.messageSoundsOff,\n desktopAlerts: settings.desktopAlerts,\n desktopAlertsEnabled: (isSecureConnection() || isLocalHost()) && typeof firebase != 'undefined' && typeof navigator != 'undefined' && typeof _firebase_init_js__WEBPACK_IMPORTED_MODULE_3__[\"FIREBASE_INIT\"] != 'undefined',\n firebaseToken: localStorage.getObject(\"firebase-token\"),\n errorText: '',\n errorLevel: null,\n sidePanelSelected: 'login',\n sidePanelTitle: null,\n sidePanelAvatar: null,\n dialogSelected: null,\n contextMenuVisible: false,\n login: '',\n password: '',\n myUserId: null,\n liveConnection: navigator.onLine,\n topicSelected: '',\n topicSelectedOnline: false,\n topicSelectedAcs: null,\n newGroupTopicParams: null,\n loginDisabled: false,\n displayMobile: window.innerWidth <= MEDIA_BREAKPOINT,\n showInfoPanel: false,\n mobilePanel: 'sidepanel'\n }, _defineProperty(_ref, \"contextMenuVisible\", false), _defineProperty(_ref, \"contextMenuBounds\", null), _defineProperty(_ref, \"contextMenuClickAt\", null), _defineProperty(_ref, \"contextMenuParams\", null), _defineProperty(_ref, \"contextMenuItems\", []), _defineProperty(_ref, \"chatList\", []), _defineProperty(_ref, \"searchResults\", []), _defineProperty(_ref, \"searchableContacts\", []), _defineProperty(_ref, \"credMethod\", undefined), _defineProperty(_ref, \"credCode\", undefined), _ref;\n }\n }, {\n key: \"componentDidMount\",\n value: function componentDidMount() {\n var _this2 = this;\n\n window.addEventListener('resize', this.handleResize);\n window.addEventListener('online', function (e) {\n _this2.handleOnline(true);\n });\n window.addEventListener('offline', function (e) {\n _this2.handleOnline(false);\n });\n window.addEventListener('hashchange', this.handleHashRoute); // Window/tab visible or invisible for pausing timers.\n\n document.addEventListener('visibilitychange', this.handleVisibilityEvent);\n this.setState({\n viewportWidth: document.documentElement.clientWidth\n });\n this.tinode = TinodeWeb.tnSetup(this.state.serverAddress, this.state.transport);\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect; // Initialize desktop alerts.\n\n if (this.state.desktopAlertsEnabled) {\n try {\n this.fbPush = firebase.initializeApp(_firebase_init_js__WEBPACK_IMPORTED_MODULE_3__[\"FIREBASE_INIT\"], APP_NAME).messaging();\n this.fbPush.usePublicVapidKey(_firebase_init_js__WEBPACK_IMPORTED_MODULE_3__[\"FIREBASE_INIT\"].messagingVapidKey);\n navigator.serviceWorker.register('/service-worker.js').then(function (reg) {\n _this2.fbPush.useServiceWorker(reg);\n\n _this2.initDesktopAlerts();\n\n if (_this2.state.desktopAlerts) {\n if (!_this2.state.firebaseToken) {\n _this2.togglePushToken(true);\n } else {\n _this2.tinode.setDeviceToken(_this2.state.firebaseToken);\n }\n }\n }).catch(function (err) {\n // registration failed :(\n console.log(\"Failed to register service worker:\", err);\n });\n } catch (err) {\n this.handleError(\"Failed to initialize push notifications\", 'err');\n console.log(\"Failed to initialize push notifications\", err);\n this.setState({\n desktopAlertsEnabled: false\n });\n }\n }\n\n var token = localStorage.getObject('keep-logged-in') ? localStorage.getObject('auth-token') : undefined;\n var parsedNav = parseUrlHash(window.location.hash);\n\n if (token) {\n // When reading from storage, date is returned as string.\n token.expires = new Date(token.expires);\n this.tinode.setAuthToken(token);\n this.tinode.connect().catch(function (err) {\n // Socket error\n _this2.handleError(err.message, 'err');\n });\n delete parsedNav.params.info;\n delete parsedNav.params.tab;\n parsedNav.path[0] = '';\n navigateTo(composeUrlHash(parsedNav.path, parsedNav.params));\n } else if (!parsedNav.params.token) {\n navigateTo('');\n }\n\n this.readTimer = null;\n this.readTimerCallback = null;\n this.handleHashRoute();\n }\n }, {\n key: \"componentWillUnmount\",\n value: function componentWillUnmount() {\n window.removeEventListener('resize', this.handleResize);\n window.removeEventListener('hashchange', this.handleHashRoute);\n document.removeEventListener('visibilitychange', this.handleVisibilityEvent);\n } // Setup transport (usually websocket) and server address. This will terminate connection with the server.\n\n }, {\n key: \"handleResize\",\n value: function handleResize() {\n var mobile = document.documentElement.clientWidth <= MEDIA_BREAKPOINT;\n this.setState({\n viewportWidth: document.documentElement.clientWidth\n });\n\n if (this.state.displayMobile != mobile) {\n this.setState({\n displayMobile: mobile\n });\n }\n } // Handle for hashchange event: display appropriate panels.\n\n }, {\n key: \"handleHashRoute\",\n value: function handleHashRoute() {\n var hash = parseUrlHash(window.location.hash);\n\n if (hash.path && hash.path.length > 0) {\n // Left-side panel selector.\n if (['register', 'settings', 'edit', 'cred', 'reset', 'newtpk', 'contacts', ''].includes(hash.path[0])) {\n this.setState({\n sidePanelSelected: hash.path[0]\n });\n } else {\n console.log(\"Unknown sidepanel view\", hash.path[0]);\n } // Topic for MessagesView selector.\n\n\n if (hash.path.length > 1 && hash.path[1] != this.state.topicSelected) {\n this.setState({\n topicSelected: Tinode.topicType(hash.path[1]) ? hash.path[1] : null\n });\n }\n } else {\n // Empty hashpath\n this.setState({\n sidePanelSelected: ''\n });\n } // Save validation credentials, if available.\n\n\n if (hash.params.method) {\n this.setState({\n credMethod: hash.params.method\n });\n }\n\n if (hash.params.code) {\n this.setState({\n credCode: hash.params.code\n });\n } // Additional parameters of panels.\n\n\n this.setState({\n showInfoPanel: hash.params.info,\n newTopicTabSelected: hash.params.tab\n });\n }\n }, {\n key: \"handleOnline\",\n value: function handleOnline(online) {\n var newState = {\n liveConnection: online\n };\n\n if (online) {\n this.handleError('', null);\n } else {\n this.handleError(\"No connection\", 'warn');\n }\n\n this.setState({\n liveConnection: online\n });\n } // Handling read notifications here to be able to pause\n // them then the window/tab is not visible.\n\n }, {\n key: \"handleAppVisibility\",\n value: function handleAppVisibility(visible, callback) {\n clearTimeout(this.readTimer);\n this.readTimerCallback = callback;\n\n if (visible && callback) {\n this.readTimer = setTimeout(callback, READ_DELAY);\n } else {\n this.readTimer = null;\n }\n }\n }, {\n key: \"handleReadTimer\",\n value: function handleReadTimer(callback) {\n this.handleAppVisibility(!document.hidden, callback);\n }\n }, {\n key: \"handleVisibilityEvent\",\n value: function handleVisibilityEvent() {\n this.handleAppVisibility(!document.hidden, this.readTimerCallback);\n }\n }, {\n key: \"handleError\",\n value: function handleError(err, level) {\n this.setState({\n errorText: err,\n errorLevel: level\n });\n } // User clicked Login button in the side panel.\n\n }, {\n key: \"handleLoginRequest\",\n value: function handleLoginRequest(login, password) {\n var _this3 = this;\n\n this.setState({\n loginDisabled: true,\n login: login,\n password: password\n });\n this.handleError('', null);\n\n if (this.tinode.isConnected()) {\n this.doLogin(login, password, {\n meth: this.state.credMethod,\n resp: this.state.credCode\n });\n } else {\n this.tinode.connect().catch(function (err) {\n // Socket error\n _this3.setState({\n loginDisabled: false\n });\n\n _this3.handleError(err.message, 'err');\n });\n }\n } // Connection succeeded.\n\n }, {\n key: \"handleConnected\",\n value: function handleConnected() {\n var params = this.tinode.getServerInfo();\n this.setState({\n serverVersion: params.ver + ' ' + (params.build ? params.build : 'none') + '; '\n });\n this.doLogin(this.state.login, this.state.password, {\n meth: this.state.credMethod,\n resp: this.state.credCode\n });\n }\n }, {\n key: \"doLogin\",\n value: function doLogin(login, password, cred) {\n var _this4 = this;\n\n if (this.tinode.isAuthenticated()) {\n // Already logged in. Go to default screen.\n navigateTo(\"\");\n return;\n } // Sanitize and package credentail.\n\n\n cred = Tinode.credential(cred); // Try to login with login/password. If they are not available, try token. If no token, ask for login/password.\n\n var promise = null;\n var token = this.tinode.getAuthToken();\n\n if (login && password) {\n this.setState({\n password: null\n });\n promise = this.tinode.loginBasic(login, password, cred);\n } else if (token) {\n promise = this.tinode.loginToken(token.token, cred);\n }\n\n if (promise) {\n promise.then(function (ctrl) {\n if (ctrl.code >= 300 && ctrl.text === 'validate credentials') {\n if (cred) {\n _this4.handleError(\"Code does not match\", 'warn');\n }\n\n _this4.handleCredentialsRequest(ctrl.params);\n } else {\n _this4.handleLoginSuccessful();\n }\n }).catch(function (err) {\n // Login failed, report error.\n _this4.setState({\n loginDisabled: false,\n credMethod: undefined,\n credCode: undefined\n });\n\n _this4.handleError(err.message, 'err');\n\n localStorage.removeItem('auth-token');\n navigateTo('');\n });\n } else {\n // No login credentials provided.\n // Make sure we are on the login page.\n navigateTo('');\n this.setState({\n loginDisabled: false\n });\n }\n }\n }, {\n key: \"handleCredentialsRequest\",\n value: function handleCredentialsRequest(params) {\n var parsed = parseUrlHash(window.location.hash);\n parsed.path[0] = 'cred';\n parsed.params['method'] = params.cred[0];\n navigateTo(composeUrlHash(parsed.path, parsed.params));\n }\n }, {\n key: \"handleLoginSuccessful\",\n value: function handleLoginSuccessful() {\n var _this5 = this;\n\n this.handleError('', null); // Refresh authentication token.\n\n if (localStorage.getObject('keep-logged-in')) {\n localStorage.setObject('auth-token', this.tinode.getAuthToken());\n } // Logged in fine, subscribe to 'me' attaching callbacks from the contacts view.\n\n\n var me = this.tinode.getMeTopic();\n me.onMetaDesc = this.tnMeMetaDesc;\n me.onContactUpdate = this.tnMeContactUpdate;\n me.onSubsUpdated = this.tnMeSubsUpdated;\n this.setState({\n connected: true,\n credMethod: undefined,\n credCode: undefined,\n myUserId: this.tinode.getCurrentUserID()\n }); // Subscribe, fetch topic desc, the list of subscriptions. Messages are not fetched.\n\n me.subscribe(me.startMetaQuery().withLaterSub().withDesc().build()).catch(function (err) {\n localStorage.removeItem('auth-token');\n\n _this5.handleError(err.message, 'err');\n\n navigateTo('');\n });\n navigateTo(setUrlSidePanel(window.location.hash, 'contacts'));\n }\n }, {\n key: \"handleDisconnect\",\n value: function handleDisconnect(err) {\n this.setState({\n connected: false,\n topicSelectedOnline: false,\n dialogSelected: null,\n errorText: err && err.message ? err.message : \"Disconnected\",\n errorLevel: err && err.message ? 'err' : 'warn',\n loginDisabled: false,\n contextMenuVisible: false,\n serverVersion: \"no connection\"\n });\n }\n }, {\n key: \"tnMeMetaDesc\",\n value: function tnMeMetaDesc(desc) {\n if (desc && desc.public) {\n this.setState({\n sidePanelTitle: desc.public.fn,\n sidePanelAvatar: makeImageUrl(desc.public.photo)\n });\n }\n } // Reactions to updates to the contact list.\n\n }, {\n key: \"tnMeContactUpdate\",\n value: function tnMeContactUpdate(what, cont) {\n if (what == 'on' || what == 'off') {\n this.resetContactList();\n\n if (this.state.topicSelected == cont.topic) {\n this.setState({\n topicSelectedOnline: what == 'on'\n });\n }\n } else if (what == 'read') {\n this.resetContactList();\n } else if (what == 'msg') {\n // New message received\n // Skip update if the topic is currently open, otherwise the badge will annoyingly flash.\n if (this.state.topicSelected != cont.topic) {\n if (this.state.messageSounds) {\n POP_SOUND.play();\n }\n\n this.resetContactList();\n } else if (document.hidden && this.state.messageSounds) {\n POP_SOUND.play();\n }\n } else if (what == 'recv') {// Explicitly ignoring \"recv\" -- it causes no visible updates to contact list.\n } else if (what == 'gone' || what == 'unsub') {\n // Topic deleted or user unsubscribed. Remove topic from view.\n // If the currently selected topic is gone, clear the selection.\n if (this.state.topicSelected == cont.topic) {\n this.handleTopicSelected(null);\n } // Redraw without the deleted topic.\n\n\n this.resetContactList();\n } else if (what == 'acs') {\n // Permissions changed. If it's for the currently selected topic,\n // update the views.\n if (this.state.topicSelected == cont.topic) {\n this.setState({\n topicSelectedAcs: cont.acs\n });\n }\n } else if (what == 'del') {// messages deleted (hard or soft) -- update pill counter.\n } else {\n // TODO(gene): handle other types of notifications:\n // * ua -- user agent changes (maybe display a pictogram for mobile/desktop).\n // * upd -- topic 'public' updated, issue getMeta().\n console.log(\"Unsupported (yet) presence update:\" + what + \" in: \" + cont.topic);\n }\n }\n }, {\n key: \"tnMeSubsUpdated\",\n value: function tnMeSubsUpdated(unused) {\n this.resetContactList();\n } // Merge search results and contact list to create a single flat\n // list of kown contacts for GroupManager to use.\n\n }, {\n key: \"resetContactList\",\n value: function resetContactList() {\n var _this6 = this;\n\n var newState = {\n chatList: []\n };\n this.tinode.getMeTopic().contacts(function (c) {\n newState.chatList.push(c);\n\n if (_this6.state.topicSelected == c.topic) {\n newState.topicSelectedOnline = c.online;\n newState.topicSelectedAcs = c.acs;\n }\n }); // Merge search results and chat list.\n\n newState.searchableContacts = TinodeWeb.prepareSearchableContacts(newState.chatList, this.state.searchResults);\n this.setState(newState);\n } // Sending \"received\" notifications\n\n }, {\n key: \"tnData\",\n value: function tnData(data) {\n var _this7 = this;\n\n var topic = this.tinode.getTopic(data.topic);\n\n if (topic.msgStatus(data) >= Tinode.MESSAGE_STATUS_SENT) {\n clearTimeout(this.receivedTimer);\n this.receivedTimer = setTimeout(function () {\n _this7.receivedTimer = undefined;\n topic.noteRecv(data.seq);\n }, RECEIVED_DELAY);\n }\n }\n /* Fnd topic: find contacts by tokens */\n\n }, {\n key: \"tnInitFind\",\n value: function tnInitFind() {\n var _this8 = this;\n\n var fnd = this.tinode.getFndTopic();\n\n if (fnd.isSubscribed()) {\n this.tnFndSubsUpdated();\n } else {\n fnd.onSubsUpdated = this.tnFndSubsUpdated;\n fnd.subscribe(fnd.startMetaQuery().withSub().withTags().build()).catch(function (err) {\n _this8.handleError(err.message, 'err');\n });\n }\n }\n }, {\n key: \"tnFndSubsUpdated\",\n value: function tnFndSubsUpdated() {\n var foundContacts = []; // Don't attempt to create P2P topics which already exist. Server will reject the duplicates.\n\n this.tinode.getFndTopic().contacts(function (s) {\n foundContacts.push(s);\n });\n this.setState({\n searchResults: foundContacts,\n searchableContacts: TinodeWeb.prepareSearchableContacts(this.state.chatList, foundContacts)\n });\n }\n /** Called when the user enters a contact into the contact search field in the NewAccount panel\n @param query {Array} is an array of contacts to search for\n */\n\n }, {\n key: \"handleSearchContacts\",\n value: function handleSearchContacts(query) {\n var _this9 = this;\n\n var fnd = this.tinode.getFndTopic();\n fnd.setMeta({\n desc: {\n public: query\n }\n }).then(function (ctrl) {\n return fnd.getMeta(fnd.startMetaQuery().withSub().build());\n }).catch(function (err) {\n _this9.handleError(err.message, 'err');\n });\n } // User clicked on a contact in the side panel or deleted a contact.\n\n }, {\n key: \"handleTopicSelected\",\n value: function handleTopicSelected(topicName, unused_index, online, acs) {\n if (topicName) {\n this.setState({\n errorText: '',\n errorLevel: null,\n mobilePanel: 'topic-view',\n showInfoPanel: false\n }); // Different contact selected.\n\n if (this.state.topicSelected != topicName) {\n this.setState({\n topicSelectedOnline: online,\n topicSelectedAcs: acs\n });\n navigateTo(setUrlTopic('', topicName));\n }\n } else {\n // Currently selected contact deleted\n this.setState({\n errorText: '',\n errorLevel: null,\n mobilePanel: 'sidepanel',\n topicSelectedOnline: false,\n topicSelectedAcs: null,\n showInfoPanel: false\n });\n\n if (this.state.topicSelected) {\n navigateTo(setUrlTopic('', null));\n }\n }\n } // In mobile view user requested to show sidepanel\n\n }, {\n key: \"handleHideMessagesView\",\n value: function handleHideMessagesView() {\n this.setState({\n mobilePanel: 'sidepanel'\n });\n navigateTo(setUrlTopic(window.location.hash, null));\n } // User is sending a message, either plain text or a drafty object, possibly\n // with attachments.\n\n }, {\n key: \"handleSendMessage\",\n value: function handleSendMessage(msg, promise, uploader) {\n var _this10 = this;\n\n var topic = this.tinode.getTopic(this.state.topicSelected);\n msg = topic.createMessage(msg, false); // The uploader is used to show progress.\n\n msg._uploader = uploader;\n\n if (!topic.isSubscribed()) {\n if (!promise) {\n promise = Promise.resolve();\n }\n\n promise = promise.then(function () {\n return topic.subscribe();\n });\n }\n\n if (promise) {\n promise = promise.catch(function (err) {\n _this10.handleError(err.message, 'err');\n });\n }\n\n topic.publishDraft(msg, promise).catch(function (err) {\n _this10.handleError(err.message, 'err');\n });\n } // User chose a Sign Up menu item.\n\n }, {\n key: \"handleNewAccount\",\n value: function handleNewAccount() {\n navigateTo(setUrlSidePanel(window.location.hash, 'register'));\n } // Actual registration of a new account.\n\n }, {\n key: \"handleNewAccountRequest\",\n value: function handleNewAccountRequest(login_, password_, public_, cred_, tags_) {\n var _this11 = this;\n\n this.tinode.connect(this.state.serverAddress).then(function () {\n return _this11.tinode.createAccountBasic(login_, password_, {\n public: public_,\n tags: tags_,\n cred: Tinode.credential(cred_)\n });\n }).then(function (ctrl) {\n if (ctrl.code >= 300 && ctrl.text == 'validate credentials') {\n _this11.handleCredentialsRequest(ctrl.params);\n } else {\n _this11.handleLoginSuccessful(_this11);\n }\n }).catch(function (err) {\n _this11.handleError(err.message, 'err');\n });\n }\n }, {\n key: \"handleUpdateAccountRequest\",\n value: function handleUpdateAccountRequest(password, pub, defacs) {\n var _this12 = this;\n\n if (pub || defacs) {\n this.tinode.getMeTopic().setMeta({\n desc: {\n public: pub,\n defacs: defacs\n }\n }).catch(function (err) {\n _this12.handleError(err.message, 'err');\n });\n }\n\n if (password) {\n this.tinode.updateAccountBasic(null, this.tinode.getCurrentLogin(), password).catch(function (err) {\n _this12.handleError(err.message, 'err');\n });\n }\n }\n }, {\n key: \"handleUpdateAccountTagsRequest\",\n value: function handleUpdateAccountTagsRequest(tags) {\n var _this13 = this;\n\n this.tinode.getFndTopic().setMeta({\n tags: tags\n }).catch(function (err) {\n _this13.handleError(err.message, 'err');\n });\n } // User chose Settings menu item.\n\n }, {\n key: \"handleSettings\",\n value: function handleSettings() {\n navigateTo(setUrlSidePanel(window.location.hash, this.state.myUserId ? 'edit' : 'settings'));\n } // User updated global parameters.\n\n }, {\n key: \"handleGlobalSettings\",\n value: function handleGlobalSettings(settings) {\n var serverAddress = settings.serverAddress || this.state.serverAddress;\n var transport = settings.transport || this.state.transport;\n\n if (this.tinode) {\n this.tinode.onDisconnect = undefined;\n this.tinode.disconnect();\n }\n\n this.tinode = TinodeWeb.tnSetup(serverAddress, transport);\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\n this.setState({\n serverAddress: serverAddress,\n transport: transport\n });\n localStorage.setObject('settings', {\n serverAddress: serverAddress,\n transport: transport\n });\n navigateTo(setUrlSidePanel(window.location.hash, ''));\n } // Initialize desktop alerts = push notifications.\n\n }, {\n key: \"initDesktopAlerts\",\n value: function initDesktopAlerts() {\n var _this14 = this;\n\n // Google could not be bothered to mention that\n // onTokenRefresh is never called.\n this.fbPush.onTokenRefresh(function () {\n _this14.requestPushToken(true);\n });\n this.fbPush.onMessage(function (payload) {// No need to do anything about it.\n // All the magic happends in the service worker.\n });\n }\n }, {\n key: \"togglePushToken\",\n value: function togglePushToken(enabled) {\n var _this15 = this;\n\n if (enabled) {\n if (!this.state.firebaseToken) {\n this.fbPush.requestPermission().then(function () {\n _this15.requestPushToken(true);\n }).catch(function (err) {\n _this15.handleError(err.message, 'err');\n\n _this15.setState({\n desktopAlerts: false,\n firebaseToken: null\n });\n\n localStorage.updateObject('settings', {\n desktopAlerts: false\n });\n console.log(\"Failed to get permission to notify.\", err);\n });\n } else {\n this.setState({\n desktopAlerts: true\n });\n localStorage.updateObject('settings', {\n desktopAlerts: true\n });\n }\n } else if (this.state.firebaseToken) {\n this.fbPush.deleteToken(this.state.firebaseToken).catch(function (err) {\n console.log(\"Unable to delete token.\", err);\n }).finally(function () {\n localStorage.updateObject('settings', {\n desktopAlerts: false\n });\n localStorage.removeItem('firebase-token');\n\n _this15.setState({\n desktopAlerts: false,\n firebaseToken: null\n });\n });\n } else {\n this.setState({\n desktopAlerts: false,\n firebaseToken: null\n });\n localStorage.updateObject('settings', {\n desktopAlerts: false\n });\n }\n }\n }, {\n key: \"requestPushToken\",\n value: function requestPushToken(sendToServer) {\n var _this16 = this;\n\n this.fbPush.getToken().then(function (refreshedToken) {\n if (refreshedToken != _this16.state.firebaseToken) {\n _this16.tinode.setDeviceToken(refreshedToken, sendToServer);\n\n localStorage.setObject('firebase-token', refreshedToken);\n }\n\n _this16.setState({\n firebaseToken: refreshedToken,\n desktopAlerts: true\n });\n\n localStorage.updateObject('settings', {\n desktopAlerts: true\n });\n }).catch(function (err) {\n _this16.handleError(err.message, 'err');\n\n console.log(\"Failed to retrieve firebase token\", err);\n });\n }\n }, {\n key: \"handleToggleMessageSounds\",\n value: function handleToggleMessageSounds(enabled) {\n this.setState({\n messageSounds: enabled\n });\n localStorage.updateObject('settings', {\n messageSoundsOff: !enabled\n });\n } // User clicked Cancel button in Setting or Sign Up panel.\n\n }, {\n key: \"handleSidepanelCancel\",\n value: function handleSidepanelCancel() {\n var parsed = parseUrlHash(window.location.hash);\n parsed.path[0] = this.state.myUserId ? 'contacts' : '';\n\n if (parsed.params) {\n delete parsed.params.code;\n delete parsed.params.method;\n delete parsed.params.tab;\n }\n\n navigateTo(composeUrlHash(parsed.path, parsed.params));\n this.setState({\n errorText: '',\n errorLevel: null\n });\n } // User clicked a (+) menu item.\n\n }, {\n key: \"handleNewTopic\",\n value: function handleNewTopic() {\n navigateTo(setUrlSidePanel(window.location.hash, 'newtpk'));\n } // Request to start a new topic. New P2P topic requires peer's name.\n\n }, {\n key: \"handleNewTopicRequest\",\n value: function handleNewTopicRequest(peerName, pub, priv, tags) {\n var _this17 = this;\n\n var topicName = peerName || this.tinode.newGroupTopicName();\n\n if (!peerName) {\n this.setState({\n newGroupTopicParams: {\n desc: {\n public: pub,\n private: {\n comment: priv\n }\n },\n tags: tags\n }\n }, function () {\n _this17.handleTopicSelected(topicName);\n });\n } else {\n this.handleTopicSelected(topicName);\n }\n } // New topic was creted, here is the new topic name.\n\n }, {\n key: \"handleNewTopicCreated\",\n value: function handleNewTopicCreated(oldName, newName) {\n if (this.state.topicSelected == oldName && oldName != newName) {\n // If the current URl contains the old topic name, replace it with new.\n // Update the name of the selected topic first so the navigator doen't clear\n // the state.\n this.setState({\n topicSelected: newName\n }, function () {\n navigateTo(setUrlTopic('', newName));\n });\n }\n }\n }, {\n key: \"handleTopicUpdateRequest\",\n value: function handleTopicUpdateRequest(topicName, pub, priv, permissions) {\n var _this18 = this;\n\n var topic = this.tinode.getTopic(topicName);\n\n if (topic) {\n var params = {};\n\n if (pub) {\n params.public = pub;\n }\n\n if (priv) {\n params.private = {\n comment: priv\n };\n }\n\n if (permissions) {\n params.defacs = permissions;\n }\n\n topic.setMeta({\n desc: params\n }).catch(function (err) {\n _this18.handleError(err.message, 'err');\n });\n }\n }\n }, {\n key: \"handleChangePermissions\",\n value: function handleChangePermissions(topicName, mode, uid) {\n var _this19 = this;\n\n var topic = this.tinode.getTopic(topicName);\n\n if (topic) {\n var am = topic.getAccessMode();\n\n if (uid) {\n am.updateGiven(mode);\n mode = am.getGiven();\n } else {\n am.updateWant(mode);\n mode = am.getWant();\n }\n\n topic.setMeta({\n sub: {\n user: uid,\n mode: mode\n }\n }).catch(function (err) {\n _this19.handleError(err.message, 'err');\n });\n }\n }\n }, {\n key: \"handleTagsUpdated\",\n value: function handleTagsUpdated(topicName, tags) {\n var _this20 = this;\n\n var topic = this.tinode.getTopic(topicName);\n\n if (topic) {\n var instance = this;\n topic.setMeta({\n tags: tags\n }).catch(function (err) {\n _this20.handleError(err.message, 'err');\n });\n }\n }\n }, {\n key: \"handleLogout\",\n value: function handleLogout() {\n updateFavicon(0);\n localStorage.removeItem('auth-token');\n\n if (this.tinode) {\n this.tinode.onDisconnect = undefined;\n this.tinode.disconnect();\n }\n\n this.setState(this.getBlankState());\n this.tinode = TinodeWeb.tnSetup(this.state.serverAddress, this.state.transport);\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\n navigateTo('');\n }\n }, {\n key: \"handleLeaveUnsubRequest\",\n value: function handleLeaveUnsubRequest(topicName) {\n var _this21 = this;\n\n var topic = this.tinode.getTopic(topicName);\n\n if (!topic) {\n return;\n }\n\n topic.leave(true).then(function (ctrl) {\n // Hide MessagesView and InfoView panels.\n navigateTo(setUrlTopic(window.location.hash, ''));\n }).catch(function (err) {\n _this21.handleError(err.message, 'err');\n });\n }\n }, {\n key: \"handleDialogCancel\",\n value: function handleDialogCancel() {\n this.setState({\n dialogSelected: null\n });\n }\n }, {\n key: \"handleShowContextMenu\",\n value: function handleShowContextMenu(params, menuItems) {\n this.setState({\n contextMenuVisible: true,\n contextMenuClickAt: {\n x: params.x,\n y: params.y\n },\n contextMenuParams: params,\n contextMenuItems: menuItems || this.defaultTopicContextMenu(params.topicName),\n contextMenuBounds: ReactDOM.findDOMNode(this).getBoundingClientRect()\n });\n }\n }, {\n key: \"defaultTopicContextMenu\",\n value: function defaultTopicContextMenu(topicName) {\n var muted = false,\n blocked = false,\n subscribed = false,\n deleter = false;\n var topic = this.tinode.getTopic(topicName);\n\n if (topic) {\n if (topic.isSubscribed()) {\n subscribed = true;\n var acs = topic.getAccessMode();\n muted = acs && acs.isMuted();\n blocked = acs && !acs.isJoiner();\n deleter = acs && acs.isDeleter();\n }\n }\n\n return [subscribed ? {\n title: \"Info\",\n handler: this.handleShowInfoView\n } : null, subscribed ? 'messages_clear' : null, subscribed && deleter ? 'messages_clear_hard' : null, subscribed ? muted ? 'topic_unmute' : 'topic_mute' : null, subscribed ? blocked ? 'topic_unblock' : 'topic_block' : null, 'topic_delete'];\n }\n }, {\n key: \"handleHideContextMenu\",\n value: function handleHideContextMenu() {\n this.setState({\n contextMenuVisible: false,\n contextMenuClickAt: null,\n contextMenuParams: null,\n contextMenuBounds: null\n });\n }\n }, {\n key: \"handleShowInfoView\",\n value: function handleShowInfoView() {\n navigateTo(addUrlParam(window.location.hash, 'info', true));\n this.setState({\n showInfoPanel: true\n });\n }\n }, {\n key: \"handleHideInfoView\",\n value: function handleHideInfoView() {\n navigateTo(removeUrlParam(window.location.hash, 'info'));\n this.setState({\n showInfoPanel: false\n });\n }\n }, {\n key: \"handleMemberUpdateRequest\",\n value: function handleMemberUpdateRequest(topicName, added, removed) {\n var _this22 = this;\n\n if (!topicName) {\n return;\n }\n\n var topic = this.tinode.getTopic(topicName);\n\n if (!topic) {\n return;\n }\n\n if (added && added.length > 0) {\n added.map(function (uid) {\n topic.invite(uid, null).catch(function (err) {\n _this22.handleError(err.message, 'err');\n });\n });\n }\n\n if (removed && removed.length > 0) {\n removed.map(function (uid) {\n topic.delSubscription(uid).catch(function (err) {\n _this22.handleError(err.message, 'err');\n });\n });\n }\n }\n }, {\n key: \"handleValidateCredentialsRequest\",\n value: function handleValidateCredentialsRequest(cred, code) {\n this.setState({\n credMethod: cred,\n credCode: code\n });\n this.doLogin(null, null, {\n meth: cred,\n resp: code\n });\n }\n }, {\n key: \"handlePasswordResetRequest\",\n value: function handlePasswordResetRequest(method, value) {\n var _this23 = this;\n\n // If already connected, connnect() will return a resolved promise.\n this.tinode.connect().then(function () {\n return _this23.tinode.requestResetAuthSecret('basic', method, value);\n }).catch(function (err) {\n // Socket error\n _this23.handleError(err.message, 'err');\n });\n }\n }, {\n key: \"handleResetPassword\",\n value: function handleResetPassword(scheme, newPassword, token) {\n var _this24 = this;\n\n token = base64ReEncode(token);\n\n if (!token) {\n this.handleError(\"Invalid security token\", 'err');\n } else {\n this.tinode.connect().then(function () {\n return _this24.tinode.updateAccountBasic(null, null, newPassword, {\n token: token\n });\n }).catch(function (err) {\n // Socket error\n _this24.handleError(err.message, 'err');\n });\n }\n }\n }, {\n key: \"render\",\n value: function render() {\n return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"div\", {\n id: \"app-container\"\n }, this.state.contextMenuVisible ? react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_widgets_context_menu_jsx__WEBPACK_IMPORTED_MODULE_4__[\"default\"], {\n tinode: this.tinode,\n bounds: this.state.contextMenuBounds,\n clickAt: this.state.contextMenuClickAt,\n params: this.state.contextMenuParams,\n items: this.state.contextMenuItems,\n hide: this.handleHideContextMenu,\n onAction: this.handleContextMenuAction,\n onError: this.handleError\n }) : null, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_sidepanel_view_jsx__WEBPACK_IMPORTED_MODULE_7__[\"default\"], {\n tinode: this.tinode,\n connected: this.state.connected,\n displayMobile: this.state.displayMobile,\n hideSelf: this.state.displayMobile && this.state.mobilePanel !== 'sidepanel',\n state: this.state.sidePanelSelected,\n title: this.state.sidePanelTitle,\n avatar: this.state.sidePanelAvatar,\n login: this.state.login,\n myUserId: this.state.myUserId,\n loginDisabled: this.state.loginDisabled,\n errorText: this.state.errorText,\n errorLevel: this.state.errorLevel,\n topicSelected: this.state.topicSelected,\n chatList: this.state.chatList,\n credMethod: this.state.credMethod,\n credCode: this.state.credCode,\n transport: this.state.transport,\n messageSounds: this.state.messageSounds,\n desktopAlerts: this.state.desktopAlerts,\n desktopAlertsEnabled: this.state.desktopAlertsEnabled,\n serverAddress: this.state.serverAddress,\n onGlobalSettings: this.handleGlobalSettings,\n onSignUp: this.handleNewAccount,\n onSettings: this.handleSettings,\n onLoginRequest: this.handleLoginRequest,\n onCreateAccount: this.handleNewAccountRequest,\n onUpdateAccount: this.handleUpdateAccountRequest,\n onUpdateAccountTags: this.handleUpdateAccountTagsRequest,\n onTogglePushNotifications: this.togglePushToken,\n onToggleMessageSounds: this.handleToggleMessageSounds,\n onTopicSelected: this.handleTopicSelected,\n onCreateTopic: this.handleNewTopicRequest,\n onNewTopic: this.handleNewTopic,\n onLogout: this.handleLogout,\n onCancel: this.handleSidepanelCancel,\n onError: this.handleError,\n onValidateCredentials: this.handleValidateCredentialsRequest,\n onPasswordResetRequest: this.handlePasswordResetRequest,\n onResetPassword: this.handleResetPassword,\n onInitFind: this.tnInitFind,\n searchResults: this.state.searchResults,\n onSearchContacts: this.handleSearchContacts,\n showContextMenu: this.handleShowContextMenu\n }), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_messages_view_jsx__WEBPACK_IMPORTED_MODULE_6__[\"default\"], {\n tinode: this.tinode,\n connected: this.state.connected,\n online: this.state.topicSelectedOnline,\n acs: this.state.topicSelectedAcs,\n displayMobile: this.state.displayMobile,\n viewportWidth: this.state.viewportWidth,\n hideSelf: this.state.displayMobile && (this.state.mobilePanel !== 'topic-view' || this.state.showInfoPanel),\n topic: this.state.topicSelected,\n myUserId: this.state.myUserId,\n serverVersion: this.state.serverVersion,\n serverAddress: this.state.serverAddress,\n errorText: this.state.errorText,\n errorLevel: this.state.errorLevel,\n newGroupTopicParams: this.state.newGroupTopicParams,\n onHideMessagesView: this.handleHideMessagesView,\n onData: this.tnData,\n onError: this.handleError,\n onNewTopicCreated: this.handleNewTopicCreated,\n readTimerHandler: this.handleReadTimer,\n showContextMenu: this.handleShowContextMenu,\n sendMessage: this.handleSendMessage\n }), this.state.showInfoPanel ? react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_info_view_jsx__WEBPACK_IMPORTED_MODULE_5__[\"default\"], {\n tinode: this.tinode,\n connected: this.state.connected,\n displayMobile: this.state.displayMobile,\n topic: this.state.topicSelected,\n searchableContacts: this.state.searchableContacts,\n myUserId: this.state.myUserId,\n errorText: this.state.errorText,\n errorLevel: this.state.errorLevel,\n onTopicDescUpdate: this.handleTopicUpdateRequest,\n onCancel: this.handleHideInfoView,\n onChangePermissions: this.handleChangePermissions,\n onMemberUpdateRequest: this.handleMemberUpdateRequest,\n onLeaveTopic: this.handleLeaveUnsubRequest,\n onAddMember: this.handleManageGroupMembers,\n onTopicTagsUpdate: this.handleTagsUpdated,\n onInitFind: this.tnInitFind,\n onError: this.handleError,\n showContextMenu: this.handleShowContextMenu\n }) : null);\n }\n }], [{\n key: \"tnSetup\",\n value: function tnSetup(serverAddress, transport) {\n var tinode = new Tinode(APP_NAME, serverAddress, API_KEY, transport, isSecureConnection());\n tinode.enableLogging(true, true);\n return tinode;\n }\n }, {\n key: \"prepareSearchableContacts\",\n value: function prepareSearchableContacts(chatList, foundContacts) {\n var merged = {}; // For chatList topics merge only p2p topics and convert them to the\n // same format as foundContacts.\n\n var _iteratorNormalCompletion = true;\n var _didIteratorError = false;\n var _iteratorError = undefined;\n\n try {\n for (var _iterator = chatList[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {\n var c = _step.value;\n\n if (Tinode.topicType(c.topic) == 'p2p') {\n merged[c.topic] = {\n user: c.topic,\n updated: c.updated,\n public: c.public,\n private: c.private,\n acs: c.acs\n };\n }\n } // Add all foundCountacts if they have not been added already.\n\n } catch (err) {\n _didIteratorError = true;\n _iteratorError = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion && _iterator.return != null) {\n _iterator.return();\n }\n } finally {\n if (_didIteratorError) {\n throw _iteratorError;\n }\n }\n }\n\n var _iteratorNormalCompletion2 = true;\n var _didIteratorError2 = false;\n var _iteratorError2 = undefined;\n\n try {\n for (var _iterator2 = foundContacts[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {\n var _c = _step2.value;\n\n if (!merged[_c.user]) {\n merged[_c.user] = _c;\n }\n }\n } catch (err) {\n _didIteratorError2 = true;\n _iteratorError2 = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion2 && _iterator2.return != null) {\n _iterator2.return();\n }\n } finally {\n if (_didIteratorError2) {\n throw _iteratorError2;\n }\n }\n }\n\n return Object.values(merged);\n }\n }]);\n\n return TinodeWeb;\n}(react__WEBPACK_IMPORTED_MODULE_0___default.a.Component);\n\n\n;\n\n//# sourceURL=webpack:///./src/views/tinode-web.jsx?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"POP_SOUND\", function() { return POP_SOUND; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return TinodeWeb; });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var firebase_app__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! firebase/app */ \"./node_modules/firebase/app/dist/index.cjs.js\");\n/* harmony import */ var firebase_app__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(firebase_app__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var firebase_messaging__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! firebase/messaging */ \"./node_modules/firebase/messaging/dist/index.esm.js\");\n/* harmony import */ var _firebase_init_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../firebase-init.js */ \"./src/firebase-init.js\");\n/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! tinode-sdk */ \"./node_modules/tinode-sdk/umd/tinode.prod.js\");\n/* harmony import */ var tinode_sdk__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(tinode_sdk__WEBPACK_IMPORTED_MODULE_4__);\n/* harmony import */ var _widgets_context_menu_jsx__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../widgets/context-menu.jsx */ \"./src/widgets/context-menu.jsx\");\n/* harmony import */ var _info_view_jsx__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./info-view.jsx */ \"./src/views/info-view.jsx\");\n/* harmony import */ var _messages_view_jsx__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./messages-view.jsx */ \"./src/views/messages-view.jsx\");\n/* harmony import */ var _sidepanel_view_jsx__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./sidepanel-view.jsx */ \"./src/views/sidepanel-view.jsx\");\n/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../config.js */ \"./src/config.js\");\n/* harmony import */ var _lib_host_name_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ../lib/host-name.js */ \"./src/lib/host-name.js\");\n/* harmony import */ var _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../lib/local-storage.js */ \"./src/lib/local-storage.js\");\n/* harmony import */ var _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ../lib/navigation.js */ \"./src/lib/navigation.js\");\nfunction _typeof(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\nfunction _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\n// The top-level class to hold all functionality together.\n\n\n\n\n\n\n\n\n\n\n\n\n // Sound to play on message received.\n\nvar POP_SOUND = new Audio('audio/msg.mp3');\n\nvar TinodeWeb =\n/*#__PURE__*/\nfunction (_React$Component) {\n _inherits(TinodeWeb, _React$Component);\n\n function TinodeWeb(props) {\n var _this;\n\n _classCallCheck(this, TinodeWeb);\n\n _this = _possibleConstructorReturn(this, _getPrototypeOf(TinodeWeb).call(this, props));\n _this.state = _this.getBlankState();\n _this.handleResize = _this.handleResize.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleHashRoute = _this.handleHashRoute.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleOnline = _this.handleOnline.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleAppVisibility = _this.handleAppVisibility.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleReadTimer = _this.handleReadTimer.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleVisibilityEvent = _this.handleVisibilityEvent.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleError = _this.handleError.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleLoginRequest = _this.handleLoginRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleConnected = _this.handleConnected.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.doLogin = _this.doLogin.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleCredentialsRequest = _this.handleCredentialsRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleLoginSuccessful = _this.handleLoginSuccessful.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleDisconnect = _this.handleDisconnect.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.tnMeMetaDesc = _this.tnMeMetaDesc.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.tnMeContactUpdate = _this.tnMeContactUpdate.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.tnMeSubsUpdated = _this.tnMeSubsUpdated.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.resetContactList = _this.resetContactList.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.tnData = _this.tnData.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.tnInitFind = _this.tnInitFind.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.tnFndSubsUpdated = _this.tnFndSubsUpdated.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleSearchContacts = _this.handleSearchContacts.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleTopicSelected = _this.handleTopicSelected.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleHideMessagesView = _this.handleHideMessagesView.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleSendMessage = _this.handleSendMessage.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleNewAccount = _this.handleNewAccount.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleNewAccountRequest = _this.handleNewAccountRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleUpdateAccountRequest = _this.handleUpdateAccountRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleUpdateAccountTagsRequest = _this.handleUpdateAccountTagsRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleSettings = _this.handleSettings.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleGlobalSettings = _this.handleGlobalSettings.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleToggleMessageSounds = _this.handleToggleMessageSounds.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.initDesktopAlerts = _this.initDesktopAlerts.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.togglePushToken = _this.togglePushToken.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.requestPushToken = _this.requestPushToken.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleSidepanelCancel = _this.handleSidepanelCancel.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleNewTopic = _this.handleNewTopic.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleNewTopicRequest = _this.handleNewTopicRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleNewTopicCreated = _this.handleNewTopicCreated.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleTopicUpdateRequest = _this.handleTopicUpdateRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleChangePermissions = _this.handleChangePermissions.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleTagsUpdated = _this.handleTagsUpdated.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleLogout = _this.handleLogout.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleLeaveUnsubRequest = _this.handleLeaveUnsubRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleDialogCancel = _this.handleDialogCancel.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleShowContextMenu = _this.handleShowContextMenu.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.defaultTopicContextMenu = _this.defaultTopicContextMenu.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleHideContextMenu = _this.handleHideContextMenu.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleShowInfoView = _this.handleShowInfoView.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleHideInfoView = _this.handleHideInfoView.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleMemberUpdateRequest = _this.handleMemberUpdateRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleValidateCredentialsRequest = _this.handleValidateCredentialsRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handlePasswordResetRequest = _this.handlePasswordResetRequest.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n _this.handleResetPassword = _this.handleResetPassword.bind(_assertThisInitialized(_assertThisInitialized(_this)));\n return _this;\n }\n\n _createClass(TinodeWeb, [{\n key: \"getBlankState\",\n value: function getBlankState() {\n var _ref;\n\n var settings = _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_11__[\"default\"].getObject(\"settings\") || {};\n return _ref = {\n connected: false,\n transport: settings.transport || null,\n serverAddress: settings.serverAddress || detectServerAddress(),\n serverVersion: \"no connection\",\n // \"On\" is the default, so saving the \"off\" state.\n messageSounds: !settings.messageSoundsOff,\n desktopAlerts: settings.desktopAlerts,\n desktopAlertsEnabled: (Object(_lib_host_name_js__WEBPACK_IMPORTED_MODULE_10__[\"isSecureConnection\"])() || Object(_lib_host_name_js__WEBPACK_IMPORTED_MODULE_10__[\"isLocalHost\"])()) && typeof firebase != 'undefined' && typeof navigator != 'undefined' && typeof _firebase_init_js__WEBPACK_IMPORTED_MODULE_3__[\"FIREBASE_INIT\"] != 'undefined',\n firebaseToken: _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_11__[\"default\"].getObject(\"firebase-token\"),\n errorText: '',\n errorLevel: null,\n sidePanelSelected: 'login',\n sidePanelTitle: null,\n sidePanelAvatar: null,\n dialogSelected: null,\n contextMenuVisible: false,\n login: '',\n password: '',\n myUserId: null,\n liveConnection: navigator.onLine,\n topicSelected: '',\n topicSelectedOnline: false,\n topicSelectedAcs: null,\n newGroupTopicParams: null,\n loginDisabled: false,\n displayMobile: window.innerWidth <= _config_js__WEBPACK_IMPORTED_MODULE_9__[\"MEDIA_BREAKPOINT\"],\n showInfoPanel: false,\n mobilePanel: 'sidepanel'\n }, _defineProperty(_ref, \"contextMenuVisible\", false), _defineProperty(_ref, \"contextMenuBounds\", null), _defineProperty(_ref, \"contextMenuClickAt\", null), _defineProperty(_ref, \"contextMenuParams\", null), _defineProperty(_ref, \"contextMenuItems\", []), _defineProperty(_ref, \"chatList\", []), _defineProperty(_ref, \"searchResults\", []), _defineProperty(_ref, \"searchableContacts\", []), _defineProperty(_ref, \"credMethod\", undefined), _defineProperty(_ref, \"credCode\", undefined), _ref;\n }\n }, {\n key: \"componentDidMount\",\n value: function componentDidMount() {\n var _this2 = this;\n\n window.addEventListener('resize', this.handleResize);\n window.addEventListener('online', function (e) {\n _this2.handleOnline(true);\n });\n window.addEventListener('offline', function (e) {\n _this2.handleOnline(false);\n });\n window.addEventListener('hashchange', this.handleHashRoute); // Window/tab visible or invisible for pausing timers.\n\n document.addEventListener('visibilitychange', this.handleVisibilityEvent);\n this.setState({\n viewportWidth: document.documentElement.clientWidth\n });\n this.tinode = TinodeWeb.tnSetup(this.state.serverAddress, this.state.transport);\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect; // Initialize desktop alerts.\n\n if (this.state.desktopAlertsEnabled) {\n try {\n this.fbPush = firebase.initializeApp(_firebase_init_js__WEBPACK_IMPORTED_MODULE_3__[\"FIREBASE_INIT\"], _config_js__WEBPACK_IMPORTED_MODULE_9__[\"APP_NAME\"]).messaging();\n this.fbPush.usePublicVapidKey(_firebase_init_js__WEBPACK_IMPORTED_MODULE_3__[\"FIREBASE_INIT\"].messagingVapidKey);\n navigator.serviceWorker.register('/service-worker.js').then(function (reg) {\n _this2.fbPush.useServiceWorker(reg);\n\n _this2.initDesktopAlerts();\n\n if (_this2.state.desktopAlerts) {\n if (!_this2.state.firebaseToken) {\n _this2.togglePushToken(true);\n } else {\n _this2.tinode.setDeviceToken(_this2.state.firebaseToken);\n }\n }\n }).catch(function (err) {\n // registration failed :(\n console.log(\"Failed to register service worker:\", err);\n });\n } catch (err) {\n this.handleError(\"Failed to initialize push notifications\", 'err');\n console.log(\"Failed to initialize push notifications\", err);\n this.setState({\n desktopAlertsEnabled: false\n });\n }\n }\n\n var token = _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_11__[\"default\"].getObject('keep-logged-in') ? _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_11__[\"default\"].getObject('auth-token') : undefined;\n var parsedNav = _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].parseUrlHash(window.location.hash);\n\n if (token) {\n // When reading from storage, date is returned as string.\n token.expires = new Date(token.expires);\n this.tinode.setAuthToken(token);\n this.tinode.connect().catch(function (err) {\n // Socket error\n _this2.handleError(err.message, 'err');\n });\n delete parsedNav.params.info;\n delete parsedNav.params.tab;\n parsedNav.path[0] = '';\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].composeUrlHash(parsedNav.path, parsedNav.params));\n } else if (!parsedNav.params.token) {\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo('');\n }\n\n this.readTimer = null;\n this.readTimerCallback = null;\n this.handleHashRoute();\n }\n }, {\n key: \"componentWillUnmount\",\n value: function componentWillUnmount() {\n window.removeEventListener('resize', this.handleResize);\n window.removeEventListener('hashchange', this.handleHashRoute);\n document.removeEventListener('visibilitychange', this.handleVisibilityEvent);\n } // Setup transport (usually websocket) and server address. This will terminate connection with the server.\n\n }, {\n key: \"handleResize\",\n value: function handleResize() {\n var mobile = document.documentElement.clientWidth <= _config_js__WEBPACK_IMPORTED_MODULE_9__[\"MEDIA_BREAKPOINT\"];\n this.setState({\n viewportWidth: document.documentElement.clientWidth\n });\n\n if (this.state.displayMobile != mobile) {\n this.setState({\n displayMobile: mobile\n });\n }\n } // Handle for hashchange event: display appropriate panels.\n\n }, {\n key: \"handleHashRoute\",\n value: function handleHashRoute() {\n var hash = _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].parseUrlHash(window.location.hash);\n\n if (hash.path && hash.path.length > 0) {\n // Left-side panel selector.\n if (['register', 'settings', 'edit', 'cred', 'reset', 'newtpk', 'contacts', ''].includes(hash.path[0])) {\n this.setState({\n sidePanelSelected: hash.path[0]\n });\n } else {\n console.log(\"Unknown sidepanel view\", hash.path[0]);\n } // Topic for MessagesView selector.\n\n\n if (hash.path.length > 1 && hash.path[1] != this.state.topicSelected) {\n this.setState({\n topicSelected: tinode_sdk__WEBPACK_IMPORTED_MODULE_4___default.a.topicType(hash.path[1]) ? hash.path[1] : null\n });\n }\n } else {\n // Empty hashpath\n this.setState({\n sidePanelSelected: ''\n });\n } // Save validation credentials, if available.\n\n\n if (hash.params.method) {\n this.setState({\n credMethod: hash.params.method\n });\n }\n\n if (hash.params.code) {\n this.setState({\n credCode: hash.params.code\n });\n } // Additional parameters of panels.\n\n\n this.setState({\n showInfoPanel: hash.params.info,\n newTopicTabSelected: hash.params.tab\n });\n }\n }, {\n key: \"handleOnline\",\n value: function handleOnline(online) {\n var newState = {\n liveConnection: online\n };\n\n if (online) {\n this.handleError('', null);\n } else {\n this.handleError(\"No connection\", 'warn');\n }\n\n this.setState({\n liveConnection: online\n });\n } // Handling read notifications here to be able to pause\n // them then the window/tab is not visible.\n\n }, {\n key: \"handleAppVisibility\",\n value: function handleAppVisibility(visible, callback) {\n clearTimeout(this.readTimer);\n this.readTimerCallback = callback;\n\n if (visible && callback) {\n this.readTimer = setTimeout(callback, READ_DELAY);\n } else {\n this.readTimer = null;\n }\n }\n }, {\n key: \"handleReadTimer\",\n value: function handleReadTimer(callback) {\n this.handleAppVisibility(!document.hidden, callback);\n }\n }, {\n key: \"handleVisibilityEvent\",\n value: function handleVisibilityEvent() {\n this.handleAppVisibility(!document.hidden, this.readTimerCallback);\n }\n }, {\n key: \"handleError\",\n value: function handleError(err, level) {\n this.setState({\n errorText: err,\n errorLevel: level\n });\n } // User clicked Login button in the side panel.\n\n }, {\n key: \"handleLoginRequest\",\n value: function handleLoginRequest(login, password) {\n var _this3 = this;\n\n this.setState({\n loginDisabled: true,\n login: login,\n password: password\n });\n this.handleError('', null);\n\n if (this.tinode.isConnected()) {\n this.doLogin(login, password, {\n meth: this.state.credMethod,\n resp: this.state.credCode\n });\n } else {\n this.tinode.connect().catch(function (err) {\n // Socket error\n _this3.setState({\n loginDisabled: false\n });\n\n _this3.handleError(err.message, 'err');\n });\n }\n } // Connection succeeded.\n\n }, {\n key: \"handleConnected\",\n value: function handleConnected() {\n var params = this.tinode.getServerInfo();\n this.setState({\n serverVersion: params.ver + ' ' + (params.build ? params.build : 'none') + '; '\n });\n this.doLogin(this.state.login, this.state.password, {\n meth: this.state.credMethod,\n resp: this.state.credCode\n });\n }\n }, {\n key: \"doLogin\",\n value: function doLogin(login, password, cred) {\n var _this4 = this;\n\n if (this.tinode.isAuthenticated()) {\n // Already logged in. Go to default screen.\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo('');\n return;\n } // Sanitize and package credentail.\n\n\n cred = tinode_sdk__WEBPACK_IMPORTED_MODULE_4___default.a.credential(cred); // Try to login with login/password. If they are not available, try token. If no token, ask for login/password.\n\n var promise = null;\n var token = this.tinode.getAuthToken();\n\n if (login && password) {\n this.setState({\n password: null\n });\n promise = this.tinode.loginBasic(login, password, cred);\n } else if (token) {\n promise = this.tinode.loginToken(token.token, cred);\n }\n\n if (promise) {\n promise.then(function (ctrl) {\n if (ctrl.code >= 300 && ctrl.text === 'validate credentials') {\n if (cred) {\n _this4.handleError(\"Code does not match\", 'warn');\n }\n\n _this4.handleCredentialsRequest(ctrl.params);\n } else {\n _this4.handleLoginSuccessful();\n }\n }).catch(function (err) {\n // Login failed, report error.\n _this4.setState({\n loginDisabled: false,\n credMethod: undefined,\n credCode: undefined\n });\n\n _this4.handleError(err.message, 'err');\n\n _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_11__[\"default\"].removeItem('auth-token');\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo('');\n });\n } else {\n // No login credentials provided.\n // Make sure we are on the login page.\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo('');\n this.setState({\n loginDisabled: false\n });\n }\n }\n }, {\n key: \"handleCredentialsRequest\",\n value: function handleCredentialsRequest(params) {\n var parsed = _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].parseUrlHash(window.location.hash);\n parsed.path[0] = 'cred';\n parsed.params['method'] = params.cred[0];\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].composeUrlHash(parsed.path, parsed.params));\n }\n }, {\n key: \"handleLoginSuccessful\",\n value: function handleLoginSuccessful() {\n var _this5 = this;\n\n this.handleError('', null); // Refresh authentication token.\n\n if (_lib_local_storage_js__WEBPACK_IMPORTED_MODULE_11__[\"default\"].getObject('keep-logged-in')) {\n _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_11__[\"default\"].setObject('auth-token', this.tinode.getAuthToken());\n } // Logged in fine, subscribe to 'me' attaching callbacks from the contacts view.\n\n\n var me = this.tinode.getMeTopic();\n me.onMetaDesc = this.tnMeMetaDesc;\n me.onContactUpdate = this.tnMeContactUpdate;\n me.onSubsUpdated = this.tnMeSubsUpdated;\n this.setState({\n connected: true,\n credMethod: undefined,\n credCode: undefined,\n myUserId: this.tinode.getCurrentUserID()\n }); // Subscribe, fetch topic desc, the list of subscriptions. Messages are not fetched.\n\n me.subscribe(me.startMetaQuery().withLaterSub().withDesc().build()).catch(function (err) {\n _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_11__[\"default\"].removeItem('auth-token');\n\n _this5.handleError(err.message, 'err');\n\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo('');\n });\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].setUrlSidePanel(window.location.hash, 'contacts'));\n }\n }, {\n key: \"handleDisconnect\",\n value: function handleDisconnect(err) {\n this.setState({\n connected: false,\n topicSelectedOnline: false,\n dialogSelected: null,\n errorText: err && err.message ? err.message : \"Disconnected\",\n errorLevel: err && err.message ? 'err' : 'warn',\n loginDisabled: false,\n contextMenuVisible: false,\n serverVersion: \"no connection\"\n });\n }\n }, {\n key: \"tnMeMetaDesc\",\n value: function tnMeMetaDesc(desc) {\n if (desc && desc.public) {\n this.setState({\n sidePanelTitle: desc.public.fn,\n sidePanelAvatar: makeImageUrl(desc.public.photo)\n });\n }\n } // Reactions to updates to the contact list.\n\n }, {\n key: \"tnMeContactUpdate\",\n value: function tnMeContactUpdate(what, cont) {\n if (what == 'on' || what == 'off') {\n this.resetContactList();\n\n if (this.state.topicSelected == cont.topic) {\n this.setState({\n topicSelectedOnline: what == 'on'\n });\n }\n } else if (what == 'read') {\n this.resetContactList();\n } else if (what == 'msg') {\n // New message received\n // Skip update if the topic is currently open, otherwise the badge will annoyingly flash.\n if (this.state.topicSelected != cont.topic) {\n if (this.state.messageSounds) {\n POP_SOUND.play();\n }\n\n this.resetContactList();\n } else if (document.hidden && this.state.messageSounds) {\n POP_SOUND.play();\n }\n } else if (what == 'recv') {// Explicitly ignoring \"recv\" -- it causes no visible updates to contact list.\n } else if (what == 'gone' || what == 'unsub') {\n // Topic deleted or user unsubscribed. Remove topic from view.\n // If the currently selected topic is gone, clear the selection.\n if (this.state.topicSelected == cont.topic) {\n this.handleTopicSelected(null);\n } // Redraw without the deleted topic.\n\n\n this.resetContactList();\n } else if (what == 'acs') {\n // Permissions changed. If it's for the currently selected topic,\n // update the views.\n if (this.state.topicSelected == cont.topic) {\n this.setState({\n topicSelectedAcs: cont.acs\n });\n }\n } else if (what == 'del') {// messages deleted (hard or soft) -- update pill counter.\n } else {\n // TODO(gene): handle other types of notifications:\n // * ua -- user agent changes (maybe display a pictogram for mobile/desktop).\n // * upd -- topic 'public' updated, issue getMeta().\n console.log(\"Unsupported (yet) presence update:\" + what + \" in: \" + cont.topic);\n }\n }\n }, {\n key: \"tnMeSubsUpdated\",\n value: function tnMeSubsUpdated(unused) {\n this.resetContactList();\n } // Merge search results and contact list to create a single flat\n // list of kown contacts for GroupManager to use.\n\n }, {\n key: \"resetContactList\",\n value: function resetContactList() {\n var _this6 = this;\n\n var newState = {\n chatList: []\n };\n this.tinode.getMeTopic().contacts(function (c) {\n newState.chatList.push(c);\n\n if (_this6.state.topicSelected == c.topic) {\n newState.topicSelectedOnline = c.online;\n newState.topicSelectedAcs = c.acs;\n }\n }); // Merge search results and chat list.\n\n newState.searchableContacts = TinodeWeb.prepareSearchableContacts(newState.chatList, this.state.searchResults);\n this.setState(newState);\n } // Sending \"received\" notifications\n\n }, {\n key: \"tnData\",\n value: function tnData(data) {\n var _this7 = this;\n\n var topic = this.tinode.getTopic(data.topic);\n\n if (topic.msgStatus(data) >= tinode_sdk__WEBPACK_IMPORTED_MODULE_4___default.a.MESSAGE_STATUS_SENT) {\n clearTimeout(this.receivedTimer);\n this.receivedTimer = setTimeout(function () {\n _this7.receivedTimer = undefined;\n topic.noteRecv(data.seq);\n }, _config_js__WEBPACK_IMPORTED_MODULE_9__[\"RECEIVED_DELAY\"]);\n }\n }\n /* Fnd topic: find contacts by tokens */\n\n }, {\n key: \"tnInitFind\",\n value: function tnInitFind() {\n var _this8 = this;\n\n var fnd = this.tinode.getFndTopic();\n\n if (fnd.isSubscribed()) {\n this.tnFndSubsUpdated();\n } else {\n fnd.onSubsUpdated = this.tnFndSubsUpdated;\n fnd.subscribe(fnd.startMetaQuery().withSub().withTags().build()).catch(function (err) {\n _this8.handleError(err.message, 'err');\n });\n }\n }\n }, {\n key: \"tnFndSubsUpdated\",\n value: function tnFndSubsUpdated() {\n var foundContacts = []; // Don't attempt to create P2P topics which already exist. Server will reject the duplicates.\n\n this.tinode.getFndTopic().contacts(function (s) {\n foundContacts.push(s);\n });\n this.setState({\n searchResults: foundContacts,\n searchableContacts: TinodeWeb.prepareSearchableContacts(this.state.chatList, foundContacts)\n });\n }\n /** Called when the user enters a contact into the contact search field in the NewAccount panel\n @param query {Array} is an array of contacts to search for\n */\n\n }, {\n key: \"handleSearchContacts\",\n value: function handleSearchContacts(query) {\n var _this9 = this;\n\n var fnd = this.tinode.getFndTopic();\n fnd.setMeta({\n desc: {\n public: query\n }\n }).then(function (ctrl) {\n return fnd.getMeta(fnd.startMetaQuery().withSub().build());\n }).catch(function (err) {\n _this9.handleError(err.message, 'err');\n });\n } // User clicked on a contact in the side panel or deleted a contact.\n\n }, {\n key: \"handleTopicSelected\",\n value: function handleTopicSelected(topicName, unused_index, online, acs) {\n if (topicName) {\n this.setState({\n errorText: '',\n errorLevel: null,\n mobilePanel: 'topic-view',\n showInfoPanel: false\n }); // Different contact selected.\n\n if (this.state.topicSelected != topicName) {\n this.setState({\n topicSelectedOnline: online,\n topicSelectedAcs: acs\n });\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].setUrlTopic('', topicName));\n }\n } else {\n // Currently selected contact deleted\n this.setState({\n errorText: '',\n errorLevel: null,\n mobilePanel: 'sidepanel',\n topicSelectedOnline: false,\n topicSelectedAcs: null,\n showInfoPanel: false\n });\n\n if (this.state.topicSelected) {\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].setUrlTopic('', null));\n }\n }\n } // In mobile view user requested to show sidepanel\n\n }, {\n key: \"handleHideMessagesView\",\n value: function handleHideMessagesView() {\n this.setState({\n mobilePanel: 'sidepanel'\n });\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].setUrlTopic(window.location.hash, null));\n } // User is sending a message, either plain text or a drafty object, possibly\n // with attachments.\n\n }, {\n key: \"handleSendMessage\",\n value: function handleSendMessage(msg, promise, uploader) {\n var _this10 = this;\n\n var topic = this.tinode.getTopic(this.state.topicSelected);\n msg = topic.createMessage(msg, false); // The uploader is used to show progress.\n\n msg._uploader = uploader;\n\n if (!topic.isSubscribed()) {\n if (!promise) {\n promise = Promise.resolve();\n }\n\n promise = promise.then(function () {\n return topic.subscribe();\n });\n }\n\n if (promise) {\n promise = promise.catch(function (err) {\n _this10.handleError(err.message, 'err');\n });\n }\n\n topic.publishDraft(msg, promise).catch(function (err) {\n _this10.handleError(err.message, 'err');\n });\n } // User chose a Sign Up menu item.\n\n }, {\n key: \"handleNewAccount\",\n value: function handleNewAccount() {\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].setUrlSidePanel(window.location.hash, 'register'));\n } // Actual registration of a new account.\n\n }, {\n key: \"handleNewAccountRequest\",\n value: function handleNewAccountRequest(login_, password_, public_, cred_, tags_) {\n var _this11 = this;\n\n this.tinode.connect(this.state.serverAddress).then(function () {\n return _this11.tinode.createAccountBasic(login_, password_, {\n public: public_,\n tags: tags_,\n cred: tinode_sdk__WEBPACK_IMPORTED_MODULE_4___default.a.credential(cred_)\n });\n }).then(function (ctrl) {\n if (ctrl.code >= 300 && ctrl.text == 'validate credentials') {\n _this11.handleCredentialsRequest(ctrl.params);\n } else {\n _this11.handleLoginSuccessful(_this11);\n }\n }).catch(function (err) {\n _this11.handleError(err.message, 'err');\n });\n }\n }, {\n key: \"handleUpdateAccountRequest\",\n value: function handleUpdateAccountRequest(password, pub, defacs) {\n var _this12 = this;\n\n if (pub || defacs) {\n this.tinode.getMeTopic().setMeta({\n desc: {\n public: pub,\n defacs: defacs\n }\n }).catch(function (err) {\n _this12.handleError(err.message, 'err');\n });\n }\n\n if (password) {\n this.tinode.updateAccountBasic(null, this.tinode.getCurrentLogin(), password).catch(function (err) {\n _this12.handleError(err.message, 'err');\n });\n }\n }\n }, {\n key: \"handleUpdateAccountTagsRequest\",\n value: function handleUpdateAccountTagsRequest(tags) {\n var _this13 = this;\n\n this.tinode.getFndTopic().setMeta({\n tags: tags\n }).catch(function (err) {\n _this13.handleError(err.message, 'err');\n });\n } // User chose Settings menu item.\n\n }, {\n key: \"handleSettings\",\n value: function handleSettings() {\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].setUrlSidePanel(window.location.hash, this.state.myUserId ? 'edit' : 'settings'));\n } // User updated global parameters.\n\n }, {\n key: \"handleGlobalSettings\",\n value: function handleGlobalSettings(settings) {\n var serverAddress = settings.serverAddress || this.state.serverAddress;\n var transport = settings.transport || this.state.transport;\n\n if (this.tinode) {\n this.tinode.onDisconnect = undefined;\n this.tinode.disconnect();\n }\n\n this.tinode = TinodeWeb.tnSetup(serverAddress, transport);\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\n this.setState({\n serverAddress: serverAddress,\n transport: transport\n });\n _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_11__[\"default\"].setObject('settings', {\n serverAddress: serverAddress,\n transport: transport\n });\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].setUrlSidePanel(window.location.hash, ''));\n } // Initialize desktop alerts = push notifications.\n\n }, {\n key: \"initDesktopAlerts\",\n value: function initDesktopAlerts() {\n var _this14 = this;\n\n // Google could not be bothered to mention that\n // onTokenRefresh is never called.\n this.fbPush.onTokenRefresh(function () {\n _this14.requestPushToken(true);\n });\n this.fbPush.onMessage(function (payload) {// No need to do anything about it.\n // All the magic happends in the service worker.\n });\n }\n }, {\n key: \"togglePushToken\",\n value: function togglePushToken(enabled) {\n var _this15 = this;\n\n if (enabled) {\n if (!this.state.firebaseToken) {\n this.fbPush.requestPermission().then(function () {\n _this15.requestPushToken(true);\n }).catch(function (err) {\n _this15.handleError(err.message, 'err');\n\n _this15.setState({\n desktopAlerts: false,\n firebaseToken: null\n });\n\n _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_11__[\"default\"].updateObject('settings', {\n desktopAlerts: false\n });\n console.log(\"Failed to get permission to notify.\", err);\n });\n } else {\n this.setState({\n desktopAlerts: true\n });\n _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_11__[\"default\"].updateObject('settings', {\n desktopAlerts: true\n });\n }\n } else if (this.state.firebaseToken) {\n this.fbPush.deleteToken(this.state.firebaseToken).catch(function (err) {\n console.log(\"Unable to delete token.\", err);\n }).finally(function () {\n _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_11__[\"default\"].updateObject('settings', {\n desktopAlerts: false\n });\n _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_11__[\"default\"].removeItem('firebase-token');\n\n _this15.setState({\n desktopAlerts: false,\n firebaseToken: null\n });\n });\n } else {\n this.setState({\n desktopAlerts: false,\n firebaseToken: null\n });\n _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_11__[\"default\"].updateObject('settings', {\n desktopAlerts: false\n });\n }\n }\n }, {\n key: \"requestPushToken\",\n value: function requestPushToken(sendToServer) {\n var _this16 = this;\n\n this.fbPush.getToken().then(function (refreshedToken) {\n if (refreshedToken != _this16.state.firebaseToken) {\n _this16.tinode.setDeviceToken(refreshedToken, sendToServer);\n\n _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_11__[\"default\"].setObject('firebase-token', refreshedToken);\n }\n\n _this16.setState({\n firebaseToken: refreshedToken,\n desktopAlerts: true\n });\n\n _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_11__[\"default\"].updateObject('settings', {\n desktopAlerts: true\n });\n }).catch(function (err) {\n _this16.handleError(err.message, 'err');\n\n console.log(\"Failed to retrieve firebase token\", err);\n });\n }\n }, {\n key: \"handleToggleMessageSounds\",\n value: function handleToggleMessageSounds(enabled) {\n this.setState({\n messageSounds: enabled\n });\n _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_11__[\"default\"].updateObject('settings', {\n messageSoundsOff: !enabled\n });\n } // User clicked Cancel button in Setting or Sign Up panel.\n\n }, {\n key: \"handleSidepanelCancel\",\n value: function handleSidepanelCancel() {\n var parsed = _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].parseUrlHash(window.location.hash);\n parsed.path[0] = this.state.myUserId ? 'contacts' : '';\n\n if (parsed.params) {\n delete parsed.params.code;\n delete parsed.params.method;\n delete parsed.params.tab;\n }\n\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].composeUrlHash(parsed.path, parsed.params));\n this.setState({\n errorText: '',\n errorLevel: null\n });\n } // User clicked a (+) menu item.\n\n }, {\n key: \"handleNewTopic\",\n value: function handleNewTopic() {\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].setUrlSidePanel(window.location.hash, 'newtpk'));\n } // Request to start a new topic. New P2P topic requires peer's name.\n\n }, {\n key: \"handleNewTopicRequest\",\n value: function handleNewTopicRequest(peerName, pub, priv, tags) {\n var _this17 = this;\n\n var topicName = peerName || this.tinode.newGroupTopicName();\n\n if (!peerName) {\n this.setState({\n newGroupTopicParams: {\n desc: {\n public: pub,\n private: {\n comment: priv\n }\n },\n tags: tags\n }\n }, function () {\n _this17.handleTopicSelected(topicName);\n });\n } else {\n this.handleTopicSelected(topicName);\n }\n } // New topic was creted, here is the new topic name.\n\n }, {\n key: \"handleNewTopicCreated\",\n value: function handleNewTopicCreated(oldName, newName) {\n if (this.state.topicSelected == oldName && oldName != newName) {\n // If the current URl contains the old topic name, replace it with new.\n // Update the name of the selected topic first so the navigator doen't clear\n // the state.\n this.setState({\n topicSelected: newName\n }, function () {\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].setUrlTopic('', newName));\n });\n }\n }\n }, {\n key: \"handleTopicUpdateRequest\",\n value: function handleTopicUpdateRequest(topicName, pub, priv, permissions) {\n var _this18 = this;\n\n var topic = this.tinode.getTopic(topicName);\n\n if (topic) {\n var params = {};\n\n if (pub) {\n params.public = pub;\n }\n\n if (priv) {\n params.private = {\n comment: priv\n };\n }\n\n if (permissions) {\n params.defacs = permissions;\n }\n\n topic.setMeta({\n desc: params\n }).catch(function (err) {\n _this18.handleError(err.message, 'err');\n });\n }\n }\n }, {\n key: \"handleChangePermissions\",\n value: function handleChangePermissions(topicName, mode, uid) {\n var _this19 = this;\n\n var topic = this.tinode.getTopic(topicName);\n\n if (topic) {\n var am = topic.getAccessMode();\n\n if (uid) {\n am.updateGiven(mode);\n mode = am.getGiven();\n } else {\n am.updateWant(mode);\n mode = am.getWant();\n }\n\n topic.setMeta({\n sub: {\n user: uid,\n mode: mode\n }\n }).catch(function (err) {\n _this19.handleError(err.message, 'err');\n });\n }\n }\n }, {\n key: \"handleTagsUpdated\",\n value: function handleTagsUpdated(topicName, tags) {\n var _this20 = this;\n\n var topic = this.tinode.getTopic(topicName);\n\n if (topic) {\n var instance = this;\n topic.setMeta({\n tags: tags\n }).catch(function (err) {\n _this20.handleError(err.message, 'err');\n });\n }\n }\n }, {\n key: \"handleLogout\",\n value: function handleLogout() {\n updateFavicon(0);\n _lib_local_storage_js__WEBPACK_IMPORTED_MODULE_11__[\"default\"].removeItem('auth-token');\n\n if (this.tinode) {\n this.tinode.onDisconnect = undefined;\n this.tinode.disconnect();\n }\n\n this.setState(this.getBlankState());\n this.tinode = TinodeWeb.tnSetup(this.state.serverAddress, this.state.transport);\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo('');\n }\n }, {\n key: \"handleLeaveUnsubRequest\",\n value: function handleLeaveUnsubRequest(topicName) {\n var _this21 = this;\n\n var topic = this.tinode.getTopic(topicName);\n\n if (!topic) {\n return;\n }\n\n topic.leave(true).then(function (ctrl) {\n // Hide MessagesView and InfoView panels.\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].setUrlTopic(window.location.hash, ''));\n }).catch(function (err) {\n _this21.handleError(err.message, 'err');\n });\n }\n }, {\n key: \"handleDialogCancel\",\n value: function handleDialogCancel() {\n this.setState({\n dialogSelected: null\n });\n }\n }, {\n key: \"handleShowContextMenu\",\n value: function handleShowContextMenu(params, menuItems) {\n this.setState({\n contextMenuVisible: true,\n contextMenuClickAt: {\n x: params.x,\n y: params.y\n },\n contextMenuParams: params,\n contextMenuItems: menuItems || this.defaultTopicContextMenu(params.topicName),\n contextMenuBounds: ReactDOM.findDOMNode(this).getBoundingClientRect()\n });\n }\n }, {\n key: \"defaultTopicContextMenu\",\n value: function defaultTopicContextMenu(topicName) {\n var muted = false,\n blocked = false,\n subscribed = false,\n deleter = false;\n var topic = this.tinode.getTopic(topicName);\n\n if (topic) {\n if (topic.isSubscribed()) {\n subscribed = true;\n var acs = topic.getAccessMode();\n muted = acs && acs.isMuted();\n blocked = acs && !acs.isJoiner();\n deleter = acs && acs.isDeleter();\n }\n }\n\n return [subscribed ? {\n title: \"Info\",\n handler: this.handleShowInfoView\n } : null, subscribed ? 'messages_clear' : null, subscribed && deleter ? 'messages_clear_hard' : null, subscribed ? muted ? 'topic_unmute' : 'topic_mute' : null, subscribed ? blocked ? 'topic_unblock' : 'topic_block' : null, 'topic_delete'];\n }\n }, {\n key: \"handleHideContextMenu\",\n value: function handleHideContextMenu() {\n this.setState({\n contextMenuVisible: false,\n contextMenuClickAt: null,\n contextMenuParams: null,\n contextMenuBounds: null\n });\n }\n }, {\n key: \"handleShowInfoView\",\n value: function handleShowInfoView() {\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].addUrlParam(window.location.hash, 'info', true));\n this.setState({\n showInfoPanel: true\n });\n }\n }, {\n key: \"handleHideInfoView\",\n value: function handleHideInfoView() {\n _lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_12__[\"default\"].removeUrlParam(window.location.hash, 'info'));\n this.setState({\n showInfoPanel: false\n });\n }\n }, {\n key: \"handleMemberUpdateRequest\",\n value: function handleMemberUpdateRequest(topicName, added, removed) {\n var _this22 = this;\n\n if (!topicName) {\n return;\n }\n\n var topic = this.tinode.getTopic(topicName);\n\n if (!topic) {\n return;\n }\n\n if (added && added.length > 0) {\n added.map(function (uid) {\n topic.invite(uid, null).catch(function (err) {\n _this22.handleError(err.message, 'err');\n });\n });\n }\n\n if (removed && removed.length > 0) {\n removed.map(function (uid) {\n topic.delSubscription(uid).catch(function (err) {\n _this22.handleError(err.message, 'err');\n });\n });\n }\n }\n }, {\n key: \"handleValidateCredentialsRequest\",\n value: function handleValidateCredentialsRequest(cred, code) {\n this.setState({\n credMethod: cred,\n credCode: code\n });\n this.doLogin(null, null, {\n meth: cred,\n resp: code\n });\n }\n }, {\n key: \"handlePasswordResetRequest\",\n value: function handlePasswordResetRequest(method, value) {\n var _this23 = this;\n\n // If already connected, connnect() will return a resolved promise.\n this.tinode.connect().then(function () {\n return _this23.tinode.requestResetAuthSecret('basic', method, value);\n }).catch(function (err) {\n // Socket error\n _this23.handleError(err.message, 'err');\n });\n }\n }, {\n key: \"handleResetPassword\",\n value: function handleResetPassword(scheme, newPassword, token) {\n var _this24 = this;\n\n token = base64ReEncode(token);\n\n if (!token) {\n this.handleError(\"Invalid security token\", 'err');\n } else {\n this.tinode.connect().then(function () {\n return _this24.tinode.updateAccountBasic(null, null, newPassword, {\n token: token\n });\n }).catch(function (err) {\n // Socket error\n _this24.handleError(err.message, 'err');\n });\n }\n }\n }, {\n key: \"render\",\n value: function render() {\n return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(\"div\", {\n id: \"app-container\"\n }, this.state.contextMenuVisible ? react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_widgets_context_menu_jsx__WEBPACK_IMPORTED_MODULE_5__[\"default\"], {\n tinode: this.tinode,\n bounds: this.state.contextMenuBounds,\n clickAt: this.state.contextMenuClickAt,\n params: this.state.contextMenuParams,\n items: this.state.contextMenuItems,\n hide: this.handleHideContextMenu,\n onAction: this.handleContextMenuAction,\n onError: this.handleError\n }) : null, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_sidepanel_view_jsx__WEBPACK_IMPORTED_MODULE_8__[\"default\"], {\n tinode: this.tinode,\n connected: this.state.connected,\n displayMobile: this.state.displayMobile,\n hideSelf: this.state.displayMobile && this.state.mobilePanel !== 'sidepanel',\n state: this.state.sidePanelSelected,\n title: this.state.sidePanelTitle,\n avatar: this.state.sidePanelAvatar,\n login: this.state.login,\n myUserId: this.state.myUserId,\n loginDisabled: this.state.loginDisabled,\n errorText: this.state.errorText,\n errorLevel: this.state.errorLevel,\n topicSelected: this.state.topicSelected,\n chatList: this.state.chatList,\n credMethod: this.state.credMethod,\n credCode: this.state.credCode,\n transport: this.state.transport,\n messageSounds: this.state.messageSounds,\n desktopAlerts: this.state.desktopAlerts,\n desktopAlertsEnabled: this.state.desktopAlertsEnabled,\n serverAddress: this.state.serverAddress,\n onGlobalSettings: this.handleGlobalSettings,\n onSignUp: this.handleNewAccount,\n onSettings: this.handleSettings,\n onLoginRequest: this.handleLoginRequest,\n onCreateAccount: this.handleNewAccountRequest,\n onUpdateAccount: this.handleUpdateAccountRequest,\n onUpdateAccountTags: this.handleUpdateAccountTagsRequest,\n onTogglePushNotifications: this.togglePushToken,\n onToggleMessageSounds: this.handleToggleMessageSounds,\n onTopicSelected: this.handleTopicSelected,\n onCreateTopic: this.handleNewTopicRequest,\n onNewTopic: this.handleNewTopic,\n onLogout: this.handleLogout,\n onCancel: this.handleSidepanelCancel,\n onError: this.handleError,\n onValidateCredentials: this.handleValidateCredentialsRequest,\n onPasswordResetRequest: this.handlePasswordResetRequest,\n onResetPassword: this.handleResetPassword,\n onInitFind: this.tnInitFind,\n searchResults: this.state.searchResults,\n onSearchContacts: this.handleSearchContacts,\n showContextMenu: this.handleShowContextMenu\n }), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_messages_view_jsx__WEBPACK_IMPORTED_MODULE_7__[\"default\"], {\n tinode: this.tinode,\n connected: this.state.connected,\n online: this.state.topicSelectedOnline,\n acs: this.state.topicSelectedAcs,\n displayMobile: this.state.displayMobile,\n viewportWidth: this.state.viewportWidth,\n hideSelf: this.state.displayMobile && (this.state.mobilePanel !== 'topic-view' || this.state.showInfoPanel),\n topic: this.state.topicSelected,\n myUserId: this.state.myUserId,\n serverVersion: this.state.serverVersion,\n serverAddress: this.state.serverAddress,\n errorText: this.state.errorText,\n errorLevel: this.state.errorLevel,\n newGroupTopicParams: this.state.newGroupTopicParams,\n onHideMessagesView: this.handleHideMessagesView,\n onData: this.tnData,\n onError: this.handleError,\n onNewTopicCreated: this.handleNewTopicCreated,\n readTimerHandler: this.handleReadTimer,\n showContextMenu: this.handleShowContextMenu,\n sendMessage: this.handleSendMessage\n }), this.state.showInfoPanel ? react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_info_view_jsx__WEBPACK_IMPORTED_MODULE_6__[\"default\"], {\n tinode: this.tinode,\n connected: this.state.connected,\n displayMobile: this.state.displayMobile,\n topic: this.state.topicSelected,\n searchableContacts: this.state.searchableContacts,\n myUserId: this.state.myUserId,\n errorText: this.state.errorText,\n errorLevel: this.state.errorLevel,\n onTopicDescUpdate: this.handleTopicUpdateRequest,\n onCancel: this.handleHideInfoView,\n onChangePermissions: this.handleChangePermissions,\n onMemberUpdateRequest: this.handleMemberUpdateRequest,\n onLeaveTopic: this.handleLeaveUnsubRequest,\n onAddMember: this.handleManageGroupMembers,\n onTopicTagsUpdate: this.handleTagsUpdated,\n onInitFind: this.tnInitFind,\n onError: this.handleError,\n showContextMenu: this.handleShowContextMenu\n }) : null);\n }\n }], [{\n key: \"tnSetup\",\n value: function tnSetup(serverAddress, transport) {\n var tinode = new tinode_sdk__WEBPACK_IMPORTED_MODULE_4___default.a(_config_js__WEBPACK_IMPORTED_MODULE_9__[\"APP_NAME\"], serverAddress, _config_js__WEBPACK_IMPORTED_MODULE_9__[\"API_KEY\"], transport, Object(_lib_host_name_js__WEBPACK_IMPORTED_MODULE_10__[\"isSecureConnection\"])());\n tinode.enableLogging(true, true);\n return tinode;\n }\n }, {\n key: \"prepareSearchableContacts\",\n value: function prepareSearchableContacts(chatList, foundContacts) {\n var merged = {}; // For chatList topics merge only p2p topics and convert them to the\n // same format as foundContacts.\n\n var _iteratorNormalCompletion = true;\n var _didIteratorError = false;\n var _iteratorError = undefined;\n\n try {\n for (var _iterator = chatList[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {\n var c = _step.value;\n\n if (tinode_sdk__WEBPACK_IMPORTED_MODULE_4___default.a.topicType(c.topic) == 'p2p') {\n merged[c.topic] = {\n user: c.topic,\n updated: c.updated,\n public: c.public,\n private: c.private,\n acs: c.acs\n };\n }\n } // Add all foundCountacts if they have not been added already.\n\n } catch (err) {\n _didIteratorError = true;\n _iteratorError = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion && _iterator.return != null) {\n _iterator.return();\n }\n } finally {\n if (_didIteratorError) {\n throw _iteratorError;\n }\n }\n }\n\n var _iteratorNormalCompletion2 = true;\n var _didIteratorError2 = false;\n var _iteratorError2 = undefined;\n\n try {\n for (var _iterator2 = foundContacts[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {\n var _c = _step2.value;\n\n if (!merged[_c.user]) {\n merged[_c.user] = _c;\n }\n }\n } catch (err) {\n _didIteratorError2 = true;\n _iteratorError2 = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion2 && _iterator2.return != null) {\n _iterator2.return();\n }\n } finally {\n if (_didIteratorError2) {\n throw _iteratorError2;\n }\n }\n }\n\n return Object.values(merged);\n }\n }]);\n\n return TinodeWeb;\n}(react__WEBPACK_IMPORTED_MODULE_0___default.a.Component);\n\n\n;\n\n//# sourceURL=webpack:///./src/views/tinode-web.jsx?"); /***/ }), @@ -1251,4 +1321,5 @@ eval("/* (ignored) */\n\n//# sourceURL=webpack:///./lib/locales_(ignored)?"); /***/ }) -/******/ }); \ No newline at end of file +/******/ }); +}); \ No newline at end of file