= 0 && mode.indexOf(c) < 0) {\n // Permission is marked as inactive: hide unchecked permissions, disable checked permissions\n continue;\n }\n items.push(\n \n | {names[c]} | \n {skip.indexOf(c) < 0 ?\n = 0)} onChange={this.handleChange}/>\n :\n = 0)} />\n } | {this.props.compare ? \n = 0)}/>\n | : null}\n
\n );\n }\n\n return (\n \n {this.props.userTitle ?\n
: null}\n
\n
\n {this.props.compare ?\n \n | {this.props.modeTitle} | \n {this.props.compareTitle} | \n
:\n null}\n \n {items}\n
\n
\n
\n \n \n
\n
\n );\n }\n};\n\nexport default injectIntl(PermissionsEditor);\n","// TagManager: edit topic or user tags.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport ChipInput from './chip-input.jsx';\n\nimport { MAX_TAG_COUNT, MAX_TAG_LENGTH, MIN_TAG_LENGTH } from '../config.js';\nimport { arrayEqual } from '../lib/utils.js';\n\nexport default class TagManager extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n tags: this.props.tags,\n tagInput: '',\n activated: this.props.activated\n };\n\n this.handleTagInput = this.handleTagInput.bind(this);\n this.handleAddTag = this.handleAddTag.bind(this);\n this.handleRemoveTag = this.handleRemoveTag.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n static getDerivedStateFromProps(nextProps, prevState) {\n if (!arrayEqual(nextProps.tags, prevState.tags) && !prevState.activated) {\n return {tags: nextProps.tags};\n }\n return null;\n }\n\n handleTagInput(text) {\n this.setState({tagInput: text});\n if (text.length > 0) {\n const last = text[text.length-1];\n if (text[0] == '\"') {\n // This is a quoted string.\n if (text.length > 1 && last == '\"') {\n this.handleAddTag(text.substring(1, text.length-1));\n }\n } else if (last == ',' || last == ' ' || last == ';' || last == '\"') {\n // User entered ',', ' ' or ';'\n this.handleAddTag(text.substring(0, text.length-1).trim());\n }\n }\n }\n\n handleAddTag(tag) {\n const maxTagCount = this.props.tinode.getServerLimit('maxTagCount', MAX_TAG_COUNT);\n\n if (tag.length > 0 && this.state.tags.length < maxTagCount) {\n const tags = this.state.tags.slice(0);\n tags.push(tag);\n\n this.setState({tags: tags, tagInput: ''});\n if (this.props.onTagsChanged) {\n this.props.onTagsChanged(tags);\n }\n return tags;\n }\n return this.state.tags;\n }\n\n handleRemoveTag(tag, index) {\n const tags = this.state.tags.slice(0);\n tags.splice(index, 1);\n this.setState({tags: tags});\n if (this.props.onTagsChanged) {\n this.props.onTagsChanged(tags);\n }\n }\n\n handleSubmit() {\n // Add unprocessed input to tags, submit the list.\n this.props.onSubmit(this.handleAddTag(this.state.tagInput.trim()));\n this.setState({activated: false, tags: this.props.tags});\n }\n\n handleCancel() {\n this.setState({activated: false, tagInput: '', tags: this.props.tags});\n if (this.props.onCancel) {\n this.props.onCancel();\n }\n }\n\n render() {\n const minTagLength = this.props.tinode.getServerLimit('minTagLength', MIN_TAG_LENGTH);\n const maxTagLength = this.props.tinode.getServerLimit('maxTagLength', MAX_TAG_LENGTH);\n\n let tags = [];\n if (this.state.activated) {\n this.state.tags.map((tag) => {\n tags.push({user: tag, invalid: (tag.length < minTagLength || tag.length > maxTagLength)});\n });\n } else {\n this.state.tags.map((tag) => {\n tags.push({tag});\n });\n if (tags.length == 0) {\n tags = (\n \n \n \n );\n }\n }\n return (\n \n
\n \n
\n {this.state.activated ?\n
\n
{\n (add_tags_prompt) => \n }\n {this.props.onSubmit || this.props.onCancel ?\n
\n \n \n
\n : null}\n
\n :\n
\n }\n
\n );\n }\n};\n","// InfoView: panel with topic/user info.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport AvatarUpload from '../widgets/avatar-upload.jsx';\nimport BadgeList from '../widgets/badge-list.jsx';\nimport CheckBox from '../widgets/checkbox.jsx';\nimport ContactList from '../widgets/contact-list.jsx';\nimport ErrorPanel from '../widgets/error-panel.jsx';\nimport GroupManager from '../widgets/group-manager.jsx';\nimport InPlaceEdit from '../widgets/in-place-edit.jsx';\nimport MenuCancel from '../widgets/menu-cancel.jsx';\nimport MoreButton from '../widgets/more-button.jsx';\nimport PermissionsEditor from '../widgets/permissions-editor.jsx';\nimport TagManager from '../widgets/tag-manager.jsx';\n\nimport { MAX_TITLE_LENGTH, NO_ACCESS_MODE } from '../config.js';\n\nimport { makeImageDataUrl } from '../lib/blob-helpers.js';\nimport { arrayEqual, theCard } from '../lib/utils.js';\n\nconst messages = defineMessages({\n requested: {\n id: 'requested_permissions',\n defaultMessage: 'Requested',\n description: 'Title for permissions'\n },\n granted: {\n id: 'granted_permissions',\n defaultMessage: 'Granted',\n description: 'Title for permissions'\n },\n edit_permissions: {\n id: 'menu_item_edit_permissions',\n defaultMessage: 'Edit permissions',\n description: 'Menu item [Edit permissions]'\n },\n other_user: {\n id: 'label_other_user',\n defaultMessage: 'Other',\n description: 'Label for the other user when the user is unnamed'\n },\n clear_messages: {\n id: 'action_clear_messages',\n defaultMessage: 'Clear Messages',\n description: 'Flat button [Clear Messages] (soft-delete messages)'\n },\n clear_messages_warning: {\n id: 'clear_messages_warning',\n defaultMessage: 'Are you sure you want to clear all messages? It cannot be undone.',\n description: 'Alert dialog warning when deleting all messages.'\n },\n delete_messages: {\n id: 'action_delete_messages',\n defaultMessage: 'Clear Messages for All',\n description: 'Flat button [Clear for All] (hard-delete all messages)'\n },\n delete_messages_warning: {\n id: 'delete_messages_warning',\n defaultMessage: 'Are you sure you want to delete all messages for everyone? It cannot be undone.',\n description: 'Alert dialog warning when hard-deleting all messages.'\n },\n leave_chat: {\n id: 'action_leave_chat',\n defaultMessage: 'Leave Conversation',\n description: 'Flat button [Leave Conversation]'\n },\n leave_chat_warning: {\n id: 'leave_chat_warning',\n defaultMessage: 'Are you sure you want to leave this conversation?',\n description: 'Alert dialog warning when unsubscribing from a chat.'\n },\n block_contact: {\n id: 'action_block_contact',\n defaultMessage: \"Block Contact\",\n description: \"Flat button [Block Contact]\"\n },\n block_contact_warning: {\n id: 'block_contact_warning',\n defaultMessage: 'Are you sure you want to block this contact?',\n description: 'Alert dialog warning when blocking a contact.'\n },\n report_chat: {\n id: 'action_report_chat',\n defaultMessage: 'Report Conversation',\n description: 'Flat button [Report Group]'\n },\n report_chat_warning: {\n id: 'report_chat_warning',\n defaultMessage: 'Are you sure you want to block and report this conversation?',\n description: 'Alert dialog warning when reporting a conversation for abuse'\n },\n});\n\nclass InfoView extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n topic: null,\n owner: false,\n admin: false,\n sharer: false,\n deleter: false,\n muted: false,\n address: null,\n groupTopic: undefined,\n channel: undefined,\n fullName: undefined,\n avatar: null,\n private: null,\n selectedContact: null,\n access: null,\n modeGiven: null,\n modeWant: null,\n modeGiven2: null, // P2P topic, the other user mode given\n modeWant2: null, // P2P topic, the other user mode want\n auth: null,\n anon: null,\n contactList: [],\n tags: [],\n trustedBadges: [],\n showMemberPanel: false,\n showPermissionEditorFor: undefined,\n moreInfoExpanded: false,\n previousMetaDesc: undefined,\n previousSubsUpdated: undefined,\n previousTagsUpdated: undefined\n };\n\n this.resetSubs = this.resetSubs.bind(this);\n this.resetDesc = this.resetDesc.bind(this);\n this.onMetaDesc = this.onMetaDesc.bind(this);\n this.onSubsUpdated = this.onSubsUpdated.bind(this);\n this.onTagsUpdated = this.onTagsUpdated.bind(this);\n this.handleFullNameUpdate = this.handleFullNameUpdate.bind(this);\n this.handlePrivateUpdate = this.handlePrivateUpdate.bind(this);\n this.handleImageChanged = this.handleImageChanged.bind(this);\n this.handleMuted = this.handleMuted.bind(this);\n this.handlePermissionsChanged = this.handlePermissionsChanged.bind(this);\n this.handleLaunchPermissionsEditor = this.handleLaunchPermissionsEditor.bind(this);\n this.handleHidePermissionsEditor = this.handleHidePermissionsEditor.bind(this);\n this.handleShowAddMembers = this.handleShowAddMembers.bind(this);\n this.handleHideAddMembers = this.handleHideAddMembers.bind(this);\n this.handleMemberUpdateRequest = this.handleMemberUpdateRequest.bind(this);\n this.handleDeleteMessages = this.handleDeleteMessages.bind(this);\n this.handleLeave = this.handleLeave.bind(this);\n this.handleBlock = this.handleBlock.bind(this);\n this.handleReport = this.handleReport.bind(this);\n this.handleMemberSelected = this.handleMemberSelected.bind(this);\n this.handleMoreInfo = this.handleMoreInfo.bind(this);\n this.handleTagsUpdated = this.handleTagsUpdated.bind(this);\n this.handleContextMenu = this.handleContextMenu.bind(this);\n }\n\n // No need to separately handle component mount.\n componentDidUpdate(props) {\n const topic = this.props.tinode.getTopic(props.topic);\n if (!topic) {\n return;\n }\n\n if (this.onMetaDesc != topic.onMetaDesc) {\n this.previousMetaDesc = topic.onMetaDesc;\n topic.onMetaDesc = this.onMetaDesc;\n\n this.previousSubsUpdated = topic.onSubsUpdated;\n topic.onSubsUpdated = this.onSubsUpdated;\n\n if (topic.getType() == 'grp') {\n this.previousTagsUpdated = topic.onTagsUpdated;\n topic.onTagsUpdated = this.onTagsUpdated;\n } else {\n this.previousTagsUpdated = undefined;\n }\n }\n\n if (this.state.topic != props.topic) {\n this.setState({topic: props.topic});\n this.resetDesc(topic, props);\n this.resetSubs(topic, props);\n }\n }\n\n componentWillUnmount() {\n const topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n this.setState({topic: null});\n topic.onMetaDesc = this.previousMetaDesc;\n topic.onSubsUpdated = this.previousSubsUpdated;\n topic.onTagsUpdated = this.previousTagsUpdated;\n }\n\n resetSubs(topic, props) {\n const newState = {contactList: []};\n if (topic.getType() == 'p2p') {\n // Fetch the other party in the p2p conversation.\n // Topic may not be ready yet, so check if user is found.\n const user2 = topic.subscriber(props.topic);\n if (user2) {\n newState.modeGiven2 = user2.acs.getGiven();\n newState.modeWant2 = user2.acs.getWant();\n } else {\n newState.modeGiven2 = NO_ACCESS_MODE;\n newState.modeWant2 = NO_ACCESS_MODE;\n }\n } else {\n topic.subscribers((sub) => {\n newState.contactList.push(sub);\n }, this);\n }\n\n this.setState(newState);\n }\n\n resetDesc(topic, props) {\n const defacs = topic.getDefaultAccess() || {};\n const acs = topic.getAccessMode();\n\n const badges = [];\n if (topic.trusted) {\n for (const [key, val] of Object.entries(topic.trusted)) {\n if (val) {\n badges.push(key);\n }\n }\n }\n this.setState({\n owner: acs && acs.isOwner(),\n admin: acs && acs.isAdmin(),\n sharer: acs && acs.isSharer(),\n deleter: acs && acs.isDeleter(),\n muted: acs && acs.isMuted(),\n\n fullName: topic.public ? topic.public.fn : undefined,\n avatar: makeImageDataUrl(topic.public ? topic.public.photo : null),\n trustedBadges: badges,\n private: topic.private ? topic.private.comment : null,\n address: topic.name,\n groupTopic: topic.isGroupType(),\n channel: topic.isChannelType(),\n showMemberPanel: false,\n access: acs ? acs.getMode() : undefined,\n modeGiven: acs ? acs.getGiven() : undefined,\n modeWant: acs ? acs.getWant() : undefined,\n auth: defacs.auth,\n anon: defacs.anon\n });\n\n if (topic.getType() == 'grp' && acs && acs.isOwner()) {\n // Requesting tags: owner is editing the topic.\n topic.getMeta(topic.startMetaQuery().withTags().build());\n }\n }\n\n onMetaDesc(desc) {\n const topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n this.resetDesc(topic, this.props);\n\n if (this.previousMetaDesc && this.previousMetaDesc != this.onMetaDesc) {\n this.previousMetaDesc(desc);\n }\n }\n\n onSubsUpdated(subs) {\n const topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n this.resetSubs(topic, this.props);\n\n if (this.previousSubsUpdated && this.previousSubsUpdated != this.onSubsUpdated) {\n this.previousSubsUpdated(subs);\n }\n }\n\n onTagsUpdated(tags) {\n this.setState({tags: tags});\n\n if (this.previousTagsUpdated && this.previousTagsUpdated != this.onTagsUpdated) {\n this.previousTagsUpdated();\n }\n }\n\n handleFullNameUpdate(fn) {\n fn = fn.trim().substring(0, MAX_TITLE_LENGTH);\n if (this.state.fullName !== fn) {\n this.setState({fullName: fn});\n this.props.onTopicDescUpdate(this.props.topic, theCard(fn, null), null);\n }\n }\n\n handlePrivateUpdate(comment) {\n comment = comment.trim().substring(0, MAX_TITLE_LENGTH);\n if (this.state.private !== comment) {\n this.setState({private: comment});\n this.props.onTopicDescUpdate(this.props.topic, null, comment || Tinode.DEL_CHAR);\n }\n }\n\n handleImageChanged(img) {\n this.setState({avatar: img});\n this.props.onTopicDescUpdate(this.props.topic, theCard(null, img || Tinode.DEL_CHAR), null);\n }\n\n handleMuted(ignored, checked) {\n this.setState({muted: checked});\n this.props.onChangePermissions(this.props.topic, checked ? '-P' : '+P');\n }\n\n handlePermissionsChanged(perm) {\n switch (this.state.showPermissionEditorFor) {\n case 'auth':\n this.props.onTopicDescUpdate(this.props.topic, null, null, {auth: perm});\n break;\n case 'anon':\n this.props.onTopicDescUpdate(this.props.topic, null, null, {anon: perm});\n break;\n case 'mode':\n case 'want':\n this.props.onChangePermissions(this.props.topic, perm);\n break;\n case 'given':\n this.props.onChangePermissions(this.props.topic, perm, this.props.topic);\n break;\n case 'user':\n this.props.onChangePermissions(this.props.topic, perm, this.state.userPermissionsEdited);\n break;\n }\n\n this.setState({showPermissionEditorFor: undefined});\n }\n\n handleLaunchPermissionsEditor(which, uid) {\n const {formatMessage} = this.props.intl;\n let toEdit, toCompare, toSkip, titleEdit, titleCompare, userTitle, userAvatar;\n switch (which) {\n case 'mode':\n toEdit = this.state.access;\n break;\n case 'want':\n toEdit = this.state.modeWant;\n toCompare = this.state.modeGiven;\n if (this.state.owner) {\n // Don't allow owner to unset 'O' permission. All other permissions are OK.\n toSkip = 'O';\n } else {\n // Allow accepting any of 'ASDO' permissions but don't allow asking for them.\n toSkip = Tinode.AccessMode.encode(Tinode.AccessMode.diff('ASDO', this.state.modeGiven));\n if (this.state.channel) {\n // Channels are read-only.\n toSkip += 'W';\n }\n }\n titleEdit = formatMessage(messages.requested);\n titleCompare = formatMessage(messages.granted);\n break;\n case 'given':\n toEdit = this.state.modeGiven2;\n toCompare = this.state.modeWant2;\n toSkip = this.state.groupTopic ? (this.state.owner ? '' : 'O') : 'ASDO';\n titleEdit = formatMessage(messages.granted);\n titleCompare = formatMessage(messages.requested);\n break;\n case 'auth':\n toEdit = this.state.auth;\n toSkip = 'O';\n break;\n case 'anon':\n toEdit = this.state.anon;\n toSkip = 'O';\n break;\n case 'user': {\n const topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n const user = topic.subscriber(uid);\n if (!user || !user.acs) {\n return;\n }\n toEdit = user.acs.getGiven();\n toCompare = user.acs.getWant();\n toSkip = this.state.owner ? '' : 'O';\n titleEdit = formatMessage(messages.granted);\n titleCompare = formatMessage(messages.requested);\n if (user.public) {\n userTitle = user.public.fn;\n userAvatar = user.public.photo;\n }\n break;\n }\n default:\n console.log(\"Unknown permission editing mode '\" + which + \"'\");\n break;\n }\n this.setState({\n showPermissionEditorFor: which,\n userPermissionsEdited: uid,\n userPermissionsTitle: userTitle,\n userPermissionsAvatar: userAvatar,\n editedPermissions: toEdit,\n immutablePermissions: toCompare,\n editedPermissionsTitle: titleEdit,\n immutablePermissionsTitle: titleCompare,\n editedPermissionsSkipped: toSkip,\n });\n }\n\n handleHidePermissionsEditor() {\n this.setState({showPermissionEditorFor: undefined});\n }\n\n handleShowAddMembers(e) {\n e.preventDefault();\n this.props.onInitFind();\n this.setState({showMemberPanel: true});\n }\n\n handleHideAddMembers() {\n this.setState({showMemberPanel: false});\n }\n\n handleMemberUpdateRequest(members, added, removed) {\n this.props.onMemberUpdateRequest(this.props.topic, added, removed);\n this.setState({showMemberPanel: false});\n }\n\n handleDeleteMessages(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(this.state.deleter ? messages.delete_messages : messages.clear_messages), // title\n formatMessage(this.state.deleter ? messages.delete_messages_warning : messages.clear_messages_warning), // content\n (() => { this.props.onDeleteMessages(this.props.topic); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n\n handleLeave(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(messages.leave_chat), // title\n formatMessage(messages.leave_chat_warning), // content\n (() => { this.props.onLeaveTopic(this.props.topic); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n\n handleBlock(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(messages.block_contact), // title\n formatMessage(messages.block_contact_warning), // content\n (() => { this.props.onBlockTopic(this.props.topic); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n\n handleReport(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(messages.report_chat), // title\n formatMessage(messages.report_chat_warning), // content\n (() => { this.props.onReportTopic(this.props.topic); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n\n handleMemberSelected(uid) {\n this.setState({selectedContact: uid});\n }\n\n handleMoreInfo(open) {\n this.setState({moreInfoExpanded: open});\n }\n\n handleTagsUpdated(tags) {\n if (!arrayEqual(this.state.tags.slice(0), tags.slice(0))) {\n this.props.onTopicTagsUpdate(this.props.topic, tags);\n }\n }\n\n handleContextMenu(params) {\n const {formatMessage} = this.props.intl;\n const instance = this;\n const topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n const user = topic.subscriber(params.topicName);\n if (!user || !user.acs) {\n return;\n }\n\n const menuItems = [\n {title: formatMessage(messages.edit_permissions), handler: function() {\n instance.handleLaunchPermissionsEditor('user', params.topicName);\n }},\n 'member_delete',\n user.acs.isMuted() ? 'member_unmute' : 'member_mute',\n user.acs.isJoiner() ? 'member_block' : 'member_unblock'\n ];\n this.props.showContextMenu({\n topicName: this.props.topic,\n x: params.x,\n y: params.y,\n user: params.topicName}, menuItems);\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n\n return (\n \n
\n {this.props.displayMobile ?\n
: null}\n {this.state.showMemberPanel ?\n
\n :\n this.state.showPermissionEditorFor ?\n
\n :\n
\n
\n
\n
\n
Unknown}\n readOnly={!this.state.owner}\n value={this.state.fullName}\n required={true}\n onFinished={this.handleFullNameUpdate} />
\n
\n \n
\n
\n {\n (private_placeholder) => \n }\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n
\n
{\n (more) => \n }\n {this.state.moreInfoExpanded ?\n
\n
\n \n {this.state.address}\n
\n {this.state.groupTopic ?\n
\n \n \n {this.state.access}\n \n
\n :\n
\n
\n \n
\n
\n
\n \n {this.state.access}\n
\n
{this.state.fullName ? this.state.fullName : formatMessage(messages.other_user)}:\n \n {this.state.modeGiven2}\n \n
\n
\n
\n }\n {this.state.sharer && (this.state.auth || this.state.anon) ?\n
\n
\n \n
\n
\n
Auth: {this.state.admin ?\n \n {this.state.auth}\n \n :\n {this.state.auth}\n }\n
\n
Anon: {this.state.admin ?\n \n {this.state.anon}\n \n :\n {this.state.anon}\n }\n
\n
\n
\n :\n null\n }\n
\n :\n null\n }\n
\n
\n {this.state.owner ?\n <>\n
{\n (tags) => \n }\n
\n >\n :\n null\n }\n
\n {this.state.groupTopic && this.state.sharer ?\n <>\n
\n
\n
\n \n
\n
\n
{\n (no_members) => \n }\n
\n >\n :\n null\n }\n
\n }\n
\n );\n }\n};\n\nexport default injectIntl(InfoView);\n","// File uload/download progress indicator with a cancel inside.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nexport default class FileProgress extends React.PureComponent {\n render() {\n return (\n \n );\n }\n}\n","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport FileProgress from './file-progress.jsx';\nimport { bytesToHumanSize } from '../lib/strformat.js';\nimport { isUrlRelative, sanitizeUrl } from '../lib/utils.js';\n\nexport default class Attachment extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n downloader: null,\n progress: 0\n };\n\n this.downloadFile = this.downloadFile.bind(this);\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n downloadFile(url, filename, mimetype) {\n if (!url) {\n this.props.onError(\"Invalid download URL '\" + url + \"'\");\n return;\n }\n\n const downloader = this.props.tinode.getLargeFileHelper();\n this.setState({downloader: downloader});\n downloader.download(url, filename, mimetype,\n (loaded) => {\n this.setState({progress: loaded / this.props.size});\n },\n (err) => {\n this.props.onError(err, 'err');\n }\n ).then(() => {\n this.setState({downloader: null, progress: 0});\n }).catch((err) => {\n if (err) {\n this.props.onError(\"Error downloading file: \" + err.message, 'err');\n }\n this.setState({downloader: null, progress: 0});\n });\n }\n\n handleCancel() {\n if (this.props.uploading) {\n this.props.onCancelUpload();\n } else if (this.state.downloader) {\n this.state.downloader.cancel();\n }\n }\n\n render() {\n let filename = this.props.filename || 'file_attachment';\n if (filename.length > 36) {\n filename = filename.substr(0, 16) + '...' + filename.substr(-16);\n }\n let size = this.props.size > 0 ?\n ({bytesToHumanSize(this.props.size)}) :\n null;\n\n // Detect if the download URL is relative or absolute.\n // If the URL is relative use LargeFileHelper to attach authentication\n // credentials to the request.\n let url, helperFunc;\n if (!this.props.uploading && !this.state.downloader && isUrlRelative(this.props.downloadUrl)) {\n // Relative URL. Use download helper.\n url = '#';\n helperFunc = (e) => {\n e.preventDefault();\n this.downloadFile(this.props.downloadUrl, this.props.filename, this.props.mimetype);\n };\n } else {\n url = sanitizeUrl(this.props.downloadUrl);\n helperFunc = null;\n }\n const downloadWidget = <>file_download >;\n return (\n \n
insert_drive_file
\n
\n
{filename} {size}
\n {this.props.uploading || this.state.downloader ?\n
\n :\n
\n }\n
\n
\n );\n }\n};\n","// Image view with progress bar and a cancel button.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport FileProgress from './file-progress.jsx';\n\nexport default class UploadingImage extends React.PureComponent {\n constructor(props) {\n super(props);\n }\n\n render() {\n return (\n \n {React.createElement('img', this.props)}\n
\n \n
\n
\n );\n }\n};\n","// Single message, sent or received.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\nimport { Drafty } from 'tinode-sdk';\n\nimport Attachment from './attachment.jsx';\nimport LetterTile from './letter-tile.jsx';\nimport ReceivedMarker from './received-marker.jsx'\nimport UploadingImage from './uploading-image.jsx'\nimport { sanitizeImageUrl, sanitizeUrl } from '../lib/utils.js';\n\nexport default class ChatMessage extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n progress: 0\n };\n\n if (props.uploader) {\n props.uploader.onProgress = this.handleProgress.bind(this);\n }\n\n this.handleImagePreview = this.handleImagePreview.bind(this);\n this.handleFormButtonClick = this.handleFormButtonClick.bind(this);\n this.handleContextClick = this.handleContextClick.bind(this);\n this.handleCancelUpload = this.handleCancelUpload.bind(this);\n }\n\n handleImagePreview(e) {\n e.preventDefault();\n this.props.onImagePreview({\n url: e.target.src,\n filename: e.target.title,\n width: e.target.dataset.width,\n height: e.target.dataset.height,\n size: e.target.dataset.size,\n type: e.target.dataset.mime\n });\n }\n\n handleFormButtonClick(e) {\n e.preventDefault();\n const data = {\n seq: this.props.seq\n };\n data.resp = {};\n if (e.target.dataset.name) {\n data.resp[e.target.dataset.name] = e.target.dataset.val ? e.target.dataset.val :\n e.target.dataset.val === undefined ? 1 : '' + e.target.dataset.val;\n }\n if (e.target.dataset.act == 'url') {\n data.ref = sanitizeUrl(e.target.dataset.ref) || 'about:blank';\n }\n const text = e.target.dataset.title || 'unknown';\n this.props.onFormResponse(e.target.dataset.act, text, data);\n }\n\n handleContextClick(e) {\n e.preventDefault();\n e.stopPropagation();\n const menuItems = this.props.received == Tinode.MESSAGE_STATUS_FAILED ? ['menu_item_send_retry'] : [];\n this.props.showContextMenu({ seq: this.props.seq, content: this.props.content,\n y: e.pageY, x: e.pageX }, menuItems);\n }\n\n handleProgress(ratio) {\n this.setState({progress: ratio});\n }\n\n handleCancelUpload() {\n this.props.onCancelUpload(this.props.seq, this.props.uploader);\n }\n\n render() {\n const sideClass = this.props.deleted ? 'center' :\n (this.props.sequence + ' ' + (this.props.response ? 'left' : 'right'));\n const bubbleClass = (this.props.sequence == 'single' || this.props.sequence == 'last') ? 'bubble tip' : 'bubble';\n const avatar = this.props.deleted ? null : (this.props.userAvatar || true);\n const fullDisplay = (this.props.userFrom && this.props.response && !this.props.deleted &&\n (this.props.sequence == 'single' || this.props.sequence == 'last'));\n\n let content = this.props.content;\n const attachments = [];\n if (this.props.mimeType == Drafty.getContentType() && Drafty.isValid(content)) {\n Drafty.attachments(content, function(att, i) {\n if (att.mime == 'application/json') {\n // Don't show json objects as attachments.\n // They are not meant for users.\n return;\n }\n attachments.push();\n }, this);\n content = React.createElement(React.Fragment, null, Drafty.format(content, draftyFormatter, this));\n } else if (this.props.deleted) {\n // Message represents a range of deleted messages.\n content = <>block \n \n >\n } else if (typeof content != 'string') {\n content = <>\n error_outline \n \n >\n }\n\n return (\n \n {this.props.userFrom && this.props.response ?\n \n {fullDisplay ?\n :\n null}\n
:\n null}\n \n
\n
\n
\n {content}\n {attachments}\n
\n {this.props.timestamp ?\n
\n : null}\n
\n {this.props.deleted || !this.props.showContextMenu ?\n null :\n
\n \n expand_more\n \n \n }\n
\n {fullDisplay ?\n
\n {\n (notFound) => {return this.props.userName || {notFound}}\n }\n
\n : null\n }\n
\n \n );\n }\n};\n\n// Convert Drafty object to a tree of React elements.\nimport { BROKEN_IMAGE_SIZE, REM_SIZE } from '../config.js';\nimport { fitImageSize } from '../lib/blob-helpers.js';\n\n// Converts Drafty elements into React classes.\n// 'this' is set by the caller.\nfunction draftyFormatter(style, data, values, key) {\n if (style == 'EX') {\n // attachments are handled elsewhere.\n return null;\n }\n\n let el = Drafty.tagName(style);\n if (el) {\n const attr = Drafty.attrValue(style, data) || {};\n attr.key = key;\n switch (style) {\n case 'HL':\n // Highlighted text. Assign class name.\n attr.className = 'highlight';\n break;\n case 'IM':\n // Additional processing for images\n if (data) {\n attr.className = 'inline-image';\n const dim = fitImageSize(data.width, data.height,\n Math.min(this.props.viewportWidth - REM_SIZE * 6.5, REM_SIZE * 34.5), REM_SIZE * 24, false) ||\n {dstWidth: BROKEN_IMAGE_SIZE, dstHeight: BROKEN_IMAGE_SIZE};\n attr.style = {\n width: dim.dstWidth + 'px',\n height: dim.dstHeight + 'px',\n // Looks like a Chrome bug: broken image does not respect 'width' and 'height'.\n minWidth: dim.dstWidth + 'px',\n minHeight: dim.dstHeight + 'px'\n };\n if (!Drafty.isProcessing(data)) {\n attr.src = this.props.tinode.authorizeURL(sanitizeImageUrl(attr.src));\n attr.alt = data.name;\n if (attr.src) {\n attr.onClick = this.handleImagePreview;\n attr.className += ' image-clickable';\n attr.loading = 'lazy';\n } else {\n attr.src = 'img/broken_image.png';\n }\n } else {\n // Use custom element instead of
.\n el = UploadingImage;\n }\n }\n break;\n case 'BN':\n // Button\n attr.onClick = this.handleFormButtonClick;\n let inner = React.Children.map(values, (child) => {\n return typeof child == 'string' ? child : undefined;\n });\n if (!inner || inner.length == 0) {\n inner = [attr.name]\n }\n // Get text which will be sent back when the button is clicked.\n attr['data-title'] = inner.join('');\n break;\n case 'FM':\n // Form\n attr.className = 'bot-form';\n break;\n case 'RW':\n // Form element formatting is dependent on element content.\n break;\n default:\n if (el == '_UNKN') {\n // Unknown element.\n // TODO: make it prettier.\n el = <>extension>;\n }\n break;\n }\n return React.createElement(el, attr, values);\n } else {\n return values;\n }\n};\n","// Send message form.\nimport React from 'react';\nimport { defineMessages, injectIntl } from 'react-intl';\nimport { Drafty } from 'tinode-sdk';\n\nimport { KEYPRESS_DELAY } from '../config.js';\nimport { filePasted } from '../lib/blob-helpers.js';\n\nconst messages = defineMessages({\n messaging_disabled: {\n id: 'messaging_disabled_prompt',\n defaultMessage: 'Messaging disabled',\n description: 'Prompt in SendMessage in read-only topic'\n },\n type_new_message: {\n id: 'new_message_prompt',\n defaultMessage: 'New message',\n description: 'Prompt in send message field'\n },\n add_image_caption: {\n id: 'image_caption_prompt',\n defaultMessage: 'Image caption',\n description: 'Prompt in SendMessage for attached image'\n },\n file_attachment_too_large: {\n id: 'file_attachment_too_large',\n defaultMessage: 'The file size {size} exceeds the {limit} limit.',\n description: 'Error message when attachment is too large'\n },\n cannot_initiate_upload: {\n id: 'cannot_initiate_file_upload',\n defaultMessage: 'Cannot initiate file upload.',\n description: 'Generic error messagewhen attachment fails'\n },\n});\n\nclass SendMessage extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n message: '',\n // Make initial keypress time as if it happened 5001 milliseconds in the past.\n keypressTimestamp: new Date().getTime() - KEYPRESS_DELAY - 1\n };\n\n this.handlePasteEvent = this.handlePasteEvent.bind(this);\n this.handleAttachImage = this.handleAttachImage.bind(this);\n this.handleAttachFile = this.handleAttachFile.bind(this);\n this.handleSend = this.handleSend.bind(this);\n this.handleKeyPress = this.handleKeyPress.bind(this);\n this.handleMessageTyping = this.handleMessageTyping.bind(this);\n }\n\n componentDidMount() {\n if (this.messageEditArea) {\n this.messageEditArea.addEventListener('paste', this.handlePasteEvent, false);\n }\n }\n\n componentWillUnmount() {\n if (this.messageEditArea) {\n this.messageEditArea.removeEventListener('paste', this.handlePasteEvent, false);\n }\n }\n\n componentDidUpdate() {\n if (this.messageEditArea) {\n this.messageEditArea.focus();\n }\n }\n\n handlePasteEvent(e) {\n if (this.props.disabled) {\n return;\n }\n // FIXME: handle large files too.\n if (filePasted(e,\n (bits, mime, width, height, fname) => {\n this.props.onAttachImage(mime, bits, width, height, fname);\n },\n (mime, bits, fname) => {\n this.props.onAttachFile(mime, bits, fname);\n },\n this.props.onError)) {\n\n // If a file was pasted, don't paste base64 data into input field.\n e.preventDefault();\n }\n }\n\n handleAttachImage(e) {\n if (e.target.files && e.target.files.length > 0) {\n this.props.onAttachImage(e.target.files[0]);\n }\n // Clear the value so the same file can be uploaded again.\n e.target.value = '';\n }\n\n handleAttachFile(e) {\n const {formatMessage} = this.props.intl;\n if (e.target.files && e.target.files.length > 0) {\n this.props.onAttachFile(e.target.files[0]);\n }\n // Clear the value so the same file can be uploaded again.\n e.target.value = '';\n }\n\n handleSend(e) {\n e.preventDefault();\n const message = this.state.message.trim();\n if (message || this.props.acceptBlank || this.props.noInput) {\n this.props.onSendMessage(message);\n this.setState({message: ''});\n }\n }\n\n /* Send on Enter key */\n handleKeyPress(e) {\n // Remove this if you don't want Enter to trigger send\n if (e.key === 'Enter') {\n // Have Shift-Enter insert a line break instead\n if (!e.shiftKey) {\n e.preventDefault();\n e.stopPropagation();\n\n this.handleSend(e);\n }\n }\n }\n\n handleMessageTyping(e) {\n const newState = {message: e.target.value};\n if (this.props.onKeyPress) {\n const now = new Date().getTime();\n if (now - this.state.keypressTimestamp > KEYPRESS_DELAY) {\n this.props.onKeyPress();\n newState.keypressTimestamp = now;\n }\n }\n this.setState(newState);\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n const prompt = this.props.disabled ?\n formatMessage(messages.messaging_disabled) :\n (this.props.messagePrompt ?\n formatMessage(messages[this.props.messagePrompt]) :\n formatMessage(messages.type_new_message));\n return (\n \n );\n }\n};\n\nexport default injectIntl(SendMessage);\n","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\nimport SendMessage from '../widgets/send-message.jsx';\n\nimport { bytesToHumanSize } from '../lib/strformat.js';\n\n// Get material icon name from mime type.\nfunction iconFromMime(mime) {\n // If more icons become available in material icons, add them to this mime-to-icon mapping.\n const mimeToIcon = {default: 'insert_drive_file', image: 'image', text: 'description', video: 'theaters'};\n\n return mimeToIcon[mime] || mimeToIcon[(mime || '').split('/')[0]] || mimeToIcon['default'];\n}\n\nexport default class DocPreview extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleSendDoc = this.handleSendDoc.bind(this);\n }\n\n handleSendDoc(caption) {\n this.props.onClose();\n this.props.onSendMessage(this.props.content.file);\n }\n\n render() {\n if (!this.props.content) {\n return null;\n }\n return (\n \n
\n
\n
\n
{iconFromMime(this.props.content.type)}\n
{this.props.content.type || 'application/octet-stream'}
\n
{bytesToHumanSize(this.props.content.size)}
\n
\n
\n
\n
\n );\n }\n};\n","// GroupSubs: a list of group subscribers currently online.\n// Show in the topic title bar\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport { MAX_ONLINE_IN_TOPIC } from '../config.js'\n\nimport LetterTile from './letter-tile.jsx';\nimport { makeImageDataUrl } from '../lib/blob-helpers.js';\n\nexport default class GroupSubs extends React.Component {\n constructor(props) {\n super(props);\n }\n\n render() {\n const usersOnline = [];\n const totalCount = (this.props.subscribers || []).length;\n const countToShow = Math.min(MAX_ONLINE_IN_TOPIC, totalCount);\n\n (this.props.subscribers || []).some((sub) => {\n usersOnline.push(\n \n \n
\n );\n return usersOnline.length == countToShow;\n });\n\n return (\n {usersOnline} {totalCount > countToShow ?\n \n \n : null}\n
\n );\n }\n};\n","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\nimport SendMessage from '../widgets/send-message.jsx';\n\nimport { REM_SIZE } from '../config.js';\nimport { fitImageSize } from '../lib/blob-helpers.js';\nimport { bytesToHumanSize } from '../lib/strformat.js';\n\nexport default class ImagePreview extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n width: 0,\n height: 0\n };\n\n this.handleSendImage = this.handleSendImage.bind(this);\n }\n\n assignWidth(node) {\n if (node && !this.state.width) {\n const bounds = node.getBoundingClientRect();\n this.setState({\n width: bounds.width | 0,\n height: bounds.height | 0\n });\n }\n }\n\n handleSendImage(caption) {\n this.props.onClose();\n this.props.onSendMessage(caption, this.props.content.blob);\n }\n\n render() {\n if (!this.props.content) {\n return null;\n }\n\n const dim = fitImageSize(this.props.content.width, this.props.content.height,\n this.state.width, this.state.height, false);\n const size = dim ? { width: dim.dstWidth + 'px', height: dim.dstHeight + 'px' } :\n ((this.props.content.width > this.props.content.height) ? {width: '100%'} : {height: '100%'});\n size.maxWidth = '100%';\n size.maxHeight = '100%';\n\n let filename = this.props.content.filename;\n // Average font aspect ratio is ~0.5; File name takes 1/3 of the viewport width.\n const maxlength = Math.max(((this.state.width / REM_SIZE / 1.5) | 0) - 2, 12);\n if (filename.length > maxlength) {\n filename = filename.slice(0, maxlength/2 - 1) + '…' + filename.slice(1 - maxlength/2);\n }\n const width = this.props.content.width || '-';\n const height = this.props.content.height || '-';\n return (\n \n
\n
this.assignWidth(node)}>\n

\n
\n {this.props.onSendMessage ?\n
\n :\n }\n
\n );\n }\n};\n","// A single topic or user.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nexport default class Invitation extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleButtonAction = this.handleButtonAction.bind(this);\n }\n\n handleButtonAction(evt, data) {\n evt.preventDefault();\n this.props.onAction(data);\n }\n\n render() {\n return (\n \n
\n \n
\n
\n \n \n \n
\n
\n );\n }\n};\n","/* This is just a static page to display when no conversation is selected. */\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport { APP_NAME } from '../config.js';\n\nexport default class LogoView extends React.PureComponent {\n render() {\n const version = APP_NAME + ' (' + Tinode.getLibrary() + ')';\n return (\n \n
\n
\n
\n Tinode Web
\n \n
{version}
\n
{this.props.serverVersion} ({this.props.serverAddress})
\n
\n
\n );\n }\n};\n","// Utility class for hash navigation.\n\n// Parse hash as in http://www.example.com/path#hash as if it were\n// path and arguments.\nexport default class HashNavigation {\n static parseUrlHash(hash) {\n // Split path from args, path -> parts[0], args->path[1]\n const parts = hash.split('?', 2);\n const params = {};\n let path = [];\n if (parts[0]) {\n path = parts[0].substr(1).split('/');\n }\n if (parts[1]) {\n parts[1].split('&').forEach(function(part) {\n let item = part.split('=');\n if (item[0]) {\n params[decodeURIComponent(item[0])] = decodeURIComponent(item[1]);\n }\n });\n }\n return {path: path, params: params};\n }\n\n static navigateTo(url) {\n window.location.hash = url;\n }\n\n static composeUrlHash(path, params) {\n let url = path.join('/');\n const args = [];\n for (const key in params) {\n if (params.hasOwnProperty(key)) {\n args.push(key + '=' + params[key]);\n }\n }\n if (args.length > 0) {\n url += '?' + args.join('&');\n }\n return url;\n }\n\n static addUrlParam(hash, key, value) {\n const parsed = this.parseUrlHash(hash);\n parsed.params[key] = value;\n return this.composeUrlHash(parsed.path, parsed.params);\n }\n\n static removeUrlParam(hash, key) {\n const parsed = this.parseUrlHash(hash);\n delete parsed.params[key];\n return this.composeUrlHash(parsed.path, parsed.params);\n }\n\n static setUrlSidePanel(hash, sidepanel) {\n const parsed = this.parseUrlHash(hash);\n parsed.path[0] = sidepanel;\n return this.composeUrlHash(parsed.path, parsed.params);\n }\n\n static setUrlTopic(hash, topic) {\n const parsed = this.parseUrlHash(hash);\n parsed.path[1] = topic;\n // Close InfoView on topic change.\n delete parsed.params.info;\n return this.composeUrlHash(parsed.path, parsed.params);\n }\n}\n","import React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\nconst Drafty = Tinode.Drafty;\n\nimport ChatMessage from '../widgets/chat-message.jsx';\nimport ContactBadges from '../widgets/contact-badges.jsx';\nimport DocPreview from '../widgets/doc-preview.jsx';\nimport ErrorPanel from '../widgets/error-panel.jsx';\nimport GroupSubs from '../widgets/group-subs.jsx';\nimport ImagePreview from '../widgets/image-preview.jsx';\nimport Invitation from '../widgets/invitation.jsx';\nimport LetterTile from '../widgets/letter-tile.jsx';\nimport LoadSpinner from '../widgets/load-spinner.jsx';\nimport LogoView from './logo-view.jsx';\nimport SendMessage from '../widgets/send-message.jsx';\n\nimport { DEFAULT_P2P_ACCESS_MODE, IMAGE_PREVIEW_DIM, KEYPRESS_DELAY, MESSAGES_PAGE,\n MAX_EXTERN_ATTACHMENT_SIZE, MAX_IMAGE_DIM, MAX_INBAND_ATTACHMENT_SIZE, READ_DELAY } from '../config.js';\nimport { SUPPORTED_IMAGE_FORMATS, blobToBase64, filePasted, fileToBase64,\n imageScaled, makeImageDataUrl } from '../lib/blob-helpers.js';\nimport HashNavigation from '../lib/navigation.js';\nimport { bytesToHumanSize, shortDateFormat } from '../lib/strformat.js';\n\n// Run timer with this frequency (ms) for checking notification queue.\nconst NOTIFICATION_EXEC_INTERVAL = 300;\n\nconst messages = defineMessages({\n online_now: {\n id: 'online_now',\n defaultMessage: 'online now',\n description: 'Indicator that the user or topic is currently online',\n },\n last_seen: {\n id: 'last_seen_timestamp',\n defaultMessage: 'Last seen',\n description: 'Label for the timestamp of when the user or topic was last online'\n },\n not_found: {\n id: 'title_not_found',\n defaultMessage: 'Not found',\n description: 'Title shown when topic is not found'\n },\n channel: {\n id: 'channel',\n defaultMessage: 'channel',\n description: 'Subtitle shown for channels in MessagesView instead of last seen'\n },\n file_attachment_too_large: {\n id: 'file_attachment_too_large',\n defaultMessage: 'The file size {size} exceeds the {limit} limit.',\n description: 'Error message when attachment is too large'\n }\n});\n\n// Checks if the access permissions are granted but not yet accepted.\nfunction isUnconfirmed(acs) {\n if (acs) {\n const ex = acs.getExcessive() || '';\n return acs.isJoiner('given') && (ex.includes('R') || ex.includes('W'));\n }\n return false;\n}\n\nfunction isPeerRestricted(acs) {\n if (acs) {\n const ms = acs.getMissing() || '';\n return acs.isJoiner('want') && (ms.includes('R') || ms.includes('W'));\n }\n return false;\n}\n\nclass MessagesView extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = MessagesView.getDerivedStateFromProps(props, {});\n\n this.leave = this.leave.bind(this);\n this.sendImageAttachment = this.sendImageAttachment.bind(this);\n this.sendFileAttachment = this.sendFileAttachment.bind(this);\n this.sendKeyPress = this.sendKeyPress.bind(this);\n this.handleScrollReference = this.handleScrollReference.bind(this);\n this.handleScrollEvent = this.handleScrollEvent.bind(this);\n this.handleDescChange = this.handleDescChange.bind(this);\n this.handleSubsUpdated = this.handleSubsUpdated.bind(this);\n this.handleNewMessage = this.handleNewMessage.bind(this);\n this.handleAllMessagesReceived = this.handleAllMessagesReceived.bind(this);\n this.handleInfoReceipt = this.handleInfoReceipt.bind(this);\n this.handleImagePostview = this.handleImagePostview.bind(this);\n this.handleClosePreview = this.handleClosePreview.bind(this);\n this.handleFormResponse = this.handleFormResponse.bind(this);\n this.handleContextClick = this.handleContextClick.bind(this);\n this.handleShowContextMenuMessage = this.handleShowContextMenuMessage.bind(this);\n this.handleNewChatAcceptance = this.handleNewChatAcceptance.bind(this);\n this.handleEnablePeer = this.handleEnablePeer.bind(this);\n this.handleAttachFile = this.handleAttachFile.bind(this);\n this.handleAttachImage = this.handleAttachImage.bind(this);\n this.handleCancelUpload = this.handleCancelUpload.bind(this);\n this.postReadNotification = this.postReadNotification.bind(this);\n this.clearNotificationQueue = this.clearNotificationQueue.bind(this);\n\n this.readNotificationQueue = [];\n this.readNotificationTimer = null;\n }\n\n componentDidMount() {\n // this.propsChange(this.props, this.state);\n if (this.messagesScroller) {\n this.messagesScroller.addEventListener('scroll', this.handleScrollEvent);\n }\n }\n\n componentWillUnmount() {\n if (this.messagesScroller) {\n this.messagesScroller.removeEventListener('scroll', this.handleScrollEvent);\n }\n\n // Flush all notifications.\n this.clearNotificationQueue();\n }\n\n // Scroll last message into view on component update e.g. on message received\n // or vertical shrinking.\n componentDidUpdate(prevProps, prevState) {\n if (this.messagesScroller) {\n if (prevState.topic != this.state.topic || prevState.messages.length != this.state.messages.length) {\n // New message\n this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight - this.state.scrollPosition;\n } else if (prevProps.viewportHeight > this.props.viewportHeight) {\n // Componet changed height.\n this.messagesScroller.scrollTop += prevProps.viewportHeight - this.props.viewportHeight;\n }\n }\n\n const topic = this.props.tinode ? this.props.tinode.getTopic(this.state.topic) : undefined;\n if (this.state.topic != prevState.topic) {\n if (prevState.topic && !Tinode.isNewGroupTopicName(prevState.topic)) {\n this.leave(prevState.topic);\n }\n\n if (topic) {\n topic.onData = this.handleNewMessage;\n topic.onAllMessagesReceived = this.handleAllMessagesReceived;\n topic.onInfo = this.handleInfoReceipt;\n topic.onMetaDesc = this.handleDescChange;\n topic.onSubsUpdated = this.handleSubsUpdated;\n topic.onPres = this.handleSubsUpdated;\n }\n }\n\n if (!this.props.applicationVisible) {\n // If application is not visible, flush all unsent 'read' notifications.\n this.clearNotificationQueue();\n } else {\n // Otherwise assume there are unread messages.\n this.postReadNotification(0);\n }\n\n if (topic && !topic.isSubscribed() && this.props.ready &&\n ((this.state.topic != prevState.topic) || !prevProps.ready)) {\n // Is this a new topic?\n const newTopic = (this.props.newTopicParams && this.props.newTopicParams._topicName == this.props.topic);\n\n // Don't request the tags. They are useless unless the user\n // is the owner and is editing the topic.\n let getQuery = topic.startMetaQuery().withLaterDesc().withLaterSub();\n if (this.state.isReader || newTopic) {\n // Reading is either permitted or we don't know because it's a new topic. Ask for messages.\n getQuery = getQuery.withLaterData(MESSAGES_PAGE);\n if (this.state.isReader) {\n getQuery = getQuery.withLaterDel();\n }\n // And show \"loading\" spinner.\n this.setState({ fetchingMessages: true });\n }\n const setQuery = newTopic ? this.props.newTopicParams : undefined;\n topic.subscribe(getQuery.build(), setQuery)\n .then((ctrl) => {\n if (ctrl.code == 303) {\n // Redirect to another topic requested.\n HashNavigation.navigateTo(HashNavigation.setUrlTopic('', ctrl.params.topic));\n return;\n }\n if (this.state.topic != ctrl.topic) {\n this.setState({topic: ctrl.topic});\n }\n this.props.onNewTopicCreated(this.props.topic, ctrl.topic);\n // If there are unsent messages, try sending them now.\n topic.queuedMessages((pub) => {\n if (!pub._sending && topic.isSubscribed()) {\n topic.publishMessage(pub);\n }\n });\n })\n .catch((err) => {\n console.log(\"Failed subscription to\", this.state.topic);\n this.props.onError(err.message, 'err');\n const blankState = MessagesView.getDerivedStateFromProps({}, {});\n blankState.title = this.props.intl.formatMessage(messages.not_found);\n this.setState(blankState);\n });\n }\n }\n\n static getDerivedStateFromProps(nextProps, prevState) {\n let nextState = {};\n if (!nextProps.topic) {\n // Default state: no topic.\n nextState = {\n messages: [],\n onlineSubs: [],\n topic: null,\n title: '',\n avatar: null,\n isVerified: false,\n isStaff: false,\n isDangerous: false,\n docPreview: null,\n imagePreview: null,\n imagePostview: null,\n typingIndicator: false,\n scrollPosition: 0,\n fetchingMessages: false,\n peerMessagingDisabled: false,\n channel: false\n };\n } else if (nextProps.topic != prevState.topic) {\n const topic = nextProps.tinode.getTopic(nextProps.topic);\n nextState = {\n topic: nextProps.topic,\n docPreview: null,\n imagePreview: null,\n imagePostview: null,\n typingIndicator: false,\n scrollPosition: 0,\n fetchingMessages: false\n };\n\n if (topic) {\n // Topic exists.\n const msgs = [];\n const subs = [];\n\n if (nextProps.connected) {\n topic.subscribers((sub) => {\n if (sub.online && sub.user != nextProps.myUserId) {\n subs.push(sub);\n }\n });\n }\n\n topic.messages(function(msg) {\n if (!msg.deleted) {\n msgs.push(msg);\n }\n });\n\n Object.assign(nextState, {\n messages: msgs,\n onlineSubs: subs\n });\n\n if (topic.public) {\n Object.assign(nextState, {\n title: topic.public.fn,\n avatar: makeImageDataUrl(topic.public.photo)\n });\n } else {\n Object.assign(nextState, {\n title: '',\n avatar: null\n });\n }\n\n const peer = topic.p2pPeerDesc();\n if (peer) {\n Object.assign(nextState, {\n peerMessagingDisabled: isPeerRestricted(peer.acs)\n });\n } else if (prevState.peerMessagingDisabled) {\n Object.assign(nextState, {\n peerMessagingDisabled: false\n });\n }\n Object.assign(nextState, {\n channel: topic.isChannelType()\n });\n } else {\n // Invalid topic.\n Object.assign(nextState, {\n messages: [],\n onlineSubs: [],\n title: '',\n avatar: null,\n peerMessagingDisabled: false,\n channel: false\n });\n }\n }\n\n if (nextProps.acs) {\n if (nextProps.acs.isWriter() != prevState.isWriter) {\n nextState.isWriter = !prevState.isWriter;\n }\n if (nextProps.acs.isReader() != prevState.isReader) {\n nextState.isReader = !prevState.isReader;\n }\n if (!nextProps.acs.isReader('given') != prevState.readingBlocked) {\n nextState.readingBlocked = !prevState.readingBlocked;\n }\n if (nextProps.acs.isSharer() != prevState.isSharer) {\n nextState.isSharer = !prevState.isSharer;\n }\n } else {\n if (prevState.isWriter) {\n nextState.isWriter = false;\n }\n if (prevState.isReader) {\n nextState.isReader = false;\n }\n if (!prevState.readingBlocked) {\n prevState.readingBlocked = true;\n }\n if (prevState.isSharer) {\n nextState.isSharer = false;\n }\n }\n\n if (isUnconfirmed(nextProps.acs) == !prevState.unconformed) {\n nextState.unconfirmed = !prevState.unconformed;\n }\n\n // Clear subscribers online when there is no connection.\n if (!nextProps.connected && prevState.onlineSubs && prevState.onlineSubs.length > 0) {\n nextState.onlineSubs = [];\n }\n\n return nextState;\n }\n\n leave(oldTopicName) {\n if (!oldTopicName || !this.props.tinode.isTopicCached(oldTopicName)) {\n return;\n }\n const oldTopic = this.props.tinode.getTopic(oldTopicName);\n if (oldTopic && oldTopic.isSubscribed()) {\n oldTopic.leave(false)\n .catch(() => { /* do nothing here */ })\n .finally(() => {\n // We don't care if the request succeeded or failed.\n // The topic is dead regardless.\n this.setState({fetchingMessages: false});\n oldTopic.onData = undefined;\n oldTopic.onAllMessagesReceived = undefined;\n oldTopic.onInfo = undefined;\n oldTopic.onMetaDesc = undefined;\n oldTopic.onSubsUpdated = undefined;\n oldTopic.onPres = undefined;\n });\n }\n }\n\n handleScrollReference(node) {\n if (node) {\n node.addEventListener('scroll', this.handleScrollEvent);\n this.messagesScroller = node;\n this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight - this.state.scrollPosition;\n }\n }\n\n // Get older messages\n handleScrollEvent(event) {\n this.setState({scrollPosition: event.target.scrollHeight - event.target.scrollTop});\n if (event.target.scrollTop <= 0) {\n this.setState((prevState, props) => {\n const newState = {};\n if (!prevState.fetchingMessages) {\n const topic = this.props.tinode.getTopic(this.state.topic);\n if (topic && topic.isSubscribed() && topic.msgHasMoreMessages()) {\n newState.fetchingMessages = true;\n topic.getMessagesPage(MESSAGES_PAGE).catch((err) => {\n this.setState({fetchingMessages: false});\n this.props.onError(err.message, 'err');\n });\n }\n }\n return newState;\n });\n }\n }\n\n handleDescChange(desc) {\n if (desc.public) {\n this.setState({\n title: desc.public.fn,\n avatar: makeImageDataUrl(desc.public.photo)\n });\n } else {\n this.setState({\n title: '',\n avatar: null\n });\n }\n\n if (desc.acs) {\n this.setState({\n isWriter: desc.acs.isWriter(),\n isReader: desc.acs.isReader(),\n readingBlocked: !desc.acs.isReader('given'),\n unconfirmed: isUnconfirmed(desc.acs),\n });\n }\n }\n\n postReadNotification(seq) {\n // Ignore notifications if the app is invisible.\n if (!this.props.applicationVisible) {\n return;\n }\n\n // Set up the timer if it's not running already.\n if (!this.readNotificationTimer) {\n this.readNotificationTimer = setInterval(() => {\n if (this.readNotificationQueue.length == 0) {\n // Shut down the timer if the queue is empty.\n clearInterval(this.readNotificationTimer);\n this.readNotificationTimer = null;\n return;\n }\n\n let seq = -1;\n while (this.readNotificationQueue.length > 0) {\n const n = this.readNotificationQueue[0];\n if (n.topicName != this.state.topic) {\n // Topic has changed. Drop the notification.\n this.readNotificationQueue.shift();\n continue;\n }\n\n const now = new Date();\n if (n.sendAt <= now) {\n // Remove expired notification from queue.\n this.readNotificationQueue.shift();\n seq = Math.max(seq, n.seq);\n } else {\n break;\n }\n }\n\n // Send only one notification for the whole batch of messages.\n if (seq >= 0) {\n const topic = this.props.tinode.getTopic(this.state.topic);\n if (topic) {\n topic.noteRead(seq);\n }\n }\n }, NOTIFICATION_EXEC_INTERVAL);\n }\n\n const now = new Date();\n this.readNotificationQueue.push({\n topicName: this.state.topic,\n seq: seq,\n sendAt: now.setMilliseconds(now.getMilliseconds() + READ_DELAY)\n });\n }\n\n // Clear notification queue and timer.\n clearNotificationQueue() {\n this.readNotificationQueue = [];\n if (this.readNotificationTimer) {\n clearInterval(this.readNotificationTimer);\n this.readNotificationTimer = null;\n }\n }\n\n handleSubsUpdated() {\n if (this.state.topic) {\n const subs = [];\n const topic = this.props.tinode.getTopic(this.state.topic);\n topic.subscribers((sub) => {\n if (sub.online && sub.user != this.props.myUserId) {\n subs.push(sub);\n }\n });\n const newState = {onlineSubs: subs};\n const peer = topic.p2pPeerDesc();\n if (peer) {\n Object.assign(newState, {\n peerMessagingDisabled: isPeerRestricted(peer.acs)\n });\n } else if (this.state.peerMessagingDisabled) {\n Object.assign(newState, {\n peerMessagingDisabled: false\n });\n }\n this.setState(newState);\n }\n }\n\n handleNewMessage(msg) {\n // Regenerate messages list\n const topic = this.props.tinode.getTopic(this.state.topic);\n const newState = {messages: []};\n topic.messages((m) => {\n if (!m.deleted) {\n newState.messages.push(m);\n }\n });\n\n // msg could be null if one or more messages were deleted.\n if (msg && !msg.deleted) {\n // If the message is added to the end of the message list,\n // scroll to the bottom.\n if (topic.isNewMessage(msg.seq)) {\n newState.scrollPosition = 0;\n }\n\n // Aknowledge messages except own messages. They are\n // automatically assumed to be read and recived.\n const status = topic.msgStatus(msg, true);\n if (status >= Tinode.MESSAGE_STATUS_SENT && msg.from != this.props.myUserId) {\n this.postReadNotification(msg.seq);\n }\n this.props.onData(msg);\n }\n this.setState(newState);\n }\n\n handleAllMessagesReceived(count) {\n this.setState({fetchingMessages: false});\n if (count > 0) {\n // 0 means \"latest\".\n this.postReadNotification(0);\n }\n }\n\n handleInfoReceipt(info) {\n switch (info.what) {\n case 'kp': {\n clearTimeout(this.keyPressTimer);\n var instance = this;\n this.keyPressTimer = setTimeout(function() {\n instance.setState({typingIndicator: false});\n }, KEYPRESS_DELAY + 1000);\n if (!this.state.typingIndicator) {\n this.setState({typingIndicator: true});\n }\n break;\n }\n case 'read':\n case 'recv':\n // Redraw due to changed recv/read status.\n this.forceUpdate();\n break;\n default:\n console.log(\"Other change in topic: \", info.what);\n }\n }\n\n handleImagePostview(content) {\n this.setState({ imagePostview: content });\n }\n\n handleClosePreview() {\n if (this.state.imagePreview && this.state.imagePreview.url) {\n URL.revokeObjectURL(this.state.imagePreview.url);\n }\n this.setState({ imagePostview: null, imagePreview: null, docPreview: null });\n }\n\n handleFormResponse(action, text, data) {\n if (action == 'pub') {\n this.props.sendMessage(Drafty.attachJSON(Drafty.parse(text), data));\n } else if (action == 'url') {\n const url = new URL(data.ref);\n const params = url.searchParams;\n for (let key in data.resp) {\n if (data.resp.hasOwnProperty(key)) {\n params.set(key, data.resp[key]);\n }\n }\n ['name', 'seq'].map((key) => {\n if (data[key]) {\n params.set(key, data[key]);\n }\n });\n params.set('uid', this.props.myUserId);\n params.set('topic', this.state.topic);\n url.search = params;\n window.open(url, '_blank');\n } else {\n console.log(\"Unknown action in form\", action);\n }\n }\n\n handleContextClick(e) {\n e.preventDefault();\n e.stopPropagation();\n this.props.showContextMenu({ topicName: this.state.topic, y: e.pageY, x: e.pageX });\n }\n\n handleShowContextMenuMessage(params, messageSpecificMenuItems) {\n params.topicName = this.state.topic;\n const menuItems = messageSpecificMenuItems || [];\n const topic = this.props.tinode.getTopic(params.topicName);\n if (topic) {\n if (!topic.isChannelType()) {\n menuItems.push('message_delete');\n }\n const acs = topic.getAccessMode();\n if (acs && acs.isDeleter()) {\n menuItems.push('message_delete_hard');\n }\n }\n this.props.showContextMenu(params, menuItems);\n }\n\n handleNewChatAcceptance(action) {\n this.props.onNewChat(this.state.topic, action);\n }\n\n handleEnablePeer(e) {\n e.preventDefault();\n this.props.onChangePermissions(this.state.topic, DEFAULT_P2P_ACCESS_MODE, this.state.topic);\n }\n\n sendKeyPress() {\n const topic = this.props.tinode.getTopic(this.state.topic);\n if (topic.isSubscribed()) {\n topic.noteKeyPress();\n }\n }\n\n // Send attachment as Drafty message:\n // - if file is too large, upload it and send a s link.\n // - if file is small enough, just send it in-band.\n sendFileAttachment(file) {\n // Server-provided limit reduced for base64 encoding and overhead.\n const maxInbandAttachmentSize = (this.props.tinode.getServerLimit('maxMessageSize',\n MAX_INBAND_ATTACHMENT_SIZE) * 0.75 - 1024) | 0;\n\n if (file.size > maxInbandAttachmentSize) {\n // Too large to send inband - uploading out of band and sending as a link.\n const uploader = this.props.tinode.getLargeFileHelper();\n if (!uploader) {\n this.props.onError(this.props.intl.formatMessage(messages.cannot_initiate_upload));\n return;\n }\n const uploadCompletionPromise = uploader.upload(file);\n const msg = Drafty.attachFile(null, {\n mime: file.type,\n filename: file.name,\n size: file.size,\n urlPromise: uploadCompletionPromise\n });\n // Pass data and the uploader to the TinodeWeb.\n this.props.sendMessage(msg, uploadCompletionPromise, uploader);\n } else {\n // Small enough to send inband.\n fileToBase64(file,\n (mime, bits, fname) => {\n this.props.sendMessage(Drafty.attachFile(null, {mime: mime, data: bits, filename: fname}));\n },\n this.props.onError\n );\n }\n }\n\n // handleAttachFile method is called when [Attach file] button is clicked.\n handleAttachFile(file) {\n const maxExternAttachmentSize = this.props.tinode.getServerLimit('maxFileUploadSize', MAX_EXTERN_ATTACHMENT_SIZE);\n\n if (file.size > maxExternAttachmentSize) {\n // Too large.\n this.props.onError(this.props.intl.formatMessage(messages.file_attachment_too_large,\n {size: bytesToHumanSize(file.size), limit: bytesToHumanSize(maxExternAttachmentSize)}), 'err');\n } else {\n this.setState({ docPreview: {\n file: file,\n filename: file.name,\n size: file.size,\n type: file.type\n }});\n }\n }\n\n // sendImageAttachment sends the image bits inband as Drafty message.\n sendImageAttachment(caption, blob) {\n const mime = this.state.imagePreview.type;\n const width = this.state.imagePreview.width;\n const height = this.state.imagePreview.height;\n const fname = this.state.imagePreview.filename;\n\n // Server-provided limit reduced for base64 encoding and overhead.\n const maxInbandAttachmentSize = (this.props.tinode.getServerLimit('maxMessageSize',\n MAX_INBAND_ATTACHMENT_SIZE) * 0.75 - 1024) | 0;\n\n if (blob.size > maxInbandAttachmentSize) {\n // Too large to send inband - uploading out of band and sending as a link.\n const uploader = this.props.tinode.getLargeFileHelper();\n if (!uploader) {\n this.props.onError(this.props.intl.formatMessage(messages.cannot_initiate_upload));\n return;\n }\n const uploadCompletionPromise = uploader.upload(blob);\n\n // Make small preview to show while uploading.\n imageScaled(blob, IMAGE_PREVIEW_DIM, IMAGE_PREVIEW_DIM, -1, false,\n (tinyMine, tinyBlob) => {\n // Convert tiny image into base64 for serialization and previewing.\n blobToBase64(tinyBlob, (blobMime, tinyBits64) => {\n let msg = Drafty.insertImage(null, 0, {\n mime: mime,\n _tempPreview: tinyBits64, // This preview will not be serialized.\n width: width,\n height: height,\n filename: fname,\n size: blob.size,\n urlPromise: uploadCompletionPromise\n });\n if (caption) {\n msg = Drafty.appendLineBreak(msg);\n msg = Drafty.append(msg, Drafty.init(caption));\n }\n // Pass data and the uploader to the TinodeWeb.\n this.props.sendMessage(msg, uploadCompletionPromise, uploader);\n }\n )},\n // Failure\n (err) => {\n this.props.onError(err, 'err');\n }\n );\n return;\n }\n\n // Upload the image if it's too big to be send inband.\n blobToBase64(blob, (blobMime, bits64) => {\n let msg = Drafty.insertImage(null, 0, {\n mime: blobMime,\n preview: bits64, // Serializable preview\n width: width,\n height: height,\n filename: fname,\n size: blob.size\n });\n if (caption) {\n msg = Drafty.appendLineBreak(msg);\n msg = Drafty.append(msg, Drafty.init(caption));\n }\n this.props.sendMessage(msg);\n });\n }\n\n // handleAttachImage method is called when [Attach image] button is clicked.\n handleAttachImage(file) {\n const maxExternAttachmentSize = this.props.tinode.getServerLimit('maxFileUploadSize', MAX_EXTERN_ATTACHMENT_SIZE);\n\n // Get image dimensions and size, optionally scale it down.\n imageScaled(file, MAX_IMAGE_DIM, MAX_IMAGE_DIM, maxExternAttachmentSize, false,\n // Success\n (mime, blob, width, height, fname) => {\n this.setState({imagePreview: {\n url: URL.createObjectURL(blob),\n blob: blob,\n filename: fname,\n width: width,\n height: height,\n size: blob.size,\n type: mime\n }});\n },\n // Failure\n (err) => {\n this.props.onError(err, 'err');\n }\n );\n }\n\n handleCancelUpload(seq, uploader) {\n const found = this.state.messages.find(msg => msg.seq == seq);\n if (found) {\n found._cancelled = true;\n }\n uploader.cancel();\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n\n let component;\n if (this.props.hideSelf) {\n component = null;\n } else if (!this.state.topic) {\n component = (\n \n );\n } else {\n let component2;\n if (this.state.imagePreview) {\n // Preview image before sending.\n component2 = (\n \n );\n } else if (this.state.imagePostview) {\n // Expand received image.\n component2 = (\n \n );\n } else if (this.state.docPreview) {\n // Preview attachment before sending.\n component2 = (\n \n );\n } else {\n const topic = this.props.tinode.getTopic(this.state.topic);\n const isChannel = topic.isChannelType();\n const groupTopic = topic.isGroupType() && !isChannel;\n const icon_badges = [];\n if (topic.trusted) {\n if (topic.trusted.verified) {\n icon_badges.push({icon: 'verified', color: 'badge-inv'});\n }\n if (topic.trusted.staff) {\n icon_badges.push({icon: 'staff', color: 'badge-inv'});\n }\n if (topic.trusted.danger) {\n icon_badges.push({icon: 'dangerous', color: 'badge-inv'});\n }\n }\n let messageNodes = [];\n let previousFrom = null;\n let chatBoxClass = null;\n for (let i=0; i\n );\n }\n\n let lastSeen = null;\n if (isChannel) {\n lastSeen = formatMessage(messages.channel);\n } else {\n const cont = this.props.tinode.getMeTopic().getContact(this.state.topic);\n if (cont && Tinode.isP2PTopicName(cont.topic)) {\n if (cont.online) {\n lastSeen = formatMessage(messages.online_now);\n } else if (cont.seen) {\n lastSeen = formatMessage(messages.last_seen) + \": \" +\n shortDateFormat(cont.seen.when, this.props.intl.locale);\n // TODO: also handle user agent in c.seen.ua\n }\n }\n }\n const avatar = this.state.avatar || true;\n const online = this.props.online ? 'online' + (this.state.typingIndicator ? ' typing' : '') : 'offline';\n\n component2 = (\n <>\n \n {this.props.displayMobile ?\n \n : null}\n \n \n
\n {!this.state.isReader ?\n
\n {this.state.readingBlocked ?\n
\n \n
\n : null }\n
\n : null }\n
\n {this.state.peerMessagingDisabled && !this.state.unconfirmed ?\n : null}\n {this.state.unconfirmed ?\n \n :\n }\n >\n );\n }\n\n component = {component2}
\n }\n return component;\n }\n};\n\nexport default injectIntl(MessagesView);\n","// The <- button to be displayed in title bars.\nimport React from 'react';\n\nexport default class ButtonBack extends React.PureComponent {\n render() {\n return (\n {e.preventDefault(); this.props.onBack();}}>\n arrow_back\n \n );\n }\n}\n","import React from 'react';\n\nexport default class MenuContacts extends React.PureComponent {\n render() {\n return (\n \n );\n }\n};\n","import React from 'react';\n\nexport default class MenuStart extends React.PureComponent {\n render() {\n return (\n \n );\n }\n};\n","import React from 'react';\n\n\nimport LetterTile from './letter-tile.jsx';\nimport ButtonBack from './button-back.jsx';\nimport ContactBadges from './contact-badges.jsx';\nimport MenuContacts from './menu-contacts.jsx';\nimport MenuStart from './menu-start.jsx';\n\nexport default class SideNavbar extends React.PureComponent {\n render() {\n const icon_badges = [];\n if (this.props.trustedBadges) {\n this.props.trustedBadges.map((b) => {\n icon_badges.push({icon: b, color: 'badge-inv'});\n });\n }\n return (\n \n {this.props.onCancel ?
: null}\n {this.props.avatar ?\n
\n \n
\n :\n null}\n
{this.props.title}
\n {this.props.state === 'login' ?\n
:\n this.props.state === 'contacts' ?\n
:\n null}\n
\n );\n }\n};\n","// Image zoom and crop widget.\n\nimport React from 'react';\n\nconst DEFAULT_MAX_ZOOM = 2.5;\n\nexport default class Cropper extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n // Coordinates of the top left corner of the image in container coordinates.\n panX: 0,\n panY: 0,\n // Origin of the zoom in image coordinates.\n originX: 0,\n originY: 0,\n // Zoom scale.\n zoom: 1,\n // Zoom min and max.\n minZoom: 0,\n maxZoom: DEFAULT_MAX_ZOOM,\n };\n\n // References to DOM elements for hooking up events and getting screen dimensions.\n // The transparent element which captures mouse moves (moves with the image).\n this.overlay = React.createRef();\n // The image cutout window or view port (static).\n this.cutout = React.createRef();\n // Scaled and translated image preview.\n this.preview = React.createRef();\n // The main container (static).\n this.boundingBox = React.createRef();\n\n // The original image width and height before any scaling.\n this.imageWidth = 0;\n this.imageHeight = 0;\n\n // Mouse position when dragging.\n this.mouseX = 0\n this.mouseY = 0;\n // Length of the previous mouse move when dragging.\n this.prevDistance = 0;\n\n // Bounding rectangles of static elements.\n this.cutoutRect = {};\n this.bBoxRect = {};\n // Center of the bounding box.\n this.originX = 0;\n this.originY = 0;\n\n this.initScaling = this.initScaling.bind(this);\n this.handleZoom = this.handleZoom.bind(this);\n this.mouseDown = this.mouseDown.bind(this);\n this.mouseUp = this.mouseUp.bind(this);\n this.mouseMove = this.mouseMove.bind(this);\n this.mouseTouch = this.mouseTouch.bind(this);\n this.positionAll = this.positionAll.bind(this);\n this.translate = this.translate.bind(this);\n }\n\n componentDidMount() {\n this.overlay.current.addEventListener('mousedown', this.mouseDown, { passive: true });\n this.overlay.current.addEventListener('touchstart', this.mouseDown, { passive: true });\n\n // The rectangle position is in viewport coordinates.\n this.bBoxRect = this.boundingBox.current.getBoundingClientRect();\n // Center of the bounding box in container coordinates.\n this.originX = this.bBoxRect.width / 2;\n this.originY = this.bBoxRect.height / 2;\n\n // The rectangle position is in viewport coordinates.\n this.cutoutRect = this.cutout.current.getBoundingClientRect();\n }\n\n componentWillUnmount() {\n this.overlay.current.removeEventListener('mousedown', this.mouseDown);\n this.overlay.current.removeEventListener('touchstart', this.mouseDown);\n }\n\n // Position all elements.\n positionAll(panX, panY, zoom) {\n // Zoom origin in image preview coordinates.\n this.setState({\n panX: panX,\n panY: panY,\n zoom: zoom,\n originX: this.originX - panX,\n originY: this.originY - panY,\n });\n }\n\n // Check if new location is within the limits.\n static checkBound(currPan, img, cutout, delta) {\n let nextDiff = Math.min(0, cutout[0] - img[0] - delta, img[1] - cutout[1] + delta);\n if (nextDiff == 0) {\n // Cutout is completely within the image.\n currPan += delta;\n } else if (Math.min(0, cutout[0] - img[0], img[1] - cutout[1]) < nextDiff) {\n // Cutout is outside of the range but closer to the bound.\n currPan += delta;\n }\n return currPan;\n }\n\n initScaling() {\n // Initialize components.\n const imgRect = this.preview.current.getBoundingClientRect();\n\n this.imageWidth = imgRect.width;\n this.imageHeight = imgRect.height;\n\n // Set zoom slider's min and max values. Make sure the scaled image cannot be smaller than the cutout.\n const minZoom = Math.max(this.cutoutRect.width / imgRect.width, this.cutoutRect.height / imgRect.height);\n this.setState({\n minZoom: minZoom,\n maxZoom: Math.max(DEFAULT_MAX_ZOOM, minZoom + 1)\n });\n\n // Initial zoom level fills the bounding box at the smallest image dimension and overflows the largest, i.e. \"fill\" not \"fit\".\n let zoom = Math.max((this.bBoxRect.width / imgRect.width), (this.bBoxRect.height / imgRect.height));\n // Converting from viewport coordinates to container, then panning.\n let panX = (this.cutoutRect.left - this.bBoxRect.left) - ((imgRect.width - this.cutoutRect.width) / 2);\n let panY = (this.cutoutRect.top - this.bBoxRect.top) - ((imgRect.height - this.cutoutRect.height) / 2);\n\n this.positionAll(panX, panY, zoom);\n }\n\n handleZoom(e) {\n let zoom = e.target.value;\n\n let panX = this.state.panX;\n let panY = this.state.panY;\n\n // Ensure that the image at the new zoom is not outside of the cutout boundaries.\n // Calculate image coordinates at the new zoom.\n const imgLeft = this.originX - (this.originX - panX) * zoom;\n const imgRight = imgLeft + this.imageWidth * zoom;\n // Cutout in the same coordinates as the image.\n const coLeft = this.cutoutRect.left - this.bBoxRect.left;\n const coRight = coLeft + this.cutoutRect.width;\n if (coLeft < imgLeft) {\n panX -= imgLeft - coLeft;\n } else if (coRight > imgRight) {\n panX += coRight - imgRight;\n }\n const imgTop = this.originY - (this.originY - panY) * zoom;\n const imgBottom = imgTop + this.imageHeight * zoom;\n const coTop = this.cutoutRect.top - this.bBoxRect.top;\n const coBottom = coTop + this.cutoutRect.height;\n if (coTop < imgTop) {\n panY -= imgTop - coTop;\n } else if (coBottom > imgBottom) {\n panY += coBottom - imgBottom;\n }\n\n this.positionAll(panX, panY, zoom);\n }\n\n mouseDown(e) {\n if (e.touches) {\n this.mouseX = e.touches[0].pageX;\n this.mouseY = e.touches[0].pageY;\n } else {\n this.mouseX = e.pageX;\n this.mouseY = e.pageY;\n }\n\n window.addEventListener('mousemove', this.mouseMove, { passive: false });\n window.addEventListener('touchmove', this.mouseTouch, { passive: false });\n window.addEventListener('mouseup', this.mouseUp, { passive: true });\n window.addEventListener('touchend', this.mouseUp, { passive: true });\n // Disable text selection in the entire document.\n document.body.style['userSelect'] = 'none';\n }\n\n // Perform image panning.\n translate(pageX, pageY) {\n const dX = pageX - this.mouseX;\n const dY = pageY - this.mouseY;\n\n this.mouseX = pageX;\n this.mouseY = pageY;\n\n // Make sure the image is within the cutout window.\n const imgRect = this.preview.current.getBoundingClientRect();\n\n // Check if the new position is within the boundaries, and if not if it's closer to them.\n let panX = Cropper.checkBound(this.state.panX, [imgRect.left, imgRect.right],\n [this.cutoutRect.left, this.cutoutRect.right], dX);\n let panY = Cropper.checkBound(this.state.panY, [imgRect.top, imgRect.bottom],\n [this.cutoutRect.top, this.cutoutRect.bottom], dY);\n\n this.positionAll(panX, panY, this.state.zoom);\n }\n\n // Image panning.\n mouseMove(e) {\n e.preventDefault();\n this.translate(e.pageX, e.pageY);\n }\n\n mouseTouch(e) {\n e.preventDefault();\n\n if (e.touches.length == 1) {\n this.translate(e.touches[0].pageX, e.touches[0].pageY);\n return;\n }\n\n // Image zooming by pinching.\n const [touch0, touch1] = e.touches;\n const distance = Math.sqrt((touch0.pageX - touch1.pageX) * (touch0.pageX - touch1.pageX) +\n (touch0.pageY - touch1.pageY) * (touch0.pageY - touch1.pageY));\n\n if (!this.prevDistance) {\n this.prevDistance = distance / this.state.zoom;\n }\n\n let scale = (distance / this.prevDistance).toFixed(3);\n this.setState({zoom: Math.max(this.minZoom, Math.min(this.maxZoom, scale))});\n }\n\n mouseUp(e) {\n window.removeEventListener('mousemove', this.mouseMove);\n window.removeEventListener('touchmove', this.mouseTouch);\n window.removeEventListener('mouseup', this.mouseUp);\n window.removeEventListener('touchend', this.mouseUp);\n // Re-enable text selection.\n document.body.style['userSelect'] = '';\n\n this.positionAll(this.state.panX, this.state.panY, this.state.zoom);\n }\n\n render() {\n // transform3d: position and scale.\n const t3d = `translate3d(${this.state.panX}px, ${this.state.panY}px, 0) scale(${this.state.zoom})`;\n // transformOrigin: zoom origin.\n const orig = `${this.state.originX}px ${this.state.originY}px`;\n\n // Overlay position and size are exactly equal to the position and size of the transformed image\n // except it uses different coordinates.\n const overlay = {\n top: `${this.originY - this.state.originY * this.state.zoom}px`,\n left: `${this.originX - this.state.originX * this.state.zoom}px`,\n width: `${this.imageWidth * this.state.zoom}px`,\n height: `${this.imageHeight * this.state.zoom}px`\n };\n return (\n \n
\n

\n
\n
\n
\n
\n \n
\n
\n );\n }\n}\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport Cropper from '../widgets/cropper.jsx';\n\nexport default class AvatarCropView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleSubmit = this.handleSubmit.bind(this);\n }\n\n handleSubmit() {\n if (this.props.onSubmit) {\n this.props.onSubmit();\n }\n }\n\n render() {\n return (\n \n
\n \n
\n
\n \n
\n
\n );\n }\n};\n","/* ContactsView holds all contacts-related stuff */\nimport React from 'react';\nimport { FormattedMessage, defineMessages } from 'react-intl';\n\nimport ContactList from '../widgets/contact-list.jsx';\n\nimport { updateFavicon } from '../lib/utils.js';\n\nconst messages = defineMessages({\n archived_contacts_title: {\n id: \"archived_contacts\",\n defaultMessage: \"Archived contacts ({count})\",\n description: \"Label for archived chats\"\n }\n});\n\nexport default class ContactsView extends React.Component {\n constructor(props) {\n super(props);\n\n this.handleAction = this.handleAction.bind(this);\n\n this.state = ContactsView.deriveStateFromProps(props);\n }\n\n static deriveStateFromProps(props) {\n const contacts = [];\n let unreadThreads = 0;\n let archivedCount = 0;\n props.chatList.map((c) => {\n const blocked = c.acs && !c.acs.isJoiner();\n // Show only blocked contacts only when props.blocked == true.\n if (blocked && props.blocked) {\n contacts.push(c);\n }\n if (blocked || props.blocked) {\n return;\n }\n\n if (c.private && c.private.arch) {\n if (props.archive) {\n contacts.push(c);\n } else {\n archivedCount ++;\n }\n } else if (!props.archive) {\n contacts.push(c);\n unreadThreads += c.unread > 0 ? 1 : 0;\n }\n });\n\n contacts.sort((a, b) => {\n return (b.touched || 0) - (a.touched || 0);\n });\n\n if (archivedCount > 0) {\n contacts.push({\n action: 'archive',\n title: messages.archived_contacts_title,\n values: {count: archivedCount}\n });\n }\n\n return {\n contactList: contacts,\n unreadThreads: unreadThreads\n };\n }\n\n componentDidUpdate(prevProps, prevState) {\n if (prevProps.chatList != this.props.chatList ||\n prevProps.archive != this.props.archive ||\n prevProps.blocked != this.props.blocked) {\n const newState = ContactsView.deriveStateFromProps(this.props);\n this.setState(newState);\n if (newState.unreadThreads != prevState.unreadThreads) {\n updateFavicon(newState.unreadThreads);\n }\n }\n }\n\n handleAction(action_ignored) {\n this.props.onShowArchive();\n }\n\n render() {\n return (\n ¯∖_(ツ)_/¯\"\n description=\"HTML message shown in ContactList when no contacts are found\">{\n (no_contacts) => \n }\n );\n }\n};\n","// Helper functions for storing values in localStorage.\n// By default localStorage can store only strings, not objects or other types.\n\nexport default class LocalStorageUtil {\n // Replace old object with the new one.\n static setObject(key, value) {\n localStorage.setItem(key, JSON.stringify(value));\n }\n\n // Get stored object.\n static getObject(key) {\n const value = localStorage.getItem(key);\n return value && JSON.parse(value);\n }\n\n // Partially or wholly update stored object.\n static updateObject(key, value) {\n const oldVal = this.getObject(key);\n this.setObject(key, Object.assign(oldVal || {}, value));\n }\n\n // Just a wrapper.\n static removeItem(key) {\n localStorage.removeItem(key);\n }\n}\n","// Account registration.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport AvatarUpload from '../widgets/avatar-upload.jsx';\nimport CheckBox from '../widgets/checkbox.jsx';\nimport VisiblePassword from '../widgets/visible-password.jsx';\n\nimport LocalStorageUtil from '../lib/local-storage.js';\nimport { theCard } from '../lib/utils.js';\n\nimport { MAX_TITLE_LENGTH } from '../config.js';\n\nexport default class CreateAccountView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n login: '',\n password: '',\n email: '',\n fn: '', // full/formatted name\n imageDataUrl: null,\n errorCleared: false,\n saveToken: LocalStorageUtil.getObject('keep-logged-in')\n };\n\n this.handleLoginChange = this.handleLoginChange.bind(this);\n this.handlePasswordChange = this.handlePasswordChange.bind(this);\n this.handleEmailChange = this.handleEmailChange.bind(this);\n this.handleFnChange = this.handleFnChange.bind(this);\n this.handleImageChanged = this.handleImageChanged.bind(this);\n this.handleToggleSaveToken = this.handleToggleSaveToken.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n }\n\n handleLoginChange(e) {\n this.setState({login: e.target.value});\n }\n\n handlePasswordChange(password) {\n this.setState({password: password});\n }\n\n handleEmailChange(e) {\n this.setState({email: e.target.value})\n }\n\n handleFnChange(e) {\n this.setState({fn: e.target.value});\n }\n\n handleImageChanged(img) {\n this.setState({imageDataUrl: img});\n }\n\n handleToggleSaveToken() {\n LocalStorageUtil.setObject('keep-logged-in', !this.state.saveToken);\n this.setState({saveToken: !this.state.saveToken});\n }\n\n handleSubmit(e) {\n e.preventDefault();\n this.setState({errorCleared: false});\n this.props.onCreateAccount(\n this.state.login.trim(),\n this.state.password.trim(),\n theCard(this.state.fn.trim().substring(0, MAX_TITLE_LENGTH), this.state.imageDataUrl),\n {'meth': 'email', 'val': this.state.email});\n }\n\n render() {\n let submitClasses = 'primary';\n if (this.props.disabled) {\n submitClasses += ' disabled';\n }\n\n return (\n \n );\n }\n};\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport AvatarUpload from '../widgets/avatar-upload.jsx';\nimport BadgeList from '../widgets/badge-list.jsx';\n\nimport { makeImageDataUrl } from '../lib/blob-helpers.js';\n\nexport default class EditAccountView extends React.Component {\n constructor(props) {\n super(props);\n\n const me = this.props.tinode.getMeTopic();\n this.state = {\n fullName: me.public ? me.public.fn : undefined,\n avatar: makeImageDataUrl(me.public ? me.public.photo : null)\n };\n }\n\n render() {\n return (\n \n
\n
\n
\n
{this.state.fullName}
\n
\n \n {this.props.myUserId}\n
\n
\n
\n
\n
\n
\n
\n
\n
\n );\n }\n};\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport AvatarUpload from '../widgets/avatar-upload.jsx';\nimport InPlaceEdit from '../widgets/in-place-edit.jsx';\nimport TagManager from '../widgets/tag-manager.jsx';\n\nimport { MAX_TITLE_LENGTH } from '../config.js';\nimport { makeImageDataUrl } from '../lib/blob-helpers.js';\nimport { arrayEqual, asEmail, asPhone, theCard } from '../lib/utils.js';\n\nexport default class AccGeneralView extends React.Component {\n constructor(props) {\n super(props);\n\n const me = this.props.tinode.getMeTopic();\n this.state = {\n fullName: me.public ? me.public.fn : undefined,\n avatar: makeImageDataUrl(me.public ? me.public.photo : null),\n tags: me.tags(),\n credentials: me.getCredentials() || [],\n addCredActive: false,\n addCredInvalid: false,\n newCred: '',\n previousOnTags: me.onTagsUpdated\n };\n\n this.tnNewTags = this.tnNewTags.bind(this);\n this.tnCredsUpdated = this.tnCredsUpdated.bind(this);\n this.handleFullNameUpdate = this.handleFullNameUpdate.bind(this);\n this.handleImageChanged = this.handleImageChanged.bind(this);\n this.handleCredChange = this.handleCredChange.bind(this);\n this.handleCredKeyDown = this.handleCredKeyDown.bind(this);\n this.handleCredEntered = this.handleCredEntered.bind(this);\n this.handleTagsUpdated = this.handleTagsUpdated.bind(this);\n }\n\n componentDidMount() {\n const me = this.props.tinode.getMeTopic();\n me.onCredsUpdated = this.tnCredsUpdated;\n me.onTagsUpdated = this.tnNewTags;\n }\n\n componentWillUnmount() {\n const me = this.props.tinode.getMeTopic();\n me.onTagsUpdated = this.state.previousOnTags;\n me.onCredsUpdated = undefined;\n }\n\n tnNewTags(tags) {\n this.setState({tags: tags});\n }\n\n tnCredsUpdated(creds) {\n this.setState({credentials: creds || []});\n }\n\n handleFullNameUpdate(fn) {\n fn = fn.trim().substring(0, MAX_TITLE_LENGTH);\n if (fn) {\n this.setState({fullName: fn});\n this.props.onUpdateAccount(undefined, theCard(fn, null));\n }\n }\n\n handleImageChanged(img) {\n this.setState({avatar: img});\n this.props.onBasicNavigate('crop');\n // this.props.onUpdateAccount(undefined, theCard(null, img || Tinode.DEL_CHAR));\n }\n\n handleCredChange(e) {\n this.setState({newCred: e.target.value, addCredInvalid: false});\n }\n\n handleCredKeyDown(e) {\n if (e.keyCode === 27) {\n // Escape pressed\n this.setState({newCred: '', addCredActive: false});\n } else if (e.keyCode === 13) {\n // Enter pressed\n this.handleCredEntered(e);\n }\n }\n\n handleCredEntered(e) {\n let value = this.state.newCred.trim();\n if (!value) {\n this.setState({addCredActive: false, addCredInvalid: false});\n return;\n }\n\n let val = asPhone(value);\n let method;\n if (val) {\n method = 'tel';\n } else {\n val = asEmail(value);\n if (val) {\n method = 'email';\n }\n }\n if (method) {\n this.props.onCredAdd(method, val);\n this.setState({addCredActive: false, newCred: ''});\n } else {\n this.setState({addCredInvalid: true});\n }\n }\n\n handleTagsUpdated(tags) {\n // Check if tags have actually changed.\n if (arrayEqual(this.state.tags.slice(0), tags.slice(0))) {\n return;\n }\n this.props.onUpdateTags(tags);\n }\n\n render() {\n const credentials = [];\n this.state.credentials.map((cred) => {\n credentials.push();\n });\n\n return (\n \n
\n
\n
\n
{\n (full_name_placeholder) => \n }
\n
\n
\n
\n
\n
{\n (title_tag_manager) => \n }\n
\n
\n
\n
\n {credentials}\n {this.state.addCredActive ?\n
\n : null}\n
\n
\n
\n
\n );\n }\n};\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport CheckBox from '../widgets/checkbox.jsx';\n\nexport default class EditAccountView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleCheckboxClick = this.handleCheckboxClick.bind(this);\n }\n\n handleCheckboxClick(what, checked) {\n if (what == 'sound') {\n this.props.onToggleMessageSounds(checked);\n } else if (what == 'alert') {\n this.props.onTogglePushNotifications(checked);\n } else if (what == 'incognito') {\n this.props.onToggleIncognitoMode(checked);\n }\n }\n\n render() {\n return (\n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n );\n }\n};\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport InPlaceEdit from '../widgets/in-place-edit.jsx';\nimport PermissionsEditor from '../widgets/permissions-editor.jsx';\n\nconst messages = defineMessages({\n delete_account: {\n id: 'delete_account',\n defaultMessage: 'Delete account',\n description: 'Title for delete account warning'\n },\n delete_account_warning: {\n id: 'delete_account_arning',\n defaultMessage: 'Are you sure you want to delete your account? It cannot be undone.',\n description: 'Warning message when deleting an account'\n }\n});\n\nclass AccSecurityView extends React.Component {\n constructor(props) {\n super(props);\n\n const me = this.props.tinode.getMeTopic();\n let blockedCount = 0;\n me.contacts((c) => {\n if (c.acs && !c.acs.isJoiner()) {\n blockedCount ++;\n }\n });\n const defacs = me.getDefaultAccess();\n this.state = {\n auth: defacs ? defacs.auth : null,\n anon: defacs ? defacs.anon : null,\n showPermissionEditorFor: undefined,\n blockedCount: blockedCount,\n };\n\n this.handlePasswordUpdate = this.handlePasswordUpdate.bind(this);\n this.handleLaunchPermissionsEditor = this.handleLaunchPermissionsEditor.bind(this);\n this.handleHidePermissionsEditor = this.handleHidePermissionsEditor.bind(this);\n this.handlePermissionsChanged = this.handlePermissionsChanged.bind(this);\n this.handleDeleteAccount = this.handleDeleteAccount.bind(this);\n }\n\n handlePasswordUpdate(pwd) {\n this.setState({password: pwd});\n this.props.onUpdateAccount(pwd);\n }\n\n handleLaunchPermissionsEditor(which) {\n this.setState({\n showPermissionEditorFor: which,\n editedPermissions: this.state[which]\n });\n }\n\n handleHidePermissionsEditor() {\n this.setState({showPermissionEditorFor: undefined});\n }\n\n handlePermissionsChanged(perm) {\n let defacs = {};\n defacs[this.state.showPermissionEditorFor] = perm;\n this.props.onUpdateAccount(undefined, undefined, defacs);\n\n let newState = {showPermissionEditorFor: undefined};\n newState[this.state.showPermissionEditorFor] = perm;\n this.setState(newState);\n }\n\n handleDeleteAccount(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(messages.delete_account), // title\n formatMessage(messages.delete_account_warning), // content\n (() => { this.props.onDeleteAccount(); }), // onConfirm\n null, // use default text \"OK\"\n true, // Show Reject button\n null // use default text \"Cancel\"\n );\n }\n\n render() {\n return (\n {this.state.showPermissionEditorFor ?\n \n :\n \n
\n
\n
\n {\n (password_unchanged) => \n }\n
\n
\n
\n
\n
\n
\n
\n \n
\n
\n
Auth: {this.state.auth}
\n
Anon: {this.state.anon}
\n
\n
\n {this.state.blockedCount > 0 ?\n <>\n
\n
\n >\n : null }\n
\n }\n );\n }\n};\n\nexport default injectIntl(AccSecurityView);\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport { APP_NAME, LINK_CONTACT_US, LINK_PRIVACY_POLICY, LINK_TERMS_OF_SERVICE } from '../config.js';\n\nexport default class AccSupportView extends React.PureComponent {\n render() {\n return (\n \n
\n
\n
\n
\n \n {APP_NAME}\n
\n
\n \n {Tinode.getLibrary()}\n
\n
\n \n {this.props.serverVersion}\n
\n
\n \n {this.props.serverAddress}\n
\n
\n
\n );\n }\n};\n","// Login form.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport CheckBox from '../widgets/checkbox.jsx';\nimport VisiblePassword from '../widgets/visible-password.jsx';\n\nimport LocalStorageUtil from '../lib/local-storage.js';\n\nexport default class LoginView extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n login: props.login,\n password: '',\n hostName: props.serverAddress,\n saveToken: props.persist\n };\n this.handleLoginChange = this.handleLoginChange.bind(this);\n this.handlePasswordChange = this.handlePasswordChange.bind(this);\n this.handleToggleSaveToken = this.handleToggleSaveToken.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n }\n\n handleLoginChange(e) {\n this.setState({login: e.target.value});\n }\n\n handlePasswordChange(e) {\n this.setState({password: e.target.value});\n }\n\n handleToggleSaveToken() {\n this.props.onPersistenceChange(!this.state.saveToken);\n this.setState({saveToken: !this.state.saveToken});\n }\n\n handleSubmit(e) {\n e.preventDefault();\n this.props.onLogin(this.state.login.trim(), this.state.password.trim());\n }\n\n render() {\n let submitClasses = 'primary';\n if (this.props.disabled) {\n submitClasses += ' disabled';\n }\n\n return (\n \n );\n }\n};\n/* END Login */\n","import React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nconst messages = defineMessages({\n invalid_id: {\n id: 'error_invalid_id',\n defaultMessage: 'Invalid ID',\n description: 'Error message'\n }\n});\n\nclass NewTopicById extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n groupId: '',\n };\n\n this.handleChange = this.handleChange.bind(this);\n this.handleKeyPress = this.handleKeyPress.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n }\n\n handleChange(e) {\n this.setState({groupId: e.target.value});\n }\n\n handleKeyPress(e) {\n if (e.key === 'Enter') {\n this.handleSubmit(e);\n }\n }\n\n handleSubmit(e) {\n e.preventDefault();\n if (this.state.groupId) {\n const name = this.state.groupId.trim();\n const prefix = name.substr(0, 3);\n if (name.length > 3 && ['usr', 'grp', 'chn'].includes(prefix)) {\n this.props.onSubmit(name);\n } else {\n this.props.onError(this.props.intl.formatMessage(messages.invalid_id), 'err');\n }\n }\n }\n\n render() {\n return (\n \n
\n {\n (prompt) => \n }\n
\n
\n \n
\n
\n );\n }\n};\n\nexport default injectIntl(NewTopicById);\n","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport AvatarUpload from './avatar-upload.jsx';\nimport CheckBox from './checkbox.jsx';\nimport TagManager from './tag-manager.jsx';\n\nimport { MAX_TITLE_LENGTH } from '../config.js';\n\nexport default class NewTopicGroup extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n fn: '', // full/formatted name\n private: '',\n imageDataUrl: null,\n tags: [],\n isChannel: false\n };\n\n this.handleFnChange = this.handleFnChange.bind(this);\n this.handlePrivateChange = this.handlePrivateChange.bind(this);\n this.handleImageChanged = this.handleImageChanged.bind(this);\n this.handleTagsChanged = this.handleTagsChanged.bind(this);\n this.handleChannelToggle = this.handleChannelToggle.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n }\n\n handleFnChange(e) {\n this.setState({fn: e.target.value});\n }\n\n handlePrivateChange(e) {\n this.setState({private: e.target.value});\n }\n\n handleImageChanged(img) {\n this.setState({imageDataUrl: img});\n }\n\n handleTagsChanged(tags) {\n this.setState({tags: tags});\n }\n\n handleChannelToggle() {\n this.setState({isChannel: !this.state.isChannel});\n }\n\n handleSubmit(e) {\n e.preventDefault();\n\n const fn = this.state.fn.trim().substring(0, MAX_TITLE_LENGTH);\n const comment = this.state.private.trim().substring(0, MAX_TITLE_LENGTH);\n if (fn) {\n this.props.onSubmit(fn, this.state.imageDataUrl, comment, this.state.tags, this.state.isChannel);\n }\n }\n\n render() {\n let submitClasses = 'primary';\n if (this.props.disabled) {\n submitClasses += ' disabled';\n }\n return (\n \n );\n }\n};\n","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nexport default class SearchContacts extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n edited: false,\n search: ''\n };\n\n this.handleSearchChange = this.handleSearchChange.bind(this);\n this.handleSearch = this.handleSearch.bind(this);\n this.handleClear = this.handleClear.bind(this);\n this.handleKeyDown = this.handleKeyDown.bind(this);\n }\n\n componentWillUnmount() {\n if (this.state.edited) {\n this.setState({search: '', edited: false});\n this.props.onSearchContacts(Tinode.DEL_CHAR);\n }\n }\n\n handleSearchChange(e) {\n this.setState({search: e.target.value});\n }\n\n handleSearch(e) {\n e.preventDefault();\n var query = this.state.search.trim();\n this.setState({edited: (query.length > 0)});\n this.props.onSearchContacts(query.length > 0 ? query : Tinode.DEL_CHAR);\n }\n\n handleClear(e) {\n e.preventDefault();\n if (this.state.edited) {\n this.props.onSearchContacts(Tinode.DEL_CHAR);\n }\n this.setState({search: '', edited: false});\n }\n\n handleKeyDown(e) {\n if (e.key === 'Enter') {\n this.handleSearch(e);\n } else if (e.key === 'Escape') {\n this.handleClear();\n }\n }\n\n render() {\n return (\n \n
\n
search\n
{\n (search_placeholder) => \n }\n
\n close\n \n
\n
\n );\n }\n};\n","// Create new topic and invite users or send an invite.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport ContactList from '../widgets/contact-list.jsx';\nimport NewTopicById from '../widgets/new-topic-by-id.jsx';\nimport NewTopicGroup from '../widgets/new-topic-group.jsx';\nimport SearchContacts from '../widgets/search-contacts.jsx';\n\nimport HashNavigation from '../lib/navigation.js';\nimport { theCard } from '../lib/utils.js';\n\nconst messages = defineMessages({\n search_for_contacts: {\n id: \"search_for_contacts\",\n defaultMessage: \"Use search to find contacts\",\n description: \"Text shown in contacts view when user entered no search query.\"\n },\n search_no_results: {\n id: \"search_no_results\",\n defaultMessage: \"Search returned no results\",\n description: \"Text shown in contacts view when query returned no results.\"\n }\n});\n\nclass NewTopicView extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n tabSelected: 'p2p',\n searchQuery: null\n };\n\n this.handleTabClick = this.handleTabClick.bind(this);\n this.handleSearchContacts = this.handleSearchContacts.bind(this);\n this.handleContactSelected = this.handleContactSelected.bind(this);\n this.handleNewGroupSubmit = this.handleNewGroupSubmit.bind(this);\n this.handleGroupByID = this.handleGroupByID.bind(this);\n }\n\n componentDidMount() {\n this.props.onInitFind();\n }\n\n handleTabClick(e) {\n e.preventDefault();\n HashNavigation.navigateTo(HashNavigation.addUrlParam(window.location.hash, 'tab', e.currentTarget.dataset.id));\n this.setState({tabSelected: e.currentTarget.dataset.id});\n }\n\n handleSearchContacts(query) {\n this.props.onSearchContacts(query);\n this.setState({searchQuery: Tinode.isNullValue(query) ? null : query});\n }\n\n handleContactSelected(sel) {\n if (this.state.tabSelected === 'p2p') {\n HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab'));\n this.props.onCreateTopic(sel);\n }\n }\n\n handleNewGroupSubmit(name, dataUrl, priv, tags, isChannel) {\n HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab'));\n this.props.onCreateTopic(undefined, theCard(name, dataUrl), priv, tags, isChannel);\n }\n\n handleGroupByID(topicName) {\n HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab'));\n this.props.onCreateTopic(topicName);\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n const no_contacts_placeholder = formatMessage(this.state.searchQuery ?\n messages.search_no_results : messages.search_for_contacts);\n return (\n \n
\n {this.state.tabSelected === 'grp' ?\n
:\n this.state.tabSelected === 'byid' ?\n
:\n
\n \n \n
}\n
\n );\n }\n};\n\nexport default injectIntl(NewTopicView);\n","// A password reset form.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport VisiblePassword from '../widgets/visible-password.jsx';\n\nimport HashNavigation from '../lib/navigation.js';\n\nexport default class PasswordResetView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n email: '',\n password: ''\n };\n\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleEmailChange = this.handleEmailChange.bind(this);\n this.handlePasswordChange = this.handlePasswordChange.bind(this);\n }\n\n componentDidMount() {\n let parsed = HashNavigation.parseUrlHash(window.location.hash);\n this.setState({token: parsed.params.token, scheme: parsed.params.scheme});\n }\n\n handleSubmit(e) {\n e.preventDefault();\n if (this.state.token) {\n this.props.onReset(this.state.scheme, this.state.password.trim(), this.state.token);\n } else {\n this.props.onRequest('email', this.state.email.trim());\n }\n }\n\n handleEmailChange(e) {\n this.setState({email: e.target.value});\n }\n\n handlePasswordChange(e) {\n this.setState({password: e.target.value});\n }\n\n render() {\n let reset = (this.state.token && this.state.scheme);\n return (\n \n );\n }\n}\n","import React from 'react';\n\nimport { KNOWN_HOSTS } from '../config.js';\n\n/* Combobox for selecting host name */\n\nexport default class HostSelector extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n hostName: props.serverAddress,\n changed: false\n };\n\n this.handleHostNameChange = this.handleHostNameChange.bind(this);\n this.handleEditingFinished = this.handleEditingFinished.bind(this);\n }\n\n handleHostNameChange(e) {\n this.setState({hostName: e.target.value, changed: true});\n }\n\n handleEditingFinished() {\n if (this.state.changed) {\n this.setState({changed: false});\n this.props.onServerAddressChange(this.state.hostName.trim());\n }\n }\n\n render() {\n var hostOptions = [];\n for (let key in KNOWN_HOSTS) {\n let item = KNOWN_HOSTS[key];\n hostOptions.push(\n \n );\n }\n return (\n \n \n \n
\n );\n }\n}\n","// Tinode config panel.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport HostSelector from '../widgets/host-selector.jsx';\n\nexport default class SettingsView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n transport: props.transport || 'def',\n serverAddress: props.serverAddress,\n };\n\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleTransportSelected = this.handleTransportSelected.bind(this);\n this.handleServerAddressChange = this.handleServerAddressChange.bind(this);\n }\n\n handleSubmit(e) {\n e.preventDefault();\n this.props.onUpdate({\n transport: this.state.transport,\n serverAddress: this.state.serverAddress\n });\n }\n\n handleTransportSelected(e) {\n this.setState({transport: e.currentTarget.value});\n }\n\n handleServerAddressChange(name) {\n this.setState({serverAddress: name});\n }\n\n render() {\n const names = {def: \"default\", ws: \"websocket\", lp: \"long polling\"};\n const transportOptions = [];\n ['def', 'ws', 'lp'].map((item) => {\n const id = 'transport-' + item;\n const name = names[item];\n transportOptions.push(\n \n \n \n \n );\n });\n\n return (\n \n );\n }\n};\n","// ValidationView: panel for confirming credentials, like email or phone.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nconst messages = defineMessages({\n phone: {\n id: 'phone_dative',\n defaultMessage: 'phone',\n description: \"Dative case of 'phone', i.e. 'phone' in 'by phone'\",\n },\n email: {\n id: 'email_dative',\n defaultMessage: 'email',\n description: \"Dative case of 'email', i.e. 'email' in 'by email'\",\n }\n});\n\nclass ValidationView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n code: props.credCode || ''\n };\n\n this.handleChange = this.handleChange.bind(this);\n this.handleKeyPress = this.handleKeyPress.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n handleChange(e) {\n this.setState({code: e.target.value});\n }\n\n handleKeyPress(e) {\n if (e.key === 'Enter') {\n this.handleSubmit(e);\n } else if (e.key == 'Escape') {\n this.handleCancel(e);\n }\n }\n\n handleSubmit(e) {\n e.preventDefault();\n if (this.state.code && this.state.code.trim()) {\n this.props.onSubmit(this.props.credMethod, this.state.code.trim());\n }\n }\n\n handleCancel(e) {\n e.preventDefault();\n this.props.onCancel();\n }\n\n\n render() {\n const { formatMessage } = this.props.intl;\n const methods = {'email': formatMessage(messages.email), 'tel': formatMessage(messages.phone)};\n let method = methods[this.props.credMethod] || this.props.credMethod;\n return (\n \n
\n \n
\n
\n {\n (numbers_only) => \n }\n
\n
\n \n \n
\n
\n );\n }\n};\n\nexport default injectIntl(ValidationView);\n","import React from 'react';\nimport { defineMessages, injectIntl } from 'react-intl';\n\nimport ErrorPanel from '../widgets/error-panel.jsx';\nimport LoadSpinner from '../widgets/load-spinner.jsx';\nimport SideNavbar from '../widgets/side-navbar.jsx';\n\nimport AvatarCropView from './avatar-crop-view.jsx';\nimport ContactsView from './contacts-view.jsx';\nimport CreateAccountView from './create-account-view.jsx';\nimport EditAccountView from './edit-account-view.jsx';\nimport AccGeneralView from './acc-general-view.jsx';\nimport AccNotificationsView from './acc-notifications-view.jsx';\nimport AccSecurityView from './acc-security-view.jsx';\nimport AccSupportView from './acc-support-view.jsx';\nimport LoginView from './login-view.jsx';\nimport NewTopicView from './new-topic-view.jsx';\nimport PasswordResetView from './password-reset-view.jsx';\nimport SettingsView from './settings-view.jsx';\nimport ValidationView from './validation-view.jsx';\n\n// Panel titles for translation.\nconst messages = defineMessages({\n login: {\n id: 'sidepanel_title_login',\n description: 'Sidepanel title for LoginView.',\n defaultMessage: 'Sign In'\n },\n register: {\n id: 'sidepanel_title_register',\n description: 'Sidepanel title for CreateAccountView.',\n defaultMessage: 'Create Account'\n },\n settings: {\n id: 'sidepanel_title_settings',\n description: 'Sidepanel title for SettingsView.',\n defaultMessage: 'Settings'\n },\n edit: {\n id: 'sidepanel_title_account_settings',\n description: 'Sidepanel title for EditAccountView.',\n defaultMessage: 'Account Settings'\n },\n general: {\n id: 'sidepanel_title_acc_general',\n description: 'Sidepanel title for AccGeneralView.',\n defaultMessage: 'General'\n },\n security: {\n id: 'sidepanel_title_acc_security',\n description: 'Sidepanel title for AccSecurityView.',\n defaultMessage: 'Security'\n },\n notif: {\n id: 'sidepanel_title_acc_notifications',\n description: 'Sidepanel title for AccNotificationsView.',\n defaultMessage: 'Notifications'\n },\n support: {\n id: 'sidepanel_title_acc_support',\n description: 'Sidepanel title for AccSupportView.',\n defaultMessage: 'Support'\n },\n newtpk: {\n id: 'sidepanel_title_newtpk',\n description: 'Sidepanel title for NewTopicView.',\n defaultMessage: 'Start New Chat'\n },\n cred: {\n id: 'sidepanel_title_cred',\n description: 'Sidepanel title for ValidationView.',\n defaultMessage: 'Confirm Credentials'\n },\n crop: {\n id: 'sidepanel_title_crop',\n description: 'Sidepanel title for AvatarCropView.',\n defaultMessage: 'Drag to Adjust'\n },\n reset: {\n id: 'sidepanel_title_reset',\n description: 'Sidepanel title for PasswordResetView.',\n defaultMessage: 'Reset Password'\n },\n archive: {\n id: 'sidepanel_title_archive',\n description: 'Sidepanel title for ContactsView-Archive.',\n defaultMessage: 'Archived Chats'\n },\n blocked: {\n id: 'sidepanel_title_blocked',\n description: 'Sidepanel title for ContactsView-Blocked.',\n defaultMessage: 'Blocked Chats'\n }\n});\n\nclass SidepanelView extends React.Component {\n constructor(props) {\n super(props);\n\n this.handleNewTopic = this.handleNewTopic.bind(this);\n }\n\n handleNewTopic() {\n this.props.onBasicNavigate('newtpk');\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n const view = this.props.state || (this.props.myUserId ? 'contacts' : 'login');\n\n let title, avatar, badges;\n if (view == 'contacts') {\n title = this.props.title;\n avatar = this.props.avatar ? this.props.avatar : true;\n badges = this.props.trustedBadges;\n } else {\n title = formatMessage(messages[view]);\n avatar = false;\n badges = null;\n }\n\n let onCancel;\n if (['login', 'contacts'].indexOf(view) == -1) {\n onCancel = this.props.onCancel;\n }\n\n return (\n \n
\n\n
\n\n
\n\n {view === 'login' ?\n
:\n\n view === 'register' ?\n
:\n\n view === 'settings' ?\n
:\n\n view === 'edit' ?\n
:\n\n view === 'general' ?\n
:\n\n view === 'notif' ?\n
:\n\n view === 'security' ?\n
:\n\n view === 'support' ?\n
:\n\n (view === 'contacts' || view == 'archive' || view == 'blocked') ?\n
:\n\n view === 'newtpk' ?\n
:\n\n view === 'cred' ?\n
:\n\n view === 'reset' ?\n
:\n\n view === 'crop' ?\n
:\n\n null}\n
\n );\n }\n};\n\nexport default injectIntl(SidepanelView);\n","import { KNOWN_HOSTS, DEFAULT_HOST } from '../config.js';\n\n// Detect server address from the URL\nexport function detectServerAddress() {\n let host = DEFAULT_HOST;\n if (typeof window.location == 'object') {\n if (window.location.protocol == 'file:' || window.location.hostname == 'localhost') {\n host = KNOWN_HOSTS.local;\n } else if (window.location.hostname) {\n host = window.location.hostname + (window.location.port ? ':' + window.location.port : '');\n }\n }\n return host;\n}\n\n// Detect if the page is served over HTTPS.\nexport function isSecureConnection() {\n if (typeof window.location == 'object') {\n return window.location.protocol == 'https:';\n }\n return false;\n}\n\nexport function isLocalHost() {\n if (typeof window.location == 'object') {\n return window.location.hostname == 'localhost';\n }\n return false;\n}\n","// The top-level class to hold all functionality together.\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport * as firebase from 'firebase/app';\nimport 'firebase/messaging';\n\nimport Tinode from 'tinode-sdk';\n\nimport Alert from '../widgets/alert.jsx';\nimport ContextMenu from '../widgets/context-menu.jsx';\n\nimport InfoView from './info-view.jsx';\nimport MessagesView from './messages-view.jsx';\nimport SidepanelView from './sidepanel-view.jsx';\n\nimport { API_KEY, APP_NAME, DEFAULT_P2P_ACCESS_MODE, LOGGING_ENABLED,\n MEDIA_BREAKPOINT, RECEIVED_DELAY } from '../config.js';\nimport { PACKAGE_VERSION } from '../version.js';\nimport { base64ReEncode, makeImageDataUrl } from '../lib/blob-helpers.js';\nimport { detectServerAddress, isLocalHost, isSecureConnection } from '../lib/host-name.js';\nimport LocalStorageUtil from '../lib/local-storage.js';\nimport HashNavigation from '../lib/navigation.js';\nimport { secondsToTime } from '../lib/strformat.js'\nimport { updateFavicon } from '../lib/utils.js';\n\n// Sound to play on message received.\nconst POP_SOUND = new Audio('audio/msg.mp3');\n\nconst messages = defineMessages({\n reconnect_countdown: {\n id: 'reconnect_countdown',\n defaultMessage: 'Disconnected. Reconnecting in {seconds}…',\n description: 'Message shown when an app update is available.'\n },\n reconnect_now: {\n id: 'reconnect_now',\n defaultMessage: 'Try now',\n description: 'Prompt for reconnecting now'\n },\n push_init_failed: {\n id: 'push_init_failed',\n defaultMessage: 'Failed to initialize push notifications',\n description: 'Error message when push notifications have failed to initialize.'\n },\n invalid_security_token: {\n id: 'invalid_security_token',\n defaultMessage: 'Invalid security token',\n description: 'Error message when resetting password.'\n },\n no_connection: {\n id: 'no_connection',\n defaultMessage: 'No connection',\n description: 'Warning that the user is offline.'\n },\n code_doesnot_match: {\n id: 'code_doesnot_match',\n defaultMessage: 'Code does not match',\n description: 'Error message when the credential validation code is incorrect.'\n },\n menu_item_info: {\n id: 'menu_item_info',\n defaultMessage: 'Info',\n description: 'Show extended topic information'\n }\n});\n\nclass TinodeWeb extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = this.getBlankState();\n\n this.handleResize = this.handleResize.bind(this);\n this.handleHashRoute = this.handleHashRoute.bind(this);\n this.handleOnline = this.handleOnline.bind(this);\n this.checkForAppUpdate = this.checkForAppUpdate.bind(this);\n this.handleVisibilityEvent = this.handleVisibilityEvent.bind(this);\n this.handleError = this.handleError.bind(this);\n this.handleLoginRequest = this.handleLoginRequest.bind(this);\n this.handlePersistenceChange = this.handlePersistenceChange.bind(this);\n this.handleConnected = this.handleConnected.bind(this);\n this.handleAutoreconnectIteration = this.handleAutoreconnectIteration.bind(this);\n this.doLogin = this.doLogin.bind(this);\n this.handleCredentialsRequest = this.handleCredentialsRequest.bind(this);\n this.handleLoginSuccessful = this.handleLoginSuccessful.bind(this);\n this.handleDisconnect = this.handleDisconnect.bind(this);\n this.tnMeMetaDesc = this.tnMeMetaDesc.bind(this);\n this.tnMeContactUpdate = this.tnMeContactUpdate.bind(this);\n this.tnMeSubsUpdated = this.tnMeSubsUpdated.bind(this);\n this.resetContactList = this.resetContactList.bind(this);\n this.tnData = this.tnData.bind(this);\n this.tnInitFind = this.tnInitFind.bind(this);\n this.tnFndSubsUpdated = this.tnFndSubsUpdated.bind(this);\n this.handleSearchContacts = this.handleSearchContacts.bind(this);\n this.handleTopicSelected = this.handleTopicSelected.bind(this);\n this.handleHideMessagesView = this.handleHideMessagesView.bind(this);\n this.handleSendMessage = this.handleSendMessage.bind(this);\n this.handleNewChatInvitation = this.handleNewChatInvitation.bind(this);\n this.handleNewAccount = this.handleNewAccount.bind(this);\n this.handleNewAccountRequest = this.handleNewAccountRequest.bind(this);\n this.handleUpdateAccountRequest = this.handleUpdateAccountRequest.bind(this);\n this.handleUpdateAccountTagsRequest = this.handleUpdateAccountTagsRequest.bind(this);\n this.handleToggleIncognitoMode = this.handleToggleIncognitoMode.bind(this);\n this.handleSettings = this.handleSettings.bind(this);\n this.handleGlobalSettings = this.handleGlobalSettings.bind(this);\n this.handleShowArchive = this.handleShowArchive.bind(this);\n this.handleShowBlocked = this.handleShowBlocked.bind(this);\n this.handleToggleMessageSounds = this.handleToggleMessageSounds.bind(this);\n this.handleCredAdd = this.handleCredAdd.bind(this);\n this.handleCredDelete = this.handleCredDelete.bind(this);\n this.handleCredConfirm = this.handleCredConfirm.bind(this);\n this.initFCMessaging = this.initFCMessaging.bind(this);\n this.togglePushToken = this.togglePushToken.bind(this);\n this.requestPushToken = this.requestPushToken.bind(this);\n this.handlePushMessage = this.handlePushMessage.bind(this);\n this.handleSidepanelCancel = this.handleSidepanelCancel.bind(this);\n this.handleStartTopicRequest = this.handleStartTopicRequest.bind(this);\n this.handleNewTopicCreated = this.handleNewTopicCreated.bind(this);\n this.handleTopicUpdateRequest = this.handleTopicUpdateRequest.bind(this);\n this.handleChangePermissions = this.handleChangePermissions.bind(this);\n this.handleTagsUpdated = this.handleTagsUpdated.bind(this);\n this.handleLogout = this.handleLogout.bind(this);\n this.handleDeleteAccount = this.handleDeleteAccount.bind(this);\n this.handleDeleteMessagesRequest = this.handleDeleteMessagesRequest.bind(this);\n this.handleLeaveUnsubRequest = this.handleLeaveUnsubRequest.bind(this);\n this.handleBlockTopicRequest = this.handleBlockTopicRequest.bind(this);\n this.handleReportTopic = this.handleReportTopic.bind(this);\n this.handleShowContextMenu = this.handleShowContextMenu.bind(this);\n this.defaultTopicContextMenu = this.defaultTopicContextMenu.bind(this);\n this.handleHideContextMenu = this.handleHideContextMenu.bind(this);\n this.handleShowAlert = this.handleShowAlert.bind(this);\n this.handleShowInfoView = this.handleShowInfoView.bind(this);\n this.handleHideInfoView = this.handleHideInfoView.bind(this);\n this.handleMemberUpdateRequest = this.handleMemberUpdateRequest.bind(this);\n this.handleValidateCredentialsRequest = this.handleValidateCredentialsRequest.bind(this);\n this.handlePasswordResetRequest = this.handlePasswordResetRequest.bind(this);\n this.handleResetPassword = this.handleResetPassword.bind(this);\n this.handleContextMenuAction = this.handleContextMenuAction.bind(this);\n }\n\n getBlankState() {\n const settings = LocalStorageUtil.getObject('settings') || {};\n const persist = !!LocalStorageUtil.getObject('keep-logged-in');\n\n return {\n connected: false,\n // Connected and subscribed to 'me'\n ready: false,\n // Try to re-login on new connection.\n autoLogin: 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 incognitoMode: false,\n desktopAlerts: persist && settings.desktopAlerts,\n desktopAlertsEnabled: (isSecureConnection() || isLocalHost()) &&\n (typeof firebase != 'undefined') && (typeof navigator != 'undefined') &&\n (typeof FIREBASE_INIT != 'undefined'),\n firebaseToken: persist ? LocalStorageUtil.getObject('firebase-token') : null,\n\n applicationVisible: !document.hidden,\n\n errorText: '',\n errorLevel: null,\n errorAction: undefined,\n errorActionText: null,\n\n sidePanelSelected: 'login',\n sidePanelTitle: null,\n sidePanelAvatar: null,\n myTrustedBadges: [],\n loadSpinnerVisible: false,\n\n login: '',\n password: '',\n persist: persist,\n myUserId: null,\n liveConnection: navigator.onLine,\n topicSelected: '',\n topicSelectedOnline: false,\n topicSelectedAcs: null,\n newTopicParams: null,\n loginDisabled: false,\n displayMobile: (window.innerWidth <= MEDIA_BREAKPOINT),\n showInfoPanel: false,\n mobilePanel: 'sidepanel',\n\n contextMenuVisible: false,\n contextMenuBounds: null,\n contextMenuClickAt: null,\n contextMenuParams: null,\n contextMenuItems: [],\n\n // Modal alert dialog.\n alertVisible: false,\n alertParams: {},\n\n // Chats as shown in the ContactsView\n chatList: [],\n // Contacts returned by a search query.\n searchResults: [],\n // Merged results of a search query and p2p chats.\n searchableContacts: [],\n credMethod: undefined,\n credCode: undefined,\n // Topic to go to after login.\n requestedTopic: undefined\n };\n }\n\n componentDidMount() {\n window.addEventListener('resize', this.handleResize);\n window.addEventListener('online', (e) => { this.handleOnline(true); });\n window.addEventListener('offline', (e) => { this.handleOnline(false); });\n window.addEventListener('hashchange', this.handleHashRoute);\n // Process background notifications from the service worker.\n const serviceWorkerChannel = new BroadcastChannel('tinode-sw');\n serviceWorkerChannel.addEventListener('message', this.handlePushMessage);\n\n // Window/tab visible or invisible for pausing timers.\n document.addEventListener('visibilitychange', this.handleVisibilityEvent);\n\n this.setState({\n viewportWidth: document.documentElement.clientWidth,\n viewportHeight: document.documentElement.clientHeight\n });\n\n new Promise((resolve, reject) => {\n this.tinode = TinodeWeb.tnSetup(this.state.serverAddress, this.state.transport,\n this.props.intl.locale, this.state.persist, resolve);\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\n this.tinode.onAutoreconnectIteration = this.handleAutoreconnectIteration;\n }).then(() => {\n // Initialize desktop alerts.\n if (this.state.desktopAlertsEnabled) {\n this.initFCMessaging().then(() => {\n if (this.state.desktopAlerts) {\n if (!this.state.firebaseToken) {\n this.togglePushToken(true);\n } else {\n this.tinode.setDeviceToken(this.state.firebaseToken);\n }\n }\n }).catch(() => {\n // do nothing: handled earlier.\n // catch needed to pervent unnecessary logging of error.\n });\n }\n\n // Parse and save the hash navigation params.\n const parsedNav = HashNavigation.parseUrlHash(window.location.hash);\n\n // Read contacts from cache.\n this.resetContactList();\n\n const token = this.state.persist ? LocalStorageUtil.getObject('auth-token') : undefined;\n if (token) {\n this.setState({autoLogin: true});\n\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((err) => {\n // Socket error\n this.handleError(err.message, 'err');\n });\n }\n\n // Save possible topic name and navigate to blank state.\n this.setState({requestedTopic: parsedNav.path[1]});\n HashNavigation.navigateTo('');\n\n this.readTimer = null;\n this.readTimerCallback = null;\n\n this.handleHashRoute();\n });\n }\n\n componentWillUnmount() {\n window.removeEventListener('resize', this.handleResize);\n window.removeEventListener('hashchange', this.handleHashRoute);\n document.removeEventListener('visibilitychange', this.handleVisibilityEvent);\n }\n\n // Setup transport (usually websocket) and server address. This will terminate connection with the server.\n static tnSetup(serverAddress, transport, locale, persistentCache, onSetupCompleted) {\n const tinode = new Tinode({appName: APP_NAME, host: serverAddress, apiKey: API_KEY, transport: transport,\n secure: isSecureConnection(), persist: persistentCache}, onSetupCompleted);\n tinode.setHumanLanguage(locale);\n tinode.enableLogging(LOGGING_ENABLED, true);\n return tinode;\n }\n\n // Notifiy Tinode that a push message was received from the server.\n handlePushMessage(data) {\n if (data.what == 'msg' && Tinode.isChannelTopicName(data.topic)) {\n // The last argument is a fake user Id: otherwise the update is seen as one from the current user.\n this.tinode.oobNotification(data.topic, data.seq, 'fake-uid');\n }\n }\n\n initFCMessaging() {\n const {formatMessage, locale} = this.props.intl;\n const onError = (msg, err) => {\n console.log(msg, err);\n this.handleError(formatMessage(messages.push_init_failed), 'err');\n this.setState({desktopAlertsEnabled: false});\n }\n\n try {\n this.fbPush = firebase.initializeApp(FIREBASE_INIT, APP_NAME).messaging();\n this.fbPush.usePublicVapidKey(FIREBASE_INIT.messagingVapidKey);\n\n return navigator.serviceWorker.register('/service-worker.js').then((reg) => {\n this.checkForAppUpdate(reg);\n this.fbPush.useServiceWorker(reg);\n reg.active.postMessage(JSON.stringify({locale: locale, version: PACKAGE_VERSION}));\n\n // Google could not be bothered to mention that onTokenRefresh is never called.\n this.fbPush.onTokenRefresh(() => {\n this.requestPushToken();\n });\n\n // Handhe FCM pushes\n // (a) for channels always,\n // (b) pushes when the app is in foreground but has not focus.\n this.fbPush.onMessage(payload => { this.handlePushMessage(payload.data); });\n\n return reg;\n }).catch((err) => {\n // registration failed :(\n onError(\"Failed to register service worker:\", err);\n return Promise.reject(err);\n });\n } catch (err) {\n onError(\"Failed to initialize push notifications\", err);\n return Promise.reject(err);\n }\n }\n\n handleResize() {\n const mobile = document.documentElement.clientWidth <= MEDIA_BREAKPOINT;\n this.setState({\n viewportWidth: document.documentElement.clientWidth,\n viewportHeight: document.documentElement.clientHeight\n });\n if (this.state.displayMobile != mobile) {\n this.setState({displayMobile: mobile});\n }\n }\n\n // Check if a newer version of TinodeWeb app is available at the server.\n checkForAppUpdate(reg) {\n reg.onupdatefound = () => {\n const installingWorker = reg.installing;\n installingWorker.onstatechange = () => {\n if (installingWorker.state == 'installed' && navigator.serviceWorker.controller) {\n const msg = <>\n \n \n .>;\n this.handleError(msg, 'info');\n }\n }\n }\n }\n\n // Handle for hashchange event: display appropriate panels.\n handleHashRoute() {\n const hash = HashNavigation.parseUrlHash(window.location.hash);\n if (hash.path && hash.path.length > 0) {\n // Left-side panel selector.\n if (['register','settings','edit','notif','security','support','general',\n 'cred','crop','reset','newtpk','archive','blocked','contacts',''].includes(hash.path[0])) {\n this.setState({sidePanelSelected: hash.path[0]});\n } else {\n console.log(\"Unknown sidepanel view\", hash.path[0]);\n }\n\n // Topic for MessagesView selector.\n const topicName = hash.path[1];\n if (Tinode.topicType(topicName) && topicName != this.state.topicSelected) {\n const newState = {\n topicSelected: topicName\n };\n const acs = this.tinode.getTopicAccessMode(topicName);\n if (acs) {\n newState.topicSelectedAcs = acs;\n }\n this.setState(newState);\n }\n } else {\n // Empty hashpath\n this.setState({sidePanelSelected: ''});\n }\n\n // Save validation credentials, if available.\n if (hash.params.method) {\n this.setState({ credMethod: hash.params.method });\n }\n if (hash.params.code) {\n this.setState({ credCode: hash.params.code });\n }\n\n // Additional parameters of panels.\n this.setState({\n showInfoPanel: hash.params.info,\n newTopicTabSelected: hash.params.tab\n });\n }\n\n handleOnline(online) {\n if (online) {\n this.handleError();\n clearInterval(this.reconnectCountdown);\n this.tinode.reconnect();\n } else {\n this.handleError(this.props.intl.formatMessage(messages.no_connection), 'warn');\n }\n this.setState({liveConnection: online});\n }\n\n handleVisibilityEvent() {\n this.setState({applicationVisible: !document.hidden});\n }\n\n handleError(err, level, action, actionText) {\n this.setState({errorText: err, errorLevel: level, errorAction: action, errorActionText: actionText});\n }\n\n // User clicked Login button in the side panel.\n handleLoginRequest(login, password) {\n this.setState({\n loginDisabled: true,\n login: login,\n password: password,\n loadSpinnerVisible: true,\n autoLogin: true\n });\n this.handleError('', null);\n\n if (this.tinode.isConnected()) {\n this.doLogin(login, password, {meth: this.state.credMethod, resp: this.state.credCode});\n } else {\n this.tinode.connect().catch((err) => {\n // Socket error\n this.setState({loginDisabled: false, autoLogin: false, loadSpinnerVisible: false});\n this.handleError(err.message, 'err');\n });\n }\n }\n\n // Enable or disable saving the password and IndexedDB.\n handlePersistenceChange(persist) {\n if (persist) {\n this.tinode.initStorage().then(() => {\n console.log(\"storage initialized\");\n LocalStorageUtil.setObject('keep-logged-in', true);\n this.setState({persist: true});\n });\n } else {\n this.tinode.clearStorage().then(() => {\n console.log(\"storage cleared\");\n LocalStorageUtil.setObject('keep-logged-in', false);\n this.setState({persist: false});\n });\n }\n }\n\n // Connection succeeded.\n handleConnected() {\n // Just to be sure.\n clearInterval(this.reconnectCountdown);\n this.handleError();\n\n const params = this.tinode.getServerInfo();\n this.setState({\n serverVersion: params.ver + ' ' + (params.build ? params.build : 'none')\n });\n\n if (this.state.autoLogin) {\n this.doLogin(this.state.login, this.state.password, {meth: this.state.credMethod, resp: this.state.credCode});\n }\n }\n\n // Called for each auto-reconnect iteration.\n handleAutoreconnectIteration(sec, prom) {\n clearInterval(this.reconnectCountdown);\n\n if (sec < 0) {\n // Clear error\n this.handleError();\n return;\n }\n\n if (prom) {\n // Reconnecting now\n prom.then(() => {\n // Reconnected: clear error\n this.handleError();\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n return;\n }\n\n const {formatMessage} = this.props.intl;\n let count = sec / 1000;\n count = count | count;\n this.reconnectCountdown = setInterval(() => {\n const timeLeft = (count > 99) ? secondsToTime(count) : count;\n this.handleError(\n formatMessage(messages.reconnect_countdown, {seconds: timeLeft}),\n 'warn',\n () => {\n clearInterval(this.reconnectCountdown);\n this.tinode.reconnect();\n },\n formatMessage(messages.reconnect_now)\n );\n count -= 1;\n }, 1000);\n }\n\n // Connection lost\n handleDisconnect(err) {\n this.setState({\n connected: false,\n ready: false,\n topicSelectedOnline: false,\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 doLogin(login, password, cred) {\n if (this.tinode.isAuthenticated()) {\n // Already logged in. Go to default screen.\n HashNavigation.navigateTo('');\n return;\n }\n // Sanitize and package credentail.\n cred = Tinode.credential(cred);\n // Try to login with login/password. If they are not available, try token. If no token, ask for login/password.\n let promise = null;\n const token = this.tinode.getAuthToken();\n if (login && password) {\n this.setState({password: null});\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((ctrl) => {\n if (ctrl.code >= 300 && ctrl.text === 'validate credentials') {\n this.setState({loadSpinnerVisible: false});\n if (cred) {\n this.handleError(this.props.intl.formatMessage(messages.code_doesnot_match), 'warn');\n }\n this.handleCredentialsRequest(ctrl.params);\n } else {\n this.handleLoginSuccessful();\n }\n }).catch((err) => {\n // Login failed, report error.\n this.setState({\n loginDisabled: false,\n credMethod: undefined,\n credCode: undefined,\n loadSpinnerVisible: false,\n autoLogin: false\n });\n this.handleError(err.message, 'err');\n localStorage.removeItem('auth-token');\n HashNavigation.navigateTo('');\n });\n } else {\n // No login credentials provided.\n // Make sure we are on the login page.\n HashNavigation.navigateTo('');\n this.setState({loginDisabled: false});\n }\n }\n\n handleCredentialsRequest(params) {\n const parsed = HashNavigation.parseUrlHash(window.location.hash);\n parsed.path[0] = 'cred';\n parsed.params['method'] = params.cred[0];\n HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsed.path, parsed.params));\n }\n\n handleLoginSuccessful() {\n this.handleError();\n\n // Refresh authentication token.\n if (LocalStorageUtil.getObject('keep-logged-in')) {\n LocalStorageUtil.setObject('auth-token', this.tinode.getAuthToken());\n }\n\n const goToTopic = this.state.requestedTopic;\n // Logged in fine, subscribe to 'me' attaching callbacks from the contacts view.\n const 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 autoLogin: true,\n requestedTopic: undefined,\n });\n // Subscribe, fetch topic desc, the list of subscriptions. Messages are not fetched.\n me.subscribe(\n me.startMetaQuery().\n withLaterSub().\n withDesc().\n withTags().\n withCred().\n build()\n ).catch((err) => {\n this.tinode.disconnect();\n localStorage.removeItem('auth-token');\n this.handleError(err.message, 'err');\n HashNavigation.navigateTo('');\n }).finally(() => {\n this.setState({loadSpinnerVisible: false});\n });\n let urlHash = HashNavigation.setUrlSidePanel(window.location.hash, 'contacts');\n if (goToTopic) {\n urlHash = HashNavigation.setUrlTopic(urlHash, goToTopic);\n }\n HashNavigation.navigateTo(urlHash);\n }\n\n tnMeMetaDesc(desc) {\n if (desc) {\n if (desc.public) {\n this.setState({\n sidePanelTitle: desc.public.fn,\n sidePanelAvatar: makeImageDataUrl(desc.public.photo)\n });\n }\n if (desc.trusted) {\n const badges = [];\n for (const [key, val] of Object.entries(desc.trusted)) {\n if (val) {\n badges.push(key);\n }\n }\n this.setState({\n myTrustedBadges: badges,\n });\n }\n if (desc.acs) {\n this.setState({\n incognitoMode: !desc.acs.isPresencer()\n });\n }\n }\n }\n\n // Reactions to updates to the contact list.\n tnMeContactUpdate(what, cont) {\n if (what == 'on' || what == 'off') {\n this.resetContactList();\n if (this.state.topicSelected == cont.topic) {\n this.setState({topicSelectedOnline: (what == 'on')});\n }\n } else if (what == 'read') {\n this.resetContactList();\n } else if (what == 'msg') {\n // Check if the topic is archived. If so, don't play a sound.\n const topic = this.tinode.getTopic(cont.topic);\n const archived = topic && topic.isArchived();\n\n // New message received. Maybe the message is from the current user, then unread is 0.\n if (cont.unread > 0 && this.state.messageSounds && !archived) {\n // Skip update if the topic is currently open, otherwise the badge will annoyingly flash.\n if (document.hidden || this.state.topicSelected != cont.topic) {\n POP_SOUND.play();\n }\n }\n // Reorder contact list to use possibly updated 'touched'.\n this.resetContactList();\n } else if (what == 'recv') {\n // 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 }\n // Redraw without the deleted topic.\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({topicSelectedAcs: cont.acs});\n }\n } else if (what == 'del') {\n // TODO: messages deleted (hard or soft) -- update pill counter.\n } else if (what == 'upd') {\n // upd - handled by the SDK. Explicitly ignoring here.\n } else {\n // TODO(gene): handle other types of notifications:\n // * ua -- user agent changes (maybe display a pictogram for mobile/desktop).\n console.log(\"Unsupported (yet) presence update:\" + what + \" in: \" + cont.topic);\n }\n }\n\n tnMeSubsUpdated(unused) {\n this.resetContactList();\n }\n\n // Merge search results and contact list to create a single flat\n // list of known contacts for GroupManager to use.\n static prepareSearchableContacts(chatList, foundContacts) {\n const merged = {};\n\n // For chatList topics merge only p2p topics and convert them to the\n // same format as foundContacts.\n for (const c of chatList) {\n if (Tinode.isP2PTopicName(c.topic)) {\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 }\n\n // Add all foundCountacts if they have not been added already.\n for (const c of foundContacts) {\n if (!merged[c.user]) {\n merged[c.user] = c;\n }\n }\n\n return Object.values(merged);\n }\n\n resetContactList() {\n const newState = {\n chatList: []\n };\n\n if (!this.state.ready) {\n newState.ready = true;\n }\n\n this.tinode.getMeTopic().contacts((c) => {\n if (!c.topic && !c.user) {\n // Contacts expect c.topic to be set.\n c.topic = c.name;\n }\n\n newState.chatList.push(c);\n if (this.state.topicSelected == c.topic) {\n newState.topicSelectedOnline = c.online;\n newState.topicSelectedAcs = c.acs;\n }\n });\n // Merge search results and chat list.\n newState.searchableContacts = TinodeWeb.prepareSearchableContacts(newState.chatList, this.state.searchResults);\n this.setState(newState);\n }\n\n // Sending \"received\" notifications\n tnData(data) {\n const topic = this.tinode.getTopic(data.topic);\n if (topic.msgStatus(data, true) >= Tinode.MESSAGE_STATUS_SENT && data.from != this.state.myUserId) {\n clearTimeout(this.receivedTimer);\n this.receivedTimer = setTimeout(() => {\n this.receivedTimer = undefined;\n topic.noteRecv(data.seq);\n }, RECEIVED_DELAY);\n }\n }\n\n /* Fnd topic: find contacts by tokens */\n tnInitFind() {\n const fnd = this.tinode.getFndTopic();\n fnd.onSubsUpdated = this.tnFndSubsUpdated;\n if (fnd.isSubscribed()) {\n this.tnFndSubsUpdated();\n } else {\n fnd.subscribe(fnd.startMetaQuery().withSub().build()).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n tnFndSubsUpdated() {\n const foundContacts = [];\n // Don't attempt to create P2P topics which already exist. Server will reject the duplicates.\n this.tinode.getFndTopic().contacts((s) => {\n foundContacts.push(s);\n });\n this.setState({\n searchResults: foundContacts,\n searchableContacts: TinodeWeb.prepareSearchableContacts(this.state.chatList, foundContacts)\n });\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 handleSearchContacts(query) {\n const fnd = this.tinode.getFndTopic();\n fnd.setMeta({desc: {public: query}}).then((ctrl) => {\n return fnd.getMeta(fnd.startMetaQuery().withSub().build());\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n // User clicked on a contact in the side panel or deleted a contact.\n handleTopicSelected(topicName) {\n // Clear newTopicParams after use.\n if (this.state.newTopicParams && this.state.newTopicParams._topicName != topicName) {\n this.setState({\n newTopicParams: null\n });\n }\n\n if (topicName) {\n this.setState({\n errorText: '',\n errorLevel: null,\n mobilePanel: 'topic-view',\n showInfoPanel: false\n });\n // Different contact selected.\n if (this.state.topicSelected != topicName) {\n this.setState({\n topicSelectedOnline: this.tinode.isTopicOnline(topicName),\n topicSelectedAcs: this.tinode.getTopicAccessMode(topicName)\n });\n HashNavigation.navigateTo(HashNavigation.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 HashNavigation.navigateTo(HashNavigation.setUrlTopic('', null));\n }\n }\n\n // In mobile view user requested to show sidepanel\n handleHideMessagesView() {\n this.setState({\n mobilePanel: 'sidepanel'\n });\n HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, null));\n }\n\n // User is sending a message, either plain text or a drafty object, possibly\n // with attachments.\n // - msg - Drafty message with content\n // - promise - Promise to be resolved when the upload is completed\n // - uploader - for tracking progress\n handleSendMessage(msg, promise, uploader) {\n const topic = this.tinode.getTopic(this.state.topicSelected);\n\n msg = topic.createMessage(msg, false);\n // The uploader is used to show progress.\n msg._uploader = uploader;\n\n if (!topic.isSubscribed()) {\n if (!promise) {\n promise = Promise.resolve();\n }\n promise = promise.then(() => { return topic.subscribe(); });\n }\n\n if (promise) {\n promise = promise.catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n topic.publishDraft(msg, promise)\n .then((ctrl) => {\n if (topic.isArchived()) {\n return topic.archive(false);\n }\n })\n .catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleNewChatInvitation(topicName, action) {\n const topic = this.tinode.getTopic(topicName);\n let response = null;\n switch (action) {\n case 'accept':\n // Accept given permissions.\n const mode = topic.getAccessMode().getGiven();\n response = topic.setMeta({sub: {mode: mode}});\n if (topic.isP2PType()) {\n // For P2P topics change 'given' permission of the peer too.\n // In p2p topics the other user has the same name as the topic.\n response = response.then((ctrl) => {\n topic.setMeta({sub: {user: topicName, mode: mode}});\n });\n }\n break;\n case 'delete':\n // Ignore invitation by deleting it.\n response = topic.delTopic(true);\n break;\n case 'block':\n // Ban the topic making futher invites impossible.\n // Just self-ban.\n const am = topic.getAccessMode().updateWant('-JP').getWant();\n response = topic.setMeta({sub: {mode: am}}).then((ctrl) => {\n return this.handleTopicSelected(null);\n });\n break;\n default:\n console.log(\"Unknown invitation action\", '\"' + action + '\"\"');\n }\n\n if (response != null) {\n response.catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n // User chose a Sign Up menu item.\n handleNewAccount() {\n this.handleError();\n\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, 'register'));\n }\n\n // Actual registration of a new account.\n handleNewAccountRequest(login_, password_, public_, cred_, tags_) {\n // Clear old error, if any.\n this.handleError();\n\n this.tinode.connect(this.state.serverAddress)\n .then(() => {\n return this.tinode.createAccountBasic(login_, password_,\n {public: public_, tags: tags_, cred: Tinode.credential(cred_)});\n }).then((ctrl) => {\n if (ctrl.code >= 300 && ctrl.text == 'validate credentials') {\n this.handleCredentialsRequest(ctrl.params);\n } else {\n this.handleLoginSuccessful(this);\n }\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleUpdateAccountRequest(password, pub, defacs) {\n this.handleError();\n\n if (pub || defacs) {\n const params = {};\n if (pub) {\n params.public = pub;\n }\n if (defacs) {\n params.defacs = defacs;\n }\n this.tinode.getMeTopic().setMeta({desc: params}).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n if (password) {\n this.tinode.updateAccountBasic(null, this.tinode.getCurrentLogin(), password).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n handleToggleIncognitoMode(on) {\n const me = this.tinode.getMeTopic();\n const am = me.getAccessMode().updateWant(on ? '-P' : '+P').getWant();\n me.setMeta({sub: {mode: am}}).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleUpdateAccountTagsRequest(tags) {\n this.tinode.getMeTopic().setMeta({tags: tags})\n .catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n // User chose Settings menu item.\n handleSettings() {\n this.handleError();\n\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash,\n this.state.myUserId ? 'edit' : 'settings'));\n }\n\n // User updated global parameters.\n handleGlobalSettings(settings) {\n const serverAddress = settings.serverAddress || this.state.serverAddress;\n const transport = settings.transport || this.state.transport;\n if (this.tinode) {\n this.tinode.clearStorage();\n this.tinode.onDisconnect = undefined;\n this.tinode.disconnect();\n }\n this.tinode = TinodeWeb.tnSetup(serverAddress, transport, this.props.intl.locale,\n LocalStorageUtil.getObject('keep-logged-in'));\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\n this.tinode.onAutoreconnectIteration = this.handleAutoreconnectIteration;\n\n this.setState({\n serverAddress: serverAddress,\n transport: transport\n });\n LocalStorageUtil.setObject('settings', {\n serverAddress: serverAddress,\n transport: transport\n });\n\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, ''));\n }\n\n // User chose 'Archived chats'.\n handleShowArchive() {\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash,\n this.state.myUserId ? 'archive' : ''));\n }\n\n // User viewes 'Blocked chats'.\n handleShowBlocked() {\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash,\n this.state.myUserId ? 'blocked' : ''));\n }\n\n togglePushToken(enabled) {\n if (enabled) {\n if (!this.state.firebaseToken) {\n const fcm = this.fbPush ?\n Promise.resolve() :\n this.initFCMessaging();\n fcm.then(() => {\n return this.fbPush.requestPermission();\n }).then(() => {\n this.requestPushToken();\n }).catch((err) => {\n console.log(\"Failed to get notification permission.\", err);\n this.handleError(err.message, 'err');\n this.setState({desktopAlerts: false, firebaseToken: null});\n LocalStorageUtil.updateObject('settings', {desktopAlerts: false});\n });\n } else {\n this.setState({desktopAlerts: true});\n if (LocalStorageUtil.getObject('keep-logged-in')) {\n LocalStorageUtil.updateObject('settings', {desktopAlerts: true});\n }\n }\n } else if (this.state.firebaseToken && this.fbPush) {\n this.fbPush.deleteToken(this.state.firebaseToken).catch((err) => {\n console.log(\"Unable to delete token.\", err);\n }).finally(() => {\n LocalStorageUtil.updateObject('settings', {desktopAlerts: false});\n localStorage.removeItem('firebase-token');\n this.setState({desktopAlerts: false, firebaseToken: null});\n // Inform the server that the token was deleted.\n this.tinode.setDeviceToken(null);\n });\n } else {\n this.setState({desktopAlerts: false, firebaseToken: null});\n LocalStorageUtil.updateObject('settings', {desktopAlerts: false});\n }\n }\n\n requestPushToken() {\n this.fbPush.getToken().then((refreshedToken) => {\n const persist = LocalStorageUtil.getObject('keep-logged-in');\n if (refreshedToken != this.state.firebaseToken) {\n this.tinode.setDeviceToken(refreshedToken);\n if (persist) {\n LocalStorageUtil.setObject('firebase-token', refreshedToken);\n }\n }\n this.setState({firebaseToken: refreshedToken, desktopAlerts: true});\n if (persist) {\n LocalStorageUtil.updateObject('settings', {desktopAlerts: true});\n }\n }).catch((err) => {\n this.handleError(err.message, 'err');\n console.log(\"Failed to retrieve firebase token\", err);\n });\n }\n\n handleToggleMessageSounds(enabled) {\n this.setState({messageSounds: enabled});\n LocalStorageUtil.updateObject('settings', {\n messageSoundsOff: !enabled\n });\n }\n\n handleCredAdd(method, value) {\n const me = this.tinode.getMeTopic();\n me.setMeta({cred: {meth: method, val: value}}).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleCredDelete(method, value) {\n const me = this.tinode.getMeTopic();\n me.delCredential(method, value).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleCredConfirm(method, response) {\n this.handleCredentialsRequest({cred: [method]});\n }\n\n // User clicked Cancel button in Setting or Sign Up panel.\n handleSidepanelCancel() {\n const parsed = HashNavigation.parseUrlHash(window.location.hash);\n let path = '';\n if (['security','support','general','notif'].includes(parsed.path[0])) {\n path = 'edit';\n } else if ('blocked' == parsed.path[0]) {\n path = 'security';\n } else if ('crop' == parsed.path[0]) {\n path = 'general';\n } else if (this.state.myUserId) {\n path = 'contacts';\n }\n parsed.path[0] = path;\n if (parsed.params) {\n delete parsed.params.code;\n delete parsed.params.method;\n delete parsed.params.tab;\n }\n HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsed.path, parsed.params));\n this.setState({errorText: '', errorLevel: null});\n }\n\n // Basic nagigator by hash value. No need to bind to this.\n basicNavigator(hash) {\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, hash));\n }\n\n // Request to start a topic, new or selected from search results, or \"by ID\".\n handleStartTopicRequest(topicName, pub, priv, tags, isChannel) {\n // Check if topic is indeed new. If not, launch it.\n if (topicName && this.tinode.isTopicCached(topicName)) {\n this.handleTopicSelected(topicName);\n return;\n }\n\n const params = {};\n if (Tinode.isP2PTopicName(topicName)) {\n // Because we are initialing the subscription, set 'want' to all permissions.\n params.sub = {mode: DEFAULT_P2P_ACCESS_MODE};\n // Give the other user all permissions too.\n params.desc = {defacs: {auth: DEFAULT_P2P_ACCESS_MODE}};\n } else {\n topicName = topicName || this.tinode.newGroupTopicName(isChannel);\n params.desc = {public: pub, private: {comment: priv}};\n params.tags = tags;\n }\n params._topicName = topicName;\n this.setState({newTopicParams: params}, () => {this.handleTopicSelected(topicName)});\n }\n\n // New topic was creted, here is the new topic name.\n 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({topicSelected: newName}, () => {\n HashNavigation.navigateTo(HashNavigation.setUrlTopic('', newName));\n });\n }\n }\n\n handleTopicUpdateRequest(topicName, pub, priv, permissions) {\n const topic = this.tinode.getTopic(topicName);\n if (topic) {\n const params = {};\n if (pub) {\n params.public = pub;\n }\n if (priv) {\n params.private = (priv === Tinode.DEL_CHAR) ?\n Tinode.DEL_CHAR : {comment: priv};\n }\n if (permissions) {\n params.defacs = permissions;\n }\n topic.setMeta({desc: params}).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n handleChangePermissions(topicName, mode, uid) {\n const topic = this.tinode.getTopic(topicName);\n if (topic) {\n const am = topic.getAccessMode();\n if (uid) {\n am.updateGiven(mode);\n mode = am.getGiven();\n } else {\n am.updateWant(mode);\n mode = am.getWant();\n }\n topic.setMeta({sub: {user: uid, mode: mode}}).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n handleTagsUpdated(topicName, tags) {\n const topic = this.tinode.getTopic(topicName);\n if (topic) {\n topic.setMeta({tags: tags}).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n handleLogout() {\n updateFavicon(0);\n\n // Remove stored data.\n localStorage.removeItem('auth-token');\n localStorage.removeItem('firebase-token');\n localStorage.removeItem('settings');\n if (this.state.firebaseToken) {\n this.fbPush.deleteToken(this.state.firebaseToken)\n }\n\n clearInterval(this.reconnectCountdown);\n\n if (this.tinode) {\n this.tinode.clearStorage();\n this.tinode.onDisconnect = undefined;\n this.tinode.disconnect();\n }\n this.setState(this.getBlankState());\n\n this.tinode = TinodeWeb.tnSetup(this.state.serverAddress,\n this.state.transport, this.props.intl.locale, LocalStorageUtil.getObject('keep-logged-in'));\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\n this.tinode.onAutoreconnectIteration = this.handleAutoreconnectIteration;\n HashNavigation.navigateTo('');\n }\n\n handleDeleteAccount() {\n this.tinode.delCurrentUser(true).then((ctrl) => {\n this.handleLogout();\n });\n }\n\n handleDeleteMessagesRequest(topicName) {\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n // Request hard-delete all messages.\n topic.delMessagesAll(true).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleLeaveUnsubRequest(topicName) {\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n topic.leave(true).then((ctrl) => {\n // Hide MessagesView and InfoView panels.\n HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, ''));\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleBlockTopicRequest(topicName) {\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n topic.updateMode(null, '-JP').then((ctrl) => {\n // Hide MessagesView and InfoView panels.\n HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, ''));\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleReportTopic(topicName) {\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n // Publish spam report.\n this.tinode.publish(Tinode.TOPIC_SYS, Tinode.Drafty.attachJSON(null, {\n 'action': 'report',\n 'target': topicName\n }));\n\n // Remove J and P permissions.\n topic.updateMode(null, '-JP').then((ctrl) => {\n // Hide MessagesView and InfoView panels.\n HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, ''));\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleShowContextMenu(params, menuItems) {\n this.setState({\n contextMenuVisible: true,\n contextMenuClickAt: {x: params.x, y: params.y},\n contextMenuParams: params,\n contextMenuItems: menuItems || this.defaultTopicContextMenu(params.topicName),\n contextMenuBounds: ReactDOM.findDOMNode(this).getBoundingClientRect()\n });\n }\n\n defaultTopicContextMenu(topicName) {\n const topic = this.tinode.getTopic(topicName);\n\n let muted = false, blocked = false, self_blocked = false, subscribed = false, deleter = false, archived = false;\n if (topic) {\n subscribed = topic.isSubscribed();\n archived = topic.isArchived();\n\n const acs = topic.getAccessMode();\n if (acs) {\n muted = acs.isMuted();\n blocked = !acs.isJoiner();\n self_blocked = !acs.isJoiner('want');\n deleter = acs.isDeleter();\n }\n }\n\n return [\n subscribed ? {\n title: this.props.intl.formatMessage(messages.menu_item_info),\n handler: this.handleShowInfoView\n } : null,\n subscribed ? 'messages_clear' : null,\n subscribed && deleter ? 'messages_clear_hard' : null,\n muted ? (blocked ? null : 'topic_unmute') : 'topic_mute',\n self_blocked ? 'topic_unblock' : 'topic_block',\n !archived ? 'topic_restore' : 'topic_archive',\n 'topic_delete'\n ];\n }\n\n handleHideContextMenu() {\n this.setState({\n contextMenuVisible: false,\n contextMenuClickAt: null,\n contextMenuParams: null,\n contextMenuBounds: null\n });\n }\n\n handleContextMenuAction(action, promise, params) {\n if (action == 'topic_archive') {\n if (promise && params.topicName && params.topicName == this.state.topicSelected) {\n promise.then(() => {\n this.handleTopicSelected(null);\n });\n }\n }\n }\n\n handleShowAlert(title, content, onConfirm, confirmText, onReject, rejectText) {\n this.setState({\n alertVisible: true,\n alertParams: {\n title: title,\n content: content,\n onConfirm: onConfirm,\n confirm: confirmText,\n onReject: onReject,\n reject: rejectText\n }\n });\n }\n\n handleShowInfoView() {\n HashNavigation.navigateTo(HashNavigation.addUrlParam(window.location.hash, 'info', true));\n this.setState({showInfoPanel: true});\n }\n\n handleHideInfoView() {\n HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'info'));\n this.setState({showInfoPanel: false});\n }\n\n handleMemberUpdateRequest(topicName, added, removed) {\n if (!topicName) {\n return;\n }\n\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n if (added && added.length > 0) {\n added.map((uid) => {\n topic.invite(uid, null).catch((err) => {\n this.handleError(err.message, 'err');\n });\n });\n }\n\n if (removed && removed.length > 0) {\n removed.map((uid) => {\n topic.delSubscription(uid).catch((err) => {\n this.handleError(err.message, 'err');\n });\n });\n }\n }\n\n handleValidateCredentialsRequest(cred, code) {\n if (this.tinode.isAuthenticated()) {\n const me = this.tinode.getMeTopic();\n me.setMeta({cred: {meth: cred, resp: code}})\n .then(() => {\n HashNavigation.navigateTo('');\n })\n .catch((err) => {\n this.handleError(err.message, 'err');\n });\n } else {\n this.setState({credMethod: cred, credCode: code});\n this.doLogin(null, null, {meth: cred, resp: code});\n }\n }\n\n handlePasswordResetRequest(method, value) {\n // If already connected, connnect() will return a resolved promise.\n this.tinode.connect()\n .then(() => {\n return this.tinode.requestResetAuthSecret('basic', method, value);\n })\n .catch((err) => {\n // Socket error\n this.handleError(err.message, 'err');\n });\n }\n\n handleResetPassword(scheme, newPassword, token) {\n token = base64ReEncode(token);\n if (!token) {\n this.handleError(this.props.intl.formatMessage(messages.invalid_security_token), 'err');\n } else {\n this.tinode.connect()\n .then(() => {\n return this.tinode.updateAccountBasic(null, null, newPassword, {token: token});\n })\n .catch((err) => {\n // Socket error\n this.handleError(err.message, 'err');\n });\n }\n }\n\n render() {\n return (\n \n {this.state.contextMenuVisible ?\n
{\n if (topicName == this.state.topicSelected) {\n this.handleTopicSelected(null);\n }\n }}\n onError={this.handleError} />\n :\n null\n }\n { this.setState({alertVisible: false}); }) : null}\n reject={this.state.alertParams.reject}\n onConfirm={() => { this.setState({alertVisible: false}); this.state.alertParams.onConfirm(); }}\n confirm={this.state.alertParams.confirm}\n />\n \n\n \n\n {this.state.showInfoPanel ?\n \n :\n null\n }\n \n );\n }\n};\n\nexport default injectIntl(TinodeWeb);\n","// Put all packages together.\n// Used to generate umd/index.prod.js\n\nimport React from 'react';\nimport ReactDOM from 'react-dom';\n\nimport { IntlProvider } from 'react-intl';\n\nimport allMessages from './messages.json';\nimport TinodeWeb from './views/tinode-web.jsx';\nimport HashNavigation from './lib/navigation.js';\n\n// Detect human language to use in the UI:\n// Check parameters from URL hash #?hl=ru, then browser, then use 'en' as a fallback.\nconst { params } = HashNavigation.parseUrlHash(window.location.hash);\nconst language = (params && params.hl) ||\n (navigator.languages && navigator.languages[0]) ||\n navigator.language ||\n navigator.userLanguage ||\n 'en';\n\n// Get the base language 'en' from a more specific 'en_GB' or 'en-US' as a partial fallback.\nconst baseLanguage = language.toLowerCase().split(/[-_]/)[0];\n\n// Try the full locale first, then the locale without the region code, fallback to 'en'.\nconst messages =\n allMessages[language] ||\n allMessages[baseLanguage] ||\n allMessages.en;\n\nReactDOM.render(\n \n \n \n \n ,\n document.getElementById('mountPoint')\n);\n","// The require scope\nvar __webpack_require__ = {};\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// startup\n// Load entry module and return exports\n// This entry module doesn't tell about it's top-level declarations so it can't be inlined\nvar __webpack_exports__ = {};\n__webpack_modules__[163](0, __webpack_exports__, __webpack_require__);\n"],"sourceRoot":""}
\ No newline at end of file
+{"version":3,"sources":["webpack://tinode-webapp/external \"React\"","webpack://tinode-webapp/external \"ReactDOM\"","webpack://tinode-webapp/external \"ReactIntl\"","webpack://tinode-webapp/external \"firebase\"","webpack://tinode-webapp/external \"Tinode\"","webpack://tinode-webapp/external [\"firebase\",\"messaging\"]","webpack://tinode-webapp/./src/widgets/alert.jsx","webpack://tinode-webapp/./src/version.js","webpack://tinode-webapp/./src/config.js","webpack://tinode-webapp/./src/widgets/context-menu.jsx","webpack://tinode-webapp/./src/lib/strformat.js","webpack://tinode-webapp/./src/widgets/letter-tile.jsx","webpack://tinode-webapp/./src/widgets/load-spinner.jsx","webpack://tinode-webapp/./src/lib/blob-helpers.js","webpack://tinode-webapp/./src/widgets/avatar-upload.jsx","webpack://tinode-webapp/./src/widgets/badge-list.jsx","webpack://tinode-webapp/./src/widgets/checkbox.jsx","webpack://tinode-webapp/./src/widgets/contact-badges.jsx","webpack://tinode-webapp/./src/lib/utils.js","webpack://tinode-webapp/./src/widgets/received-marker.jsx","webpack://tinode-webapp/./src/widgets/unread-badge.jsx","webpack://tinode-webapp/./src/widgets/contact.jsx","webpack://tinode-webapp/./src/widgets/contact-action.jsx","webpack://tinode-webapp/./src/widgets/contact-list.jsx","webpack://tinode-webapp/./src/widgets/menu-cancel.jsx","webpack://tinode-webapp/./src/widgets/error-panel.jsx","webpack://tinode-webapp/./src/widgets/chip.jsx","webpack://tinode-webapp/./src/widgets/chip-input.jsx","webpack://tinode-webapp/./src/widgets/group-manager.jsx","webpack://tinode-webapp/./src/widgets/visible-password.jsx","webpack://tinode-webapp/./src/widgets/in-place-edit.jsx","webpack://tinode-webapp/./src/widgets/more-button.jsx","webpack://tinode-webapp/./src/widgets/permissions-editor.jsx","webpack://tinode-webapp/./src/widgets/tag-manager.jsx","webpack://tinode-webapp/./src/views/info-view.jsx","webpack://tinode-webapp/./src/widgets/file-progress.jsx","webpack://tinode-webapp/./src/widgets/attachment.jsx","webpack://tinode-webapp/./src/widgets/uploading-image.jsx","webpack://tinode-webapp/./src/widgets/chat-message.jsx","webpack://tinode-webapp/./src/widgets/send-message.jsx","webpack://tinode-webapp/./src/widgets/doc-preview.jsx","webpack://tinode-webapp/./src/widgets/group-subs.jsx","webpack://tinode-webapp/./src/widgets/image-preview.jsx","webpack://tinode-webapp/./src/widgets/invitation.jsx","webpack://tinode-webapp/./src/views/logo-view.jsx","webpack://tinode-webapp/./src/lib/navigation.js","webpack://tinode-webapp/./src/views/messages-view.jsx","webpack://tinode-webapp/./src/widgets/button-back.jsx","webpack://tinode-webapp/./src/widgets/menu-contacts.jsx","webpack://tinode-webapp/./src/widgets/menu-start.jsx","webpack://tinode-webapp/./src/widgets/side-navbar.jsx","webpack://tinode-webapp/./src/widgets/cropper.jsx","webpack://tinode-webapp/./src/views/avatar-crop-view.jsx","webpack://tinode-webapp/./src/views/contacts-view.jsx","webpack://tinode-webapp/./src/lib/local-storage.js","webpack://tinode-webapp/./src/views/create-account-view.jsx","webpack://tinode-webapp/./src/views/edit-account-view.jsx","webpack://tinode-webapp/./src/views/acc-general-view.jsx","webpack://tinode-webapp/./src/views/acc-notifications-view.jsx","webpack://tinode-webapp/./src/views/acc-security-view.jsx","webpack://tinode-webapp/./src/views/acc-support-view.jsx","webpack://tinode-webapp/./src/views/login-view.jsx","webpack://tinode-webapp/./src/widgets/new-topic-by-id.jsx","webpack://tinode-webapp/./src/widgets/new-topic-group.jsx","webpack://tinode-webapp/./src/widgets/search-contacts.jsx","webpack://tinode-webapp/./src/views/new-topic-view.jsx","webpack://tinode-webapp/./src/views/password-reset-view.jsx","webpack://tinode-webapp/./src/widgets/host-selector.jsx","webpack://tinode-webapp/./src/views/settings-view.jsx","webpack://tinode-webapp/./src/views/validation-view.jsx","webpack://tinode-webapp/./src/views/sidepanel-view.jsx","webpack://tinode-webapp/./src/lib/host-name.js","webpack://tinode-webapp/./src/views/tinode-web.jsx","webpack://tinode-webapp/./src/index.js","webpack://tinode-webapp/webpack/bootstrap","webpack://tinode-webapp/webpack/runtime/compat get default export","webpack://tinode-webapp/webpack/runtime/define property getters","webpack://tinode-webapp/webpack/runtime/hasOwnProperty shorthand","webpack://tinode-webapp/webpack/startup"],"names":["React","ReactDOM","ReactIntl","firebase","messaging","Tinode","Alert","render","this","props","visible","className","title","content","onReject","onClick","reject","FormattedMessage","id","defaultMessage","onConfirm","confirm","PACKAGE_VERSION","APP_NAME","KNOWN_HOSTS","hosted","local","DEFAULT_HOST","KEYPRESS_DELAY","DEFAULT_P2P_ACCESS_MODE","REM_SIZE","AVATAR_SIZE","MAX_INBAND_ATTACHMENT_SIZE","MAX_EXTERN_ATTACHMENT_SIZE","MAX_IMAGE_DIM","MAX_TITLE_LENGTH","messages","defineMessages","info","clear_messages","clear_for_all","delete","delete_for_all","send_retry","mute","unmute","topic_delete","topic_delete_warning","delete_messages_warning","unblock","block","topic_block_warning","member_delete","archive","unarchive","edit_permissions","clear_messages_warning","ContextMenu","constructor","super","formatMessage","intl","handlePageClick","bind","handleEscapeKey","handleClick","MenuItems","handler","params","errorHandler","onShowAlert","deleteMessages","message","retryMessage","topicPermissionSetter","then","ctrl","onTopicRemoved","topicName","topic","tinode","getTopic","delTopic","catch","err","console","log","user","delSubscription","componentDidMount","document","addEventListener","componentWillUnmount","removeEventListener","e","contains","target","preventDefault","stopPropagation","hide","keyCode","item","items","currentTarget","dataset","onAction","onError","all","hard","cancelSend","seq","delMessagesAll","delMessagesList","flushMessage","msg","createMessage","publishDraft","mode","result","updateMode","count","menu","map","push","key","vSize","length","position","left","bounds","right","clickAt","x","top","bottom","y","style","injectIntl","shortDateFormat","locale","window","navigator","userLanguage","language","now","Date","getFullYear","getMonth","getDate","toLocaleTimeString","hour12","hour","minute","toLocaleDateString","month","day","year","bytesToHumanSize","bytes","sizes","bucket","Math","min","floor","log2","pow","round","toFixed","LetterTile","avatar","isGroup","iconColor","abs","value","hash","i","charCodeAt","stringHash","trim","letter","charAt","alt","src","onerror","LoadSpinner","large","clear","centered","show","SUPPORTED_IMAGE_FORMATS","MIME_EXTENSIONS","makeImageDataUrl","photo","data","type","startsWith","ref","fitImageSize","width","height","maxWidth","maxHeight","forceSquare","scale","size","dstWidth","dstHeight","srcWidth","srcHeight","xoffset","yoffset","imageScaled","fileOrBlob","maxSize","onSuccess","img","Image","crossOrigin","onload","async","URL","revokeObjectURL","dim","canvas","createElement","ctx","getContext","imageSmoothingEnabled","drawImage","mime","includes","blob","Promise","resolve","toBlob","clearRect","fname","idx","indexOf","ext","at","lastIndexOf","substring","fileNameForMime","name","createObjectURL","fileToBase64","file","reader","FileReader","split","readAsDataURL","blobToBase64","AvatarUpload","state","dataUrl","uploading","handleFileUpload","componentDidUpdate","prevProps","setState","image","files","onImageReceived","onFileReceived","uploader","getLargeFileHelper","cannot_initiate_upload","upload","url","onImageChanged","finally","unused","base64bits","du","randId","random","substr","readOnly","href","uid","accept","onChange","htmlFor","icon_mapping","badge_verified","badge_staff","badge_danger","BadgeList","badges","trustedBadges","b","comment","CheckBox","handleChange","checked","ContactBadges","color","icon","updateFavicon","oldIcon","getElementById","head","getElementsByTagName","newIcon","rel","removeChild","appendChild","theCard","fn","imageUrl","imageMimeType","card","mimeType","matches","exec","arrayEqual","a","Array","isArray","sort","l","sanitizeUrl","allowedSchemes","replace","test","schemes","join","RegExp","deliveryMarker","received","message_sending","message_sending_failed","ReceivedMarker","timestamp","marker","UnreadBadge","drafty_image","drafty_form","drafty_attachment","Contact","handleContextClick","onSelected","index","showContextMenu","pageY","pageX","online","slice","icon_badges","isVerified","isStaff","isDangerous","acs","showMode","getMode","isMuted","isJoiner","subtitle","preview","Drafty","draftyFormatter","showCheckmark","selected","showOnline","isChannel","unread","values","el","attr","concat","ContactAction","action","badge_you","badge_owner","ContactList","topicSelected","contactNodes","contactsCount","contacts","c","filterFunc","filter","filterOn","private","toLowerCase","public","myUserId","isOwner","deliveryStatus","latestMessage","_status","msgStatus","showUnread","connected","onTopicSelected","trusted","verified","staff","danger","noScroll","dangerouslySetInnerHTML","__html","emptyListMessage","MenuCancel","onCancel","ErrorPanel","level","onClearError","warn","text","whiteSpace","actionText","Chip","handleCancel","invalid","noAvatar","required","ChipInput","deriveStateFromProps","input","focused","handleTextInput","removeChipAt","handleChipCancel","handleFocusGained","handleFocusLost","handleKeyDown","placeholder","chips","prompt","sortedChips","sortChips","staticMembers","chipIndex","indexChips","prevState","keep","normal","removed","onChipRemoved","onFocusLost","onEnter","undefined","avatarDisabled","onFocus","onBlur","onKeyDown","autoFocus","no_contacts","contacts_not_found_short","GroupManager","members","indexMembers","keepInitialMembers","requiredMember","contactFilter","noContactsMessage","selectedContacts","handleContactSelected","handleMemberRemoved","handleContactFilter","handleSubmit","m","delta","present","keepInitial","stat","sel","userId","status","splice","val","query","instance","added","Object","keys","k","onSubmit","doContactFiltering","VisiblePassword","handleVisibility","handeTextChange","handleEditingFinished","onFinished","setTimeout","activeElement","tabIndex","autoComplete","InPlaceEdit","active","initialValue","handleStartEditing","handlePasswordFinished","newValue","focus","event","checkValidity","fieldType","spanText","spanClass","MoreButton","open","handleToggle","onToggle","joiner","writer","preser","approver","sharer","deleter","owner","PermissionsEditor","names","skip","compare","userTitle","userAvatar","modeTitle","compareTitle","TagManager","tags","tagInput","activated","handleTagInput","handleAddTag","handleRemoveTag","nextProps","last","tag","maxTagCount","getServerLimit","onTagsChanged","minTagLength","maxTagLength","add_tags_prompt","requested","granted","other_user","delete_messages","leave_chat","leave_chat_warning","block_contact","block_contact_warning","report_chat","report_chat_warning","InfoView","admin","muted","address","groupTopic","channel","fullName","selectedContact","access","modeGiven","modeWant","modeGiven2","modeWant2","auth","anon","contactList","showMemberPanel","showPermissionEditorFor","moreInfoExpanded","previousMetaDesc","previousSubsUpdated","previousTagsUpdated","resetSubs","resetDesc","onMetaDesc","onSubsUpdated","onTagsUpdated","handleFullNameUpdate","handlePrivateUpdate","handleImageChanged","handleMuted","handlePermissionsChanged","handleLaunchPermissionsEditor","handleHidePermissionsEditor","handleShowAddMembers","handleHideAddMembers","handleMemberUpdateRequest","handleDeleteMessages","handleLeave","handleBlock","handleReport","handleMemberSelected","handleMoreInfo","handleTagsUpdated","handleContextMenu","getType","newState","user2","subscriber","getGiven","getWant","subscribers","sub","defacs","getDefaultAccess","getAccessMode","entries","isAdmin","isSharer","isDeleter","isGroupType","isChannelType","getMeta","startMetaQuery","withTags","build","desc","subs","onTopicDescUpdate","ignored","onChangePermissions","perm","userPermissionsEdited","which","toEdit","toCompare","toSkip","titleEdit","titleCompare","userPermissionsTitle","userPermissionsAvatar","editedPermissions","immutablePermissions","editedPermissionsTitle","immutablePermissionsTitle","editedPermissionsSkipped","onInitFind","onMemberUpdateRequest","onDeleteMessages","onLeaveTopic","onBlockTopic","onReportTopic","onTopicTagsUpdate","menuItems","displayMobile","errorLevel","errorText","searchableContacts","private_placeholder","more","no_members","FileProgress","progress","Attachment","downloader","downloadFile","filename","mimetype","download","loaded","onCancelUpload","cancel","helperFunc","isUrlRelative","downloadUrl","downloadWidget","UploadingImage","ChatMessage","onProgress","handleProgress","handleImagePreview","handleFormButtonClick","handleCancelUpload","onImagePreview","resp","act","onFormResponse","MESSAGE_STATUS_FAILED","ratio","sideClass","deleted","sequence","response","bubbleClass","fullDisplay","userFrom","attachments","att","userName","notFound","viewportWidth","minWidth","minHeight","authorizeURL","sanitizeImageUrl","loading","inner","child","messaging_disabled","type_new_message","add_image_caption","file_attachment_too_large","SendMessage","keypressTimestamp","getTime","handlePasteEvent","handleAttachImage","handleAttachFile","handleSend","handleKeyPress","handleMessageTyping","messageEditArea","disabled","onImageSuccess","onAttachmentSuccess","clipboardData","originalEvent","kind","getAsFile","imageFileScaledToBase64","imageFileToBase64","filePasted","bits","onAttachImage","onAttachFile","acceptBlank","noInput","onSendMessage","shiftKey","onKeyPress","messagePrompt","attachImage","click","attachFile","display","DocPreview","handleSendDoc","caption","onClose","mimeToIcon","default","video","iconFromMime","GroupSubs","usersOnline","totalCount","countToShow","some","overflow","ImagePreview","handleSendImage","assignWidth","node","getBoundingClientRect","maxlength","max","Invitation","handleButtonAction","evt","LogoView","version","serverVersion","serverAddress","HashNavigation","parts","path","forEach","part","decodeURIComponent","location","args","hasOwnProperty","parsed","parseUrlHash","composeUrlHash","sidepanel","online_now","last_seen","not_found","isUnconfirmed","ex","getExcessive","isPeerRestricted","ms","getMissing","MessagesView","getDerivedStateFromProps","leave","sendImageAttachment","sendFileAttachment","sendKeyPress","handleScrollReference","handleScrollEvent","handleDescChange","handleSubsUpdated","handleNewMessage","handleAllMessagesReceived","handleInfoReceipt","handleImagePostview","handleClosePreview","handleFormResponse","handleShowContextMenuMessage","handleNewChatAcceptance","handleEnablePeer","postReadNotification","clearNotificationQueue","readNotificationQueue","readNotificationTimer","messagesScroller","scrollTop","scrollHeight","scrollPosition","viewportHeight","onData","onAllMessagesReceived","onInfo","onPres","applicationVisible","isSubscribed","ready","newTopic","newTopicParams","_topicName","getQuery","withLaterDesc","withLaterSub","isReader","withLaterData","withLaterDel","fetchingMessages","setQuery","subscribe","code","onNewTopicCreated","queuedMessages","pub","_sending","publishMessage","setUrlTopic","blankState","nextState","docPreview","imagePreview","imagePostview","typingIndicator","msgs","assign","onlineSubs","peer","p2pPeerDesc","peerMessagingDisabled","isWriter","readingBlocked","unconformed","unconfirmed","oldTopicName","isTopicCached","oldTopic","msgHasMoreMessages","getMessagesPage","setInterval","clearInterval","n","shift","sendAt","noteRead","setMilliseconds","getMilliseconds","isNewMessage","from","what","clearTimeout","keyPressTimer","forceUpdate","sendMessage","attachJSON","parse","searchParams","set","search","messageSpecificMenuItems","onNewChat","noteKeyPress","maxInbandAttachmentSize","uploadCompletionPromise","urlPromise","maxExternAttachmentSize","limit","tinyMine","tinyBlob","blobMime","tinyBits64","insertImage","_tempPreview","appendLineBreak","append","init","bits64","found","find","_cancelled","component","hideSelf","component2","messageNodes","previousFrom","chatBoxClass","nextFrom","thisFrom","isReply","userDesc","hi","ts","_uploader","lastSeen","cont","getMeTopic","getContact","seen","when","onHideMessagesView","ButtonBack","onBack","MenuContacts","onNewTopic","onSettings","MenuStart","onSignUp","SideNavbar","Cropper","panX","panY","originX","originY","zoom","minZoom","maxZoom","overlay","cutout","boundingBox","imageWidth","imageHeight","mouseX","mouseY","prevDistance","cutoutRect","bBoxRect","initScaling","handleZoom","mouseDown","mouseUp","mouseMove","mouseTouch","positionAll","translate","current","passive","currPan","nextDiff","imgRect","imgLeft","imgRight","coLeft","coRight","imgTop","imgBottom","coTop","coBottom","touches","body","dX","dY","checkBound","touch0","touch1","distance","sqrt","t3d","orig","source","transform","transformOrigin","onLoad","step","AvatarCropView","archived_contacts_title","ContactsView","handleAction","unreadThreads","archivedCount","chatList","blocked","arch","touched","action_ignored","onShowArchive","LocalStorageUtil","localStorage","setItem","JSON","stringify","getItem","oldVal","getObject","setObject","removeItem","CreateAccountView","login","password","email","imageDataUrl","errorCleared","saveToken","handleLoginChange","handlePasswordChange","handleEmailChange","handleFnChange","handleToggleSaveToken","onCreateAccount","submitClasses","login_prompt","password_prompt","full_name_prompt","email_prompt","stay_logged_in","EditAccountView","me","onBasicNavigate","AccGeneralView","credentials","getCredentials","addCredActive","addCredInvalid","newCred","previousOnTags","tnNewTags","tnCredsUpdated","handleCredChange","handleCredKeyDown","handleCredEntered","onCredsUpdated","creds","onUpdateAccount","method","asPhone","asEmail","onCredAdd","onUpdateTags","cred","meth","done","onCredConfirm","onCredDelete","full_name_placeholder","title_tag_manager","handleCheckboxClick","onToggleMessageSounds","onTogglePushNotifications","onToggleIncognitoMode","messageSounds","desktopAlertsEnabled","desktopAlerts","incognitoMode","delete_account","delete_account_warning","AccSecurityView","blockedCount","handlePasswordUpdate","handleDeleteAccount","pwd","onDeleteAccount","password_unchanged","onLogout","onShowBlocked","AccSupportView","LoginView","hostName","persist","onPersistenceChange","onLogin","autoCorrect","autoCapitalize","invalid_id","NewTopicById","groupId","prefix","NewTopicGroup","handlePrivateChange","handleTagsChanged","handleChannelToggle","SearchContacts","edited","handleSearchChange","handleSearch","handleClear","onSearchContacts","search_placeholder","search_for_contacts","search_no_results","NewTopicView","tabSelected","searchQuery","handleTabClick","handleSearchContacts","handleNewGroupSubmit","handleGroupByID","addUrlParam","removeUrlParam","onCreateTopic","priv","no_contacts_placeholder","searchResults","PasswordResetView","token","scheme","onReset","onRequest","reset","HostSelector","changed","handleHostNameChange","onServerAddressChange","hostOptions","list","SettingsView","transport","handleTransportSelected","handleServerAddressChange","onUpdate","def","ws","lp","transportOptions","phone","ValidationView","credCode","credMethod","numbers_only","register","settings","edit","general","security","notif","support","newtpk","crop","SidepanelView","handleNewTopic","view","errorAction","errorActionText","loadSpinnerVisible","loginDisabled","onLoginRequest","onGlobalSettings","onUpdateAccountTags","onValidateCredentials","onPasswordResetRequest","onResetPassword","newAvatar","onAvatarCropped","detectServerAddress","host","protocol","hostname","port","isSecureConnection","POP_SOUND","Audio","reconnect_countdown","reconnect_now","push_init_failed","invalid_security_token","no_connection","code_doesnot_match","menu_item_info","TinodeWeb","getBlankState","handleResize","handleHashRoute","handleOnline","checkForAppUpdate","handleVisibilityEvent","handleError","handleLoginRequest","handlePersistenceChange","handleConnected","handleAutoreconnectIteration","doLogin","handleCredentialsRequest","handleLoginSuccessful","handleDisconnect","tnMeMetaDesc","tnMeContactUpdate","tnMeSubsUpdated","resetContactList","tnData","tnInitFind","tnFndSubsUpdated","handleTopicSelected","handleHideMessagesView","handleSendMessage","handleNewChatInvitation","handleNewAccount","handleNewAccountRequest","handleUpdateAccountRequest","handleUpdateAccountTagsRequest","handleToggleIncognitoMode","handleSettings","handleGlobalSettings","handleShowArchive","handleShowBlocked","handleToggleMessageSounds","handleCredAdd","handleCredDelete","handleCredConfirm","handleImageReceived","initFCMessaging","togglePushToken","requestPushToken","handlePushMessage","handleSidepanelCancel","handleStartTopicRequest","handleNewTopicCreated","handleTopicUpdateRequest","handleChangePermissions","handleLogout","handleDeleteMessagesRequest","handleLeaveUnsubRequest","handleBlockTopicRequest","handleReportTopic","handleShowContextMenu","defaultTopicContextMenu","handleHideContextMenu","handleShowAlert","handleShowInfoView","handleHideInfoView","handleValidateCredentialsRequest","handlePasswordResetRequest","handleResetPassword","handleContextMenuAction","autoLogin","messageSoundsOff","FIREBASE_INIT","firebaseToken","hidden","sidePanelSelected","sidePanelTitle","sidePanelAvatar","myTrustedBadges","liveConnection","onLine","topicSelectedOnline","topicSelectedAcs","innerWidth","showInfoPanel","mobilePanel","contextMenuVisible","contextMenuBounds","contextMenuClickAt","contextMenuParams","contextMenuItems","alertVisible","alertParams","requestedTopic","BroadcastChannel","documentElement","clientWidth","clientHeight","tnSetup","onConnect","onDisconnect","onAutoreconnectIteration","setDeviceToken","parsedNav","expires","setAuthToken","connect","readTimer","readTimerCallback","persistentCache","onSetupCompleted","appName","apiKey","secure","setHumanLanguage","enableLogging","oobNotification","fbPush","usePublicVapidKey","messagingVapidKey","serviceWorker","reg","useServiceWorker","postMessage","onTokenRefresh","onMessage","payload","mobile","onupdatefound","installingWorker","installing","onstatechange","controller","getTopicAccessMode","newTopicTabSelected","tab","reconnectCountdown","reconnect","isConnected","initStorage","clearStorage","getServerInfo","ver","sec","prom","timeLeft","seconds","secondsToTime","isAuthenticated","promise","getAuthToken","loginBasic","loginToken","goToTopic","onContactUpdate","getCurrentUserID","withDesc","withCred","disconnect","urlHash","setUrlSidePanel","isPresencer","archived","isArchived","play","foundContacts","merged","updated","prepareSearchableContacts","receivedTimer","noteRecv","fnd","getFndTopic","withSub","s","setMeta","isTopicOnline","isP2PType","am","updateWant","login_","password_","public_","cred_","tags_","createAccountBasic","updateAccountBasic","getCurrentLogin","on","enabled","requestPermission","deleteToken","getToken","refreshedToken","delCredential","basicNavigator","newGroupTopicName","oldName","newName","permissions","updateGiven","delCurrentUser","publish","self_blocked","subscribed","confirmText","rejectText","invite","requestResetAuthSecret","newPassword","str","btoa","atob","base64ReEncode","onAddMember","handleManageGroupMembers","hl","languages","baseLanguage","allMessages","IntlProvider","textComponent","__webpack_require__","module","getter","__esModule","d","exports","definition","o","defineProperty","enumerable","get","obj","prop","prototype","call","__webpack_modules__"],"mappings":"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","file":"index.prod.js","sourcesContent":["const __WEBPACK_NAMESPACE_OBJECT__ = React;","const __WEBPACK_NAMESPACE_OBJECT__ = ReactDOM;","const __WEBPACK_NAMESPACE_OBJECT__ = ReactIntl;","const __WEBPACK_NAMESPACE_OBJECT__ = firebase;","const __WEBPACK_NAMESPACE_OBJECT__ = Tinode;","const __WEBPACK_NAMESPACE_OBJECT__ = firebase.messaging;","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nexport default class Alert extends React.PureComponent {\n render() {\n return this.props.visible ?\n \n
\n
{this.props.title}
\n
{this.props.content}
\n
\n {this.props.onReject ?\n \n :\n null\n }\n \n
\n
\n
\n :\n null;\n }\n};\n","// This is a generated file. Don't edit.\n\nexport const PACKAGE_VERSION = \"0.18.0-alpha1\";\n","import { PACKAGE_VERSION } from './version.js';\n\n// Name of this application, used in the User-Agent.\nexport const APP_NAME = 'TinodeWeb/' + (PACKAGE_VERSION || '0.17');\n\n// API key. Use https://github.com/tinode/chat/tree/master/keygen to generate your own\nexport const API_KEY = 'AQEAAAABAAD_rAp4DJh05a1HAwFT3A6K';\n\n// The array of possible hosts to connect to.\nexport const KNOWN_HOSTS = {hosted: 'web.tinode.co', local: 'localhost:6060'};\n\n// Default host name and port to connect to.\nexport const DEFAULT_HOST = KNOWN_HOSTS.hosted;\n\n// Enable console logging.\nexport const LOGGING_ENABLED = true;\n\n// Minimum time between two keypress notifications, milliseconds.\nexport const KEYPRESS_DELAY = 3*1000;\n// Delay before sending a {note} for reciving a message, milliseconds.\nexport const RECEIVED_DELAY = 500;\n// Delay before sending a read notification, milliseconds.\nexport const READ_DELAY = 1000;\n\n// The default shortest allowed tag length. Matches the value on the server.\nexport const MIN_TAG_LENGTH = 2;\n// The default greatest allowed tag length. Matches the value on the server.\nexport const MAX_TAG_LENGTH = 96;\n// The default maximum number of tags allowed. Matches the value on the server.\nexport const MAX_TAG_COUNT = 16;\n\n// Access mode for P2P subscriptions initiated by the current user.\nexport const DEFAULT_P2P_ACCESS_MODE = 'JRWPS';\n// Access mode for new group topics created by the current user.\nexport const NEW_GRP_ACCESS_MODE = 'JRWPSAO';\n// Access mode for a channel.\nexport const CHANNEL_ACCESS_MODE = 'JR';\n\n// Access mode for no access.\nexport const NO_ACCESS_MODE = 'N';\n\n// Mediaquery breakpoint between desktop and mobile, in px. Should match the value\n// in @media (max-size: 640px) in base.css\nexport const MEDIA_BREAKPOINT = 640;\n// Size of css 'rem' unit in pixels. Default 1rem = 10pt = 13px.\nexport const REM_SIZE = 13;\n\n// Size of the avatar image: when an image is uploaded, it's resized to\n// a square of this size or less.\nexport const AVATAR_SIZE = 384;\n\n// Maximum size of an avatar in bytes for sending in-band. Bigger avatars will be sent out of band (as uploads).\nexport const MAX_AVATAR_BYTES = 8192;\n\n// Size of the broken_image shown in MessagesView\nexport const BROKEN_IMAGE_SIZE = 32;\n\n// Number of chat messages to fetch in one call.\nexport const MESSAGES_PAGE = 24;\n\n// Default maximum in-band (included directly into the message) attachment size which fits into\n// a message of 256K in size. Used when the server-provided value is unavailable. The actual\n// binary size of the attachment should be smaller due to base64 encoding expansion and some overhead,\n// for instance 1024 bytes.\n// Increase this limit to a greater value in production, if desired. Also increase\n// max_message_size in the server config.\nexport const MAX_INBAND_ATTACHMENT_SIZE = 262144;\n\n// Default absolute maximum attachment size to be used with the server = 8MB.\n// Used when the server-provided value is unavailable. Increase to something like 100MB in production.\nexport const MAX_EXTERN_ATTACHMENT_SIZE = 1 << 23;\n\n// Maximum allowed linear dimension (pixels) of an image sent inline. Larger images will be shrunk\n// to make the larger dimension fit under this size. You may want to adjust it to 1600 or\n// 2400 for production.\nexport const MAX_IMAGE_DIM = 1024;\n\n// Linear dimensions of image preview: shrink image under this size for previews.\nexport const IMAGE_PREVIEW_DIM = 64;\n\n// Maximum number of online users to be shown in a topic title bar. Others will be\n// hidden under \"+X more\"\nexport const MAX_ONLINE_IN_TOPIC = 4;\n\n// Maximum length of user name, topic title, and private comment.\nexport const MAX_TITLE_LENGTH = 60;\n\n// Length of message previews in chat list, in characters.\nexport const MESSAGE_PREVIEW_LENGTH = 80;\n\n// Link for \"Contact Us\".\nexport const LINK_CONTACT_US = 'email:support@tinode.co';\n\n// Link to Privacy Policy.\nexport const LINK_PRIVACY_POLICY = 'https://tinode.co/privacy.html';\n\n// Link to Terms of Service.\nexport const LINK_TERMS_OF_SERVICE = 'https://tinode.co/terms.html';\n","// Context Menu: popup/dropdown menu.\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport { injectIntl, defineMessages } from 'react-intl';\n\nimport { REM_SIZE } from '../config.js';\nimport HashNavigation from '../lib/navigation.js';\n\nconst messages = defineMessages({\n info: {\n id: 'menu_item_info',\n defaultMessage: 'Info',\n description: 'Show extended topic information'\n },\n clear_messages: {\n id: 'menu_item_clear_messages',\n defaultMessage: 'Clear messages',\n description: 'Delete all messages'\n },\n clear_for_all: {\n id: 'menu_item_clear_messages_for_all',\n defaultMessage: 'Clear for All',\n description: 'Delete all message(s) for all members'\n },\n delete: {\n id: 'menu_item_delete',\n defaultMessage: 'Delete',\n description: 'Delete selected messages'\n },\n delete_for_all: {\n id: 'menu_item_delete_for_all',\n defaultMessage: 'Delete for All',\n description: 'Delete selected message(s) for all members'\n },\n send_retry: {\n id: 'menu_item_send_retry',\n defaultMessage: 'Retry',\n description: 'Retry sending message'\n },\n mute: {\n id: 'menu_item_mute',\n defaultMessage: 'Mute',\n description: 'Turn off notifications'\n },\n unmute: {\n id: 'menu_item_unmute',\n defaultMessage: 'Unmute',\n description: 'Turn notifications on'\n },\n topic_delete: {\n id: 'menu_item_delete_topic',\n defaultMessage: 'Delete',\n description: 'Delete entire topic'\n },\n topic_delete_warning: {\n id: 'topic_delete_warning',\n defaultMessage: 'Are you sure you want to delete this conversation?',\n description: 'Alert warning when deleting entire topic'\n },\n delete_messages_warning: {\n id: 'delete_messages_warning',\n defaultMessage: 'Are you sure you want to delete all messages for everyone? It cannot be undone.',\n description: 'Alert dialog warning when hard-deleting all messages.'\n },\n unblock: {\n id: 'menu_item_unblock',\n defaultMessage: 'Unblock',\n description: 'Unblock topic or user'\n },\n // Generic string suitable for either topic or user.\n block: {\n id: 'menu_item_block',\n defaultMessage: 'Block',\n description: 'Block topic or user'\n },\n topic_block_warning: {\n id: 'topic_block_warning',\n defaultMessage: 'Are you sure you want to block this conversation?',\n description: 'Alert warning when blocking a topic.'\n },\n member_delete: {\n id: 'menu_item_member_delete',\n defaultMessage: 'Remove',\n description: 'Remove user from topic'\n },\n archive: {\n id: 'menu_item_archive_topic',\n defaultMessage: 'Archive',\n description: 'Move topic from the list of active chats to archive'\n },\n unarchive: {\n id: 'menu_item_restore_topic',\n defaultMessage: 'Restore',\n description: 'Restore topic from archive'\n },\n edit_permissions: {\n id: 'menu_item_edit_permissions',\n defaultMessage: 'Edit permissions',\n description: 'Menu item [Edit permissions]'\n },\n clear_messages_warning: {\n id: 'clear_messages_warning',\n defaultMessage: 'Are you sure you want to clear all messages? It cannot be undone.',\n description: 'Alert dialog warning when deleting all messages.'\n },\n});\n\nclass ContextMenu extends React.Component {\n constructor(props) {\n super(props);\n\n const {formatMessage} = props.intl;\n\n this.handlePageClick = this.handlePageClick.bind(this);\n this.handleEscapeKey = this.handleEscapeKey.bind(this);\n this.handleClick = this.handleClick.bind(this);\n\n // Preconfigured menu items.\n this.MenuItems = {\n 'topic_info': {\n id: 'topic_info',\n title: formatMessage(messages.info),\n handler: null\n },\n 'messages_clear': {\n id: 'messages_clear',\n title: formatMessage(messages.clear_messages),\n handler: (params, errorHandler) => {\n return props.onShowAlert(\n formatMessage(messages.clear_messages), // title\n formatMessage(messages.clear_messages_warning), // content\n (() => { this.deleteMessages(true, false, params, errorHandler); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n },\n 'messages_clear_hard': {\n id: 'messages_clear_hard',\n title: formatMessage(messages.clear_for_all),\n handler: (params, errorHandler) => {\n return props.onShowAlert(\n formatMessage(messages.clear_for_all), // title\n formatMessage(message.delete_messages_warning), // content\n (() => { return this.deleteMessages(true, true, params, errorHandler); }),\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n },\n 'message_delete': {\n id: 'message_delete',\n title: formatMessage(messages.delete),\n handler: (params, errorHandler) => {\n return this.deleteMessages(false, false, params, errorHandler);\n }\n },\n 'message_delete_hard': {\n id: 'message_delete_hard',\n title: formatMessage(messages.delete_for_all),\n handler: (params, errorHandler) => {\n return this.deleteMessages(false, true, params, errorHandler);\n }\n },\n 'menu_item_send_retry': {\n id: 'menu_item_send_retry',\n title: formatMessage(messages.send_retry),\n handler: (params, errorHandler) => {\n return this.retryMessage(params, errorHandler);\n }\n },\n 'topic_unmute': {\n id: 'topic_unmute',\n title: formatMessage(messages.unmute),\n handler: this.topicPermissionSetter.bind(this, '+P')\n },\n 'topic_mute': {\n id: 'topic_mute',\n title: formatMessage(messages.mute),\n handler: this.topicPermissionSetter.bind(this, '-P')\n },\n 'topic_unblock': {\n id: 'topic_unblock',\n title: formatMessage(messages.unblock),\n handler: this.topicPermissionSetter.bind(this, '+JP')\n },\n 'topic_block': {\n id: 'topic_block',\n title: formatMessage(messages.block),\n handler: (params, errorHandler) => {\n return props.onShowAlert(\n formatMessage(messages.block), // title\n formatMessage(messages.topic_block_warning), // content\n (() => {\n return this.topicPermissionSetter('-JP', params, errorHandler).then((ctrl) => {\n this.props.onTopicRemoved(params.topicName);\n return ctrl;\n });\n }),\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n },\n 'topic_delete': {\n id: 'topic_delete',\n title: formatMessage(messages.topic_delete),\n handler: (params, errorHandler) => {\n return props.onShowAlert(\n formatMessage(messages.topic_delete), // title\n formatMessage(messages.topic_delete_warning), // content\n (() => {\n const topic = this.props.tinode.getTopic(params.topicName);\n if (!topic) {\n console.log(\"Topic not found: \", params.topicName);\n return;\n }\n return topic.delTopic(true).catch((err) => {\n if (errorHandler) {\n errorHandler(err.message, 'err');\n }\n });\n }),\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n },\n 'topic_archive': {\n id: 'topic_archive',\n title: formatMessage(messages.archive),\n handler: (params, errorHandler) => {\n const topic = this.props.tinode.getTopic(params.topicName);\n if (!topic) {\n console.log(\"Topic not found: \", params.topicName);\n return;\n }\n return topic.archive(true).catch((err) => {\n if (errorHandler) {\n errorHandler(err.message, 'err');\n }\n });\n }\n },\n 'topic_restore': {\n id: 'topic_restore',\n title: formatMessage(messages.unarchive),\n handler: (params, errorHandler) => {\n const topic = this.props.tinode.getTopic(params.topicName);\n if (!topic) {\n console.log(\"Topic not found: \", params.topicName);\n return;\n }\n return topic.archive(false).catch((err) => {\n if (errorHandler) {\n errorHandler(err.message, 'err');\n }\n });\n }\n },\n // menu_item_edit_permissions is defined elsewhere.\n 'permissions': {\n id: 'permissions',\n title: formatMessage(messages.edit_permissions),\n handler: null\n },\n 'member_delete': {\n id: 'member_delete',\n title: formatMessage(messages.member_delete),\n handler: (params, errorHandler) => {\n const topic = this.props.tinode.getTopic(params.topicName);\n if (!topic || !params.user) {\n console.log(\"Topic or user not found: '\" + params.topicName + \"', '\" + params.user + \"'\");\n return;\n }\n return topic.delSubscription(params.user).catch((err) => {\n if (errorHandler) {\n errorHandler(err.message, 'err');\n }\n });\n }\n },\n 'member_mute': {\n id: 'member_mute',\n title: formatMessage(messages.mute),\n handler: this.topicPermissionSetter.bind(this, '-P')\n },\n 'member_unmute': {\n id: 'member_unmute',\n title: formatMessage(messages.unmute),\n handler: this.topicPermissionSetter.bind(this, '+P')\n },\n 'member_block': {\n id: 'member_block',\n title: formatMessage(messages.block),\n handler: this.topicPermissionSetter.bind(this, '-JP')\n },\n 'member_unblock': {\n id: 'member_unblock',\n title: formatMessage(messages.unblock),\n handler: this.topicPermissionSetter.bind(this, '+JP')\n },\n };\n }\n\n componentDidMount() {\n document.addEventListener('mousedown', this.handlePageClick, false);\n document.addEventListener('keyup', this.handleEscapeKey, false);\n }\n\n componentWillUnmount() {\n document.removeEventListener('mousedown', this.handlePageClick, false);\n document.removeEventListener('keyup', this.handleEscapeKey, false);\n }\n\n handlePageClick(e) {\n if (ReactDOM.findDOMNode(this).contains(e.target)) {\n return;\n }\n e.preventDefault();\n e.stopPropagation();\n this.props.hide();\n }\n\n handleEscapeKey(e) {\n if (e.keyCode === 27) {\n this.props.hide();\n }\n }\n\n handleClick(e) {\n e.preventDefault();\n e.stopPropagation();\n this.props.hide();\n let item = this.props.items[e.currentTarget.dataset.id];\n if (typeof item == 'string') {\n item = this.MenuItems[item];\n }\n\n if (!item) {\n console.log(\"Invalid menu item ID\", e.currentTarget.dataset.id);\n } else {\n this.props.onAction(\n item.id,\n item.handler(this.props.params, this.props.onError),\n this.props.params);\n }\n }\n\n // Menu Actions\n\n deleteMessages(all, hard, params, errorHandler) {\n const topic = this.props.tinode.getTopic(params.topicName);\n if (!topic) {\n console.log(\"Topic not found: \", params.topicName);\n return;\n }\n\n // We don't know if the message is still pending (e.g. attachment is being uploaded),\n // so try cancelling first. No harm if we can't cancel.\n // The message can be cancelled if transmission to the server has not\n // started yet or if the message send has failed.\n if (!all && topic.cancelSend(params.seq)) {\n return;\n }\n // Can't cancel. Delete instead.\n const promise = all ?\n topic.delMessagesAll(hard) :\n topic.delMessagesList([params.seq], hard);\n\n return promise.catch((err) => {\n if (errorHandler) {\n errorHandler(err.message, 'err');\n }\n });\n }\n\n // Retries sending failed message.\n retryMessage(params, errorHandler) {\n const topic = this.props.tinode.getTopic(params.topicName);\n // Remove the existing message entry.\n if (!topic || !topic.flushMessage(params.seq)) {\n return;\n }\n const msg = topic.createMessage(params.content, false);\n return topic.publishDraft(msg).catch((err) => {\n if (errorHandler) {\n errorHandler(err.message, 'err');\n }\n });\n }\n\n // Function is used by context menu to set permissions.\n topicPermissionSetter(mode, params, errorHandler) {\n const topic = this.props.tinode.getTopic(params.topicName);\n if (!topic) {\n console.log(\"Topic not found\", params.topicName);\n return;\n }\n\n let result = topic.updateMode(params.user, mode);\n if (errorHandler) {\n result = result.catch((err) => {\n errorHandler(err.message, 'err');\n });\n }\n return result;\n }\n\n render() {\n let count = 0;\n let menu = [];\n this.props.items.map((item) => {\n if (typeof item == 'string') {\n item = this.MenuItems[item];\n }\n if (item && item.title) {\n menu.push(\n item.title == '-' ?\n \n :\n {item.title}\n );\n }\n count++;\n });\n\n // Ensure that menu is inside the app-container.\n const hSize = 12 * REM_SIZE;\n const vSize = REM_SIZE * (0.7 + menu.length * 2.5);\n const left = (this.props.bounds.right - this.props.clickAt.x < hSize) ?\n (this.props.clickAt.x - this.props.bounds.left - hSize) :\n (this.props.clickAt.x - this.props.bounds.left);\n const top = (this.props.bounds.bottom - this.props.clickAt.y < vSize) ?\n (this.props.clickAt.y - this.props.bounds.top - vSize) :\n (this.props.clickAt.y - this.props.bounds.top);\n\n const position = {\n left: left + 'px',\n top: top + 'px'\n };\n\n return (\n \n );\n }\n}\n\nexport default injectIntl(ContextMenu);\n","// Short representation of time in the past.\nexport function shortDateFormat(then, locale) {\n locale = locale || window.navigator.userLanguage || window.navigator.language;\n const now = new Date();\n if (then.getFullYear() == now.getFullYear()) {\n // Same year.\n if (then.getMonth() == now.getMonth() && then.getDate() == now.getDate()) {\n // Same month and day, show time only.\n return then.toLocaleTimeString(locale, {hour12: false, hour: '2-digit', minute: '2-digit'});\n }\n // Different month and/or day, show month day, time.\n return then.toLocaleDateString(locale,\n {hour12: false, month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit'});\n }\n // Different year: just show the date.\n return then.toLocaleDateString(locale, {year: 'numeric', month: 'short', day: 'numeric'});\n}\n\n// Convert seconds to minutes:seconds, i.e. 156 sec -> 2:36.\nexport function secondsToTime(seconds) {\n const min = Math.floor(seconds / 60);\n let sec = seconds % 60;\n sec = sec < 10 ? `0${sec}` : sec;\n return `${min}:${sec}`;\n}\n\n// Convert a number of bytes to human-readable format.\nexport function bytesToHumanSize(bytes) {\n if (!bytes || bytes == 0) {\n return '0 Bytes';\n }\n\n const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];\n const bucket = Math.min(Math.floor(Math.log2(bytes) / 10) | 0, sizes.length-1);\n const count = bytes / Math.pow(1024, bucket);\n const round = bucket > 0 ? (count < 3 ? 2 : (count < 30 ? 1 : 0)) : 0;\n return count.toFixed(round) + ' ' + sizes[bucket];\n}\n\n// Get 32 bit integer hash value for a string. Ideally it should produce the same value\n// as Java's String#hash().\nexport function stringHash(value) {\n let hash = 0;\n value = '' + value;\n for (let i = 0; i < value.length; i++) {\n hash = ((hash << 5) - hash) + value.charCodeAt(i);\n hash = hash & hash; // Convert to 32bit integer\n }\n return hash;\n}\n","import React from 'react';\n\nimport Tinode from 'tinode-sdk';\n\nimport { stringHash } from '../lib/strformat.js';\n\nexport default class LetterTile extends React.PureComponent {\n render() {\n let avatar;\n if (this.props.avatar === true) {\n const isGroup = (Tinode.topicType(this.props.topic) == 'grp');\n const iconColor = (isGroup ? 'light-color' : 'dark-color')\n + (Math.abs(stringHash(this.props.topic)) % 16);\n if (this.props.topic && this.props.title && this.props.title.trim()) {\n const letter = this.props.title.trim().charAt(0);\n const className = 'lettertile ' + iconColor;\n avatar = ()\n } else {\n const className = 'material-icons ' + iconColor;\n avatar = isGroup ?\n group : person;\n }\n } else if (this.props.avatar) {\n // If avatar image is invalid, show a placeholder.\n avatar =
{e.target.onerror = null; e.target.src=\"/img/broken_image.png\"}} />;\n } else {\n avatar = null;\n }\n return avatar;\n }\n}\n","import React from 'react';\n\nexport default class LoadSpinner extends React.PureComponent {\n render() {\n const className = 'load-spinner-box' + (this.props.large ? ' large' : '')\n + (this.props.clear ? ' clear' : '') + (this.props.centered ? ' centered' : '');\n return (this.props.show ?\n : null);\n }\n}\n","// File and image helper functions.\nimport { MAX_INBAND_ATTACHMENT_SIZE, MAX_IMAGE_DIM } from '../config.js';\nimport { bytesToHumanSize } from './strformat.js'\n\n// Supported image MIME types and corresponding file extensions.\nexport const SUPPORTED_IMAGE_FORMATS = ['image/jpeg', 'image/gif', 'image/png', 'image/svg', 'image/svg+xml'];\nexport const MIME_EXTENSIONS = ['jpg', 'gif', 'png', 'svg', 'svg'];\n\n// Make a data: URL from public.photo\nexport function makeImageDataUrl(photo) {\n if (photo) {\n if (photo.data && photo.type) {\n const mime = photo.type.startsWith('image/') ? photo.type : ('image/' + photo.type);\n return 'data:' + mime + ';base64,' + photo.data;\n }\n return photo.ref;\n }\n return null;\n}\n\n// Calculate linear dimensions for scaling image down to fit under a certain size.\n// Returns an object which contains destination sizes, source sizes, and offsets\n// into source (when making square images).\nexport function fitImageSize(width, height, maxWidth, maxHeight, forceSquare) {\n // Sanitize input\n width = width | 0;\n height = height | 0;\n maxWidth = maxWidth | 0;\n maxHeight = maxHeight | 0;\n\n if (width <= 0 || height <= 0 || maxWidth <= 0 || maxHeight <= 0) {\n return null;\n }\n\n if (forceSquare) {\n maxWidth = maxHeight = Math.min(maxWidth, maxHeight);\n }\n\n const scale = Math.min(\n Math.min(width, maxWidth) / width,\n Math.min(height, maxHeight) / height\n );\n\n const size = {\n dstWidth: (width * scale) | 0,\n dstHeight: (height * scale) | 0,\n };\n\n if (forceSquare) {\n // Also calculate parameters for making the image square.\n size.dstWidth = size.dstHeight = Math.min(size.dstWidth, size.dstHeight);\n size.srcWidth = size.srcHeight = Math.min(width, height);\n size.xoffset = ((width - size.srcWidth) / 2) | 0;\n size.yoffset = ((height - size.srcWidth) / 2) | 0;\n } else {\n size.xoffset = size.yoffset = 0;\n size.srcWidth = width;\n size.srcHeight = height;\n }\n return size;\n}\n\n// Ensure file's extension matches mime content type\nexport function fileNameForMime(fname, mime) {\n const idx = SUPPORTED_IMAGE_FORMATS.indexOf(mime);\n if (idx < 0 || !fname) {\n // Unknown mime or empty name.\n return fname;\n }\n const ext = MIME_EXTENSIONS[idx];\n\n const at = fname.lastIndexOf('.');\n if (at >= 0) {\n fname = fname.substring(0, at);\n }\n return fname + '.' + ext;\n}\n\n// Scale uploaded image to fit under certain dimensions and byte size, optionally constraining to a square.\n// On success calls onSuccess callback with the scaled image as Blob.\nexport function imageScaled(fileOrBlob, maxWidth, maxHeight, maxSize, forceSquare, onSuccess, onError) {\n const img = new Image();\n img.crossOrigin = 'Anonymous';\n img.onerror = function(err) {\n onError(\"Image format unrecognized\");\n }\n img.onload = async function() {\n // Once the image is loaded, the URL is no longer needed.\n URL.revokeObjectURL(img.src);\n\n // Calculate the desired image dimensions.\n const dim = fitImageSize(this.width, this.height, maxWidth, maxHeight, forceSquare);\n if (!dim) {\n onError(\"Invalid image\");\n return;\n }\n let canvas = document.createElement('canvas');\n canvas.width = dim.dstWidth;\n canvas.height = dim.dstHeight;\n let ctx = canvas.getContext('2d');\n ctx.imageSmoothingEnabled = true;\n ctx.drawImage(this, dim.xoffset, dim.yoffset, dim.srcWidth, dim.srcHeight,\n 0, 0, dim.dstWidth, dim.dstHeight);\n\n const mime = SUPPORTED_IMAGE_FORMATS.includes(fileOrBlob.type) ? fileOrBlob.type : 'image/jpeg';\n // Generate blob to check size of the image.\n let blob = await new Promise(resolve => canvas.toBlob(resolve, mime));\n if (!blob) {\n onError(\"Unsupported image format\");\n return;\n }\n\n // Ensure the image is not too large. Shrink the image keeping the aspect ratio.\n // Do nothing if maxsize is <= 0.\n while (maxSize > 0 && blob.length > maxSize) {\n dim.dstWidth = (dim.dstWidth * 0.70710678118) | 0;\n dim.dstHeight = (dim.dstHeight * 0.70710678118) | 0;\n canvas.width = dim.dstWidth;\n canvas.height = dim.dstHeight;\n ctx = canvas.getContext('2d');\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n ctx.drawImage(this, dim.xoffset, dim.yoffset, dim.srcWidth, dim.srcHeight,\n 0, 0, dim.dstWidth, dim.dstHeight);\n blob = await new Promise(resolve => canvas.toBlob(resolve, mime));\n }\n\n canvas = null;\n onSuccess(mime, blob, dim.dstWidth, dim.dstHeight, fileNameForMime(fileOrBlob.name, mime));\n };\n img.src = URL.createObjectURL(fileOrBlob);\n}\n\n// Convert File to base64 string.\nexport function fileToBase64(file, onSuccess) {\n const reader = new FileReader();\n reader.addEventListener('load', function() {\n onSuccess(file.type, reader.result.split(',')[1], file.name);\n });\n reader.readAsDataURL(file);\n}\n\n// Convert Blob to base64 string.\nexport function blobToBase64(blob, onSuccess) {\n const reader = new FileReader();\n reader.addEventListener('load', function() {\n onSuccess(blob.type, reader.result.split(',')[1]);\n });\n reader.readAsDataURL(blob);\n}\n\n// File pasted from the clipboard. It's either an inline image or a file attachment.\n// FIXME: handle large files out of band.\nexport function filePasted(event, onImageSuccess, onAttachmentSuccess, onError) {\n var items = (event.clipboardData || event.originalEvent.clipboardData || {}).items;\n for (var i in items) {\n var item = items[i];\n if (item.kind === 'file') {\n var file = item.getAsFile();\n if (!file) {\n console.log(\"Failed to get file object from pasted file item\", item.kind, item.type);\n continue;\n }\n if (file.type && file.type.split('/')[0] == 'image') {\n // Handle inline image\n if (file.size > MAX_INBAND_ATTACHMENT_SIZE || SUPPORTED_IMAGE_FORMATS.indexOf(file.type) < 0) {\n imageFileScaledToBase64(file, MAX_IMAGE_DIM, MAX_IMAGE_DIM, false, onImageSuccess, onError);\n } else {\n imageFileToBase64(file, onImageSuccess, onError);\n }\n } else {\n // Handle file attachment\n fileToBase64(file, onAttachmentSuccess, onError)\n }\n // Indicate that the pasted data contains a file.\n return true;\n }\n }\n // No file found.\n return false;\n}\n\n// Get mime type from data URL header.\nexport function getMimeType(header) {\n var mime = /^data:(image\\/[-+a-z0-9.]+);base64/.exec(header);\n return (mime && mime.length > 1) ? mime[1] : null;\n}\n\n// Given length of a binary object in bytes, calculate the length after\n// base64 encoding.\nexport function base64EncodedLen(n) {\n return Math.floor((n + 2) / 3) * 4;\n}\n\n// Given length of a base64-encoded object, calculate decoded size of the\n// pbject in bytes.\nexport function base64DecodedLen(n) {\n return Math.floor(n / 4) * 3;\n}\n\n// Re-encode string to standard base64 encoding with padding.\n// The string may be base64-URL encoded without the padding.\nexport function base64ReEncode(str) {\n if (str) {\n str = str.replace(/-/g, '+').replace(/_/g, '/');\n try {\n str = btoa(atob(str));\n } catch(err) {\n console.log(\"Failed to base64 re-encode string.\", err);\n str = null;\n }\n }\n return str;\n}\n","import React from 'react';\n\nimport LetterTile from './letter-tile.jsx';\nimport LoadSpinner from './load-spinner.jsx';\n\nimport { AVATAR_SIZE, IMAGE_PREVIEW_DIM, MAX_AVATAR_BYTES, MAX_EXTERN_ATTACHMENT_SIZE,\n MAX_INBAND_ATTACHMENT_SIZE } from '../config.js';\nimport { imageScaled, blobToBase64, makeImageDataUrl } from '../lib/blob-helpers.js';\n\nexport default class AvatarUpload extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n dataUrl: props.avatar,\n uploading: false\n };\n\n this.handleFileUpload = this.handleFileUpload.bind(this);\n }\n\n componentDidUpdate(prevProps) {\n if (this.props.avatar != prevProps.avatar) {\n this.setState({dataUrl: this.props.avatar});\n }\n }\n\n handleFileUpload(e) {\n const image = e.target.files[0];\n if (this.props.onImageReceived) {\n this.props.onFileReceived(URL.createObjectURL(image));\n return;\n }\n\n if (image.size > MAX_AVATAR_BYTES) {\n // Too large to send inband - uploading out of band and sending as a link.\n const uploader = this.props.tinode.getLargeFileHelper();\n if (!uploader) {\n this.props.onError(this.props.intl.formatMessage(messages.cannot_initiate_upload));\n return;\n }\n\n uploader.upload(image)\n .then((url) => {\n this.props.onImageChanged(url);\n })\n .catch((err) => {\n this.props.onError(err, 'err');\n })\n .finally(() => {\n this.setState({uploading: false});\n });\n } else {\n imageScaled(image, AVATAR_SIZE, AVATAR_SIZE, MAX_EXTERN_ATTACHMENT_SIZE, true,\n // Image successfully scaled and converted.\n (mime, blob) => {\n // Convert blob to base64-encoded bits.\n blobToBase64(blob, (unused, base64bits) => {\n const du = makeImageDataUrl({data: base64bits, type: mime});\n this.setState({dataUrl: du});\n this.props.onImageChanged(du);\n });\n },\n // Failure\n (err) => {\n this.props.onError(err, 'err');\n });\n }\n // Clear the value so the same file can be uploaded again.\n e.target.value = '';\n }\n\n render() {\n // Randomize id value in case more than one AvatarUpload is shown\n // at the same time.\n const randId = 'file-input-avatar-' + (Math.random() + '').substr(2);\n const className = 'avatar-upload' + (this.props.readOnly ? ' read-only' : '');\n return (\n \n {this.props.readOnly || !this.state.dataUrl ?\n null :\n
{e.preventDefault(); this.props.onImageChanged(null);}}>\n clear\n }\n {this.state.dataUrl ?\n

:\n this.props.readOnly && this.props.uid ?\n
\n \n
\n :\n
{AVATAR_SIZE}×{AVATAR_SIZE}
}\n {this.props.readOnly ? null :\n
}\n {this.props.readOnly ? null :\n
}\n
\n
\n );\n }\n};\n","import React from 'react';\nimport { defineMessages, injectIntl } from 'react-intl';\n\nconst icon_mapping = {'staff': 'verified_user'};\n\nconst messages = defineMessages({\n badge_verified: {\n id: 'badge_verified',\n defaultMessage: 'Verified/official',\n description: 'Explanation of a verified account or topic badge'\n },\n badge_staff: {\n id: 'badge_staff',\n defaultMessage: 'Staff-managed',\n description: 'Explanation of a staff-managed account or topic badge'\n },\n badge_danger: {\n id: 'badge_danger',\n defaultMessage: 'Untrustworthy',\n description: 'Suspicious or untrustworthy account or topic badge'\n },\n});\n\n\n/* Trusted badges with labels: verified, staff, danger */\nclass BadgeList extends React.PureComponent {\n render() {\n const {formatMessage} = this.props.intl;\n let badges = null;\n if (this.props.trustedBadges && this.props.trustedBadges.length > 0) {\n badges = [];\n this.props.trustedBadges.map((b) => {\n const comment = formatMessage(messages['badge_' + b]);\n const style = 'material-icons ' + b + '-color';\n badges.push({icon_mapping[b] || b} {comment}
);\n });\n return <>{badges}>;\n }\n return null;\n }\n};\n\nexport default injectIntl(BadgeList);\n","import React from 'react';\n\n/* BEGIN CheckBox: styled checkbox */\nexport default class CheckBox extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleChange = this.handleChange.bind(this);\n }\n\n handleChange() {\n this.props.onChange(this.props.name, !this.props.checked);\n }\n\n render() {\n return (\n this.props.onChange ? (\n this.props.checked ?\n check_box :\n check_box_outline_blank\n ) : (\n this.props.checked ?\n check_box :\n check_box_outline_blank\n )\n );\n }\n}\n/* END CheckBox */\n","import React from 'react';\n\nconst icon_mapping = {'muted': 'notifications_off', 'banned': 'block', 'staff': 'verified_user'};\n\n/* Contact's labels: [you], or icons [muted], [blocked] */\nexport default class ContactBadges extends React.PureComponent {\n render() {\n let badges = null;\n if (this.props.badges && this.props.badges.length > 0) {\n badges = [];\n this.props.badges.map((b) => {\n const color = (b.color ? ' ' + b.color : '');\n if (b.icon) {\n badges.push({icon_mapping[b.icon] || b.icon});\n } else {\n badges.push({b.name});\n }\n });\n return <>{badges}>;\n }\n return null;\n }\n};\n","// Odds and ends\n\nimport Tinode from 'tinode-sdk';\n\n// Make shortcut icon appear with a green dot + show unread count in title.\nexport function updateFavicon(count) {\n const oldIcon = document.getElementById('shortcut-icon');\n const head = document.head || document.getElementsByTagName('head')[0];\n const newIcon = document.createElement('link');\n newIcon.type = 'image/png';\n newIcon.id = 'shortcut-icon';\n newIcon.rel = 'shortcut icon';\n newIcon.href = 'img/logo32x32' + (count > 0 ? 'a' : '') + '.png';\n if (oldIcon) {\n head.removeChild(oldIcon);\n }\n head.appendChild(newIcon);\n\n document.title = (count > 0 ? '('+count+') ' : '') + 'Tinode';\n}\n\n// Create theCard which represents user's or topic's \"public\" info.\nexport function theCard(fn, imageUrl, imageMimeType) {\n let card = null;\n fn = fn && fn.trim();\n\n if (fn) {\n card = {\n fn: fn\n };\n }\n\n if (imageUrl) {\n card = card || {};\n let mimeType = imageMimeType;\n // Is this a data URL \"data:[][;base64],\"?\n const matches = /^data:(image\\/[-a-z0-9+.]+)?(;base64)?,/i.exec(imageUrl);\n if (matches) {\n mimeType = matches[1];\n card.photo = {\n data: imageUrl.substring(imageUrl.indexOf(',') + 1)\n };\n } else {\n card.photo = {\n ref: imageUrl\n };\n }\n card.photo.type = (mimeType || 'image/jpeg').substring('image/'.length);\n }\n\n return card;\n}\n\n// Deep-shallow compare two arrays: shallow compare each element.\nexport function arrayEqual(a, b) {\n if (a === b) {\n return true;\n }\n\n if (!Array.isArray(a) || !Array.isArray(b)) {\n return false;\n }\n\n // Compare lengths first.\n if (a.length != b.length) {\n return false;\n }\n // Order of elements is ignored.\n a.sort();\n b.sort();\n for (let i = 0, l = a.length; i < l; i++) {\n if (a[i] !== b[i]) {\n return false;\n }\n }\n return true;\n}\n\n// Checks (loosely) if the given string is a phone. If so, returns the phone number in a format\n// as close to E.164 as possible.\nexport function asPhone(val) {\n val = val.trim();\n if (/^(?:\\+?(\\d{1,3}))?[- (.]*(\\d{3})[- ).]*(\\d{3})[- .]*(\\d{2})[- .]*(\\d{2})?$/.test(val)) {\n return val.replace(/[- ().]*/, '');\n }\n return null;\n}\n\n// Checks (loosely) if the given string is an email. If so returns the email.\nexport function asEmail(val) {\n val = val.trim();\n if (/^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$/.test(val)) {\n return val;\n }\n return null;\n}\n\n// Checks if URL is a relative url, i.e. has no 'scheme://', including the case of missing scheme '//'.\n// The scheme is expected to be RFC-compliant, e.g. [a-z][a-z0-9+.-]*\n// example.html - ok\n// https:example.com - not ok.\n// http:/example.com - not ok.\n// ' ↲ https://example.com' - not ok. (↲ means carriage return)\nexport function isUrlRelative(url) {\n return url && !/^\\s*([a-z][a-z0-9+.-]*:|\\/\\/)/im.test(url);\n}\n\n// Ensure URL does not present an XSS risk. Optional allowedSchemes may contain an array of\n// strings with permitted URL schemes, such as ['ftp', 'ftps']; otherwise accept http and https only.\n// If apikey and/or token is defined, it's appended to relative URL.\nexport function sanitizeUrl(url, allowedSchemes) {\n if (!url) {\n return null;\n }\n\n // Strip control characters and whitespace. They are not valid URL characters anyway.\n url = url.replace(/[^\\x20-\\x7E]/gmi, '').trim();\n\n // Relative URLs are safe.\n // Relative URL does not start with ':', abcd123: or '//'.\n if (!/^([a-z][a-z0-9+.-]*:|\\/\\/)/i.test(url)) {\n return url;\n }\n\n // Blob URLs are safe.\n if (/^blob:http/.test(url)) {\n return url;\n }\n\n // Absolute URL. Accept only safe schemes, or no scheme.\n const schemes = Array.isArray(allowedSchemes) ? allowedSchemes.join('|') : 'http|https';\n const re = new RegExp('^((' + schemes + '):|//)', 'i');\n if (!re.test(url)) {\n return null;\n }\n\n return url;\n}\n\n// Ensure URL is suitable for
field: the URL must be a relative URL or\n// have http:, https:, blob: or data: scheme.\n// The relative URL is appended with the API key and/or auth token.\n// In case of data: scheme, the URL must start with a 'data:image/XXXX;base64,'.\nexport function sanitizeImageUrl(url) {\n if (!url) {\n return null;\n }\n\n const sanitizedUrl = sanitizeUrl(url);\n if (sanitizedUrl) {\n return sanitizedUrl;\n }\n\n // Is this a data: URL of an image?\n if (/data:image\\/[a-z0-9.-]+;base64,/i.test(url.trim())) {\n return url;\n }\n\n return null;\n}\n\n// Given message's received status, return name and color of a delivery indicator icon.\nexport function deliveryMarker(received) {\n switch (received) {\n case Tinode.MESSAGE_STATUS_SENDING:\n return { name: 'access_time' }; // watch face\n case Tinode.MESSAGE_STATUS_FAILED:\n return { name: 'warning', color: 'danger-color' }; // yellow icon /!\\\n case Tinode.MESSAGE_STATUS_SENT:\n return { name: 'done' }; // checkmark\n case Tinode.MESSAGE_STATUS_RECEIVED:\n return { name: 'done_all' }; // double checkmark\n case Tinode.MESSAGE_STATUS_READ:\n return { name: 'done_all', color: 'blue' }; // blue double checkmark\n }\n return null;\n}\n","// Received/read indicator.\nimport React from 'react';\nimport { defineMessages, injectIntl } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport { shortDateFormat } from '../lib/strformat.js';\nimport { deliveryMarker } from '../lib/utils.js';\n\nconst messages = defineMessages({\n message_sending: {\n id: 'message_sending',\n defaultMessage: 'sending...',\n description: 'Message being sent, in place of time stamp'\n },\n message_sending_failed: {\n id: 'message_sending_failed',\n defaultMessage: 'failed',\n description: 'Failed to send message, in place of time stamp'\n }\n});\n\nclass ReceivedMarker extends React.PureComponent {\n render() {\n const {formatMessage} = this.props.intl;\n let timestamp;\n if (this.props.received <= Tinode.MESSAGE_STATUS_SENDING) {\n timestamp = formatMessage(messages.message_sending);\n } else if (this.props.received == Tinode.MESSAGE_STATUS_FAILED) {\n timestamp = formatMessage(messages.message_sending_failed);\n } else {\n timestamp = shortDateFormat(this.props.timestamp, this.props.intl.locale);\n }\n\n const icon = deliveryMarker(this.props.received);\n const marker = icon ? {icon.name} : null;\n\n return (\n \n {timestamp}{'\\u00a0'}{marker}\n \n );\n }\n};\n\nexport default injectIntl(ReceivedMarker);\n","// The counter of unread messages in the topic.\nimport React from 'react';\n\nexport default class UnreadBadge extends React.PureComponent {\n render() {\n return (this.props.count > 0 ?\n {this.props.count > 9 ? \"9+\" : this.props.count}\n : null);\n }\n};\n","// A single topic or user.\nimport React from 'react';\nimport { defineMessages, FormattedMessage, injectIntl } from 'react-intl';\n\nimport ContactBadges from './contact-badges.jsx';\nimport LetterTile from './letter-tile.jsx';\nimport ReceivedMarker from './received-marker.jsx'\nimport UnreadBadge from './unread-badge.jsx';\n\nimport { Drafty } from 'tinode-sdk';\n\nimport { deliveryMarker } from '../lib/utils.js';\n\nconst messages = defineMessages({\n drafty_image: {\n id: 'drafty_image',\n defaultMessage: 'Picture',\n description: 'Comment for embedded images in drafty preview'\n },\n drafty_form: {\n id: 'drafty_form',\n defaultMessage: 'Form: ',\n description: 'Comment for form in drafty preview'\n },\n drafty_attachment: {\n id: 'drafty_attachment',\n defaultMessage: 'Attachment',\n description: 'Comment for attachment in drafty preview'\n },\n});\n\nclass Contact extends React.Component {\n constructor(props) {\n super(props);\n\n this.handleClick = this.handleClick.bind(this);\n this.handleContextClick = this.handleContextClick.bind(this);\n }\n\n handleClick(e) {\n e.preventDefault();\n e.stopPropagation();\n if (this.props.onSelected) {\n // Index this.props.index is used by GroupManager.\n this.props.onSelected(this.props.item, this.props.index);\n }\n }\n\n handleContextClick(e) {\n e.preventDefault();\n e.stopPropagation();\n this.props.showContextMenu({ topicName: this.props.item, y: e.pageY, x: e.pageX });\n }\n\n render() {\n let title = this.props.title;\n if (!title) {\n title = ;\n } else if (title.length > 30) {\n // FIXME: this is probably wrong for RTL languages.\n title = title.substring(0, 28) + '…';\n }\n const online = this.props.now ? 'online' : 'offline';\n const avatar = this.props.avatar ? this.props.avatar : true;\n const badges = this.props.badges ? this.props.badges.slice() : [];\n const icon_badges = [];\n if (this.props.isVerified) {\n icon_badges.push({icon: 'verified', color: 'verified-color'});\n }\n if (this.props.isStaff) {\n icon_badges.push({icon: 'staff', color: 'staff-color'});\n }\n if (this.props.isDangerous) {\n icon_badges.push({icon: 'dangerous', color: 'danger-color'});\n }\n if (this.props.acs) {\n if (this.props.showMode) {\n badges.push({name: this.props.acs.getMode(), key: 'mode'});\n }\n if (this.props.acs.isMuted()) {\n icon_badges.push({icon: 'muted'});\n }\n if (!this.props.acs.isJoiner()) {\n icon_badges.push({icon: 'banned'});\n }\n }\n\n const subtitle = this.props.preview ?\n (typeof this.props.preview == 'string' ? this.props.preview :\n React.createElement(React.Fragment, null, Drafty.format(this.props.preview, draftyFormatter, this))) :\n this.props.comment;\n\n const icon = deliveryMarker(this.props.received);\n const marker = icon ? {icon.name} : null;\n\n return (\n \n \n \n {this.props.showOnline ? :\n (this.props.showCheckmark && this.props.selected ?\n check_circle\n : null)}\n
\n \n
{title}\n {this.props.isChannel ?

: null}\n
\n
\n {this.props.showMode ?\n
:\n
{marker}{subtitle || '\\u00A0'}
}\n
\n {this.props.showContextMenu ?\n \n \n expand_more\n \n : null}\n \n );\n }\n};\n\n\n// Converts Drafty object into a one-line preview.\nfunction draftyFormatter(style, data, values, key) {\n let el = Drafty.tagName(style);\n const attr = { key: key };\n if (el) {\n const { formatMessage } = this.props.intl;\n switch (style) {\n case 'BR':\n // Replace new line with a space.\n el = React.Fragment;\n values = [' '];\n break;\n case 'HL':\n // Make highlight less prominent in preview.\n attr.className = 'highlight preview';\n break;\n case 'LN':\n // Disable links in previews.\n el = 'span';\n break;\n case 'IM':\n // Replace image with '[icon] Image'.\n el = React.Fragment;\n values = [photo, formatMessage(messages.drafty_image)];\n break;\n case 'BN':\n el = 'span';\n attr.className = 'flat-button faux';\n break;\n case 'FM':\n el = React.Fragment;\n values = [dashboard,\n formatMessage(messages.drafty_form)].concat(values || []);\n break;\n case 'RW':\n el = React.Fragment;\n break;\n case 'EX':\n if (data && data.mime == 'application/json') {\n // Ignore JSON attachments: they are form response payloads.\n return null;\n }\n el = React.Fragment;\n values = [attachment, formatMessage(messages.drafty_attachment)];\n break;\n default:\n if (el == '_UNKN') {\n el = React.Fragment;\n values = [extension];\n }\n break;\n }\n return React.createElement(el, attr, values);\n } else {\n return values;\n }\n};\n\nexport default injectIntl(Contact);\n","// A panel included into a list of contacts with an action text.\nimport React from 'react';\nimport { injectIntl } from 'react-intl';\n\nclass ContactAction extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleClick = this.handleClick.bind(this);\n }\n\n handleClick(e) {\n e.preventDefault();\n e.stopPropagation();\n this.props.onAction(this.props.action);\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n return (\n \n {formatMessage(this.props.title, this.props.values)}
\n \n );\n }\n};\n\nexport default injectIntl(ContactAction);\n","// ContactList: component for showing a list of contacts,\n// such as a list of group members in a group chat.\nimport React from 'react';\nimport { defineMessages, injectIntl } from 'react-intl';\n\nimport { Drafty } from 'tinode-sdk';\nimport Tinode from 'tinode-sdk';\n\nimport Contact from './contact.jsx';\nimport ContactAction from './contact-action.jsx';\n\nimport { makeImageDataUrl } from '../lib/blob-helpers.js';\n\nimport { MESSAGE_PREVIEW_LENGTH } from '../config.js';\n\nconst messages = defineMessages({\n badge_you: {\n id: 'badge_you',\n defaultMessage: 'you',\n description: 'Badge for indicating the current user'\n },\n badge_owner: {\n id: 'badge_owner',\n defaultMessage: 'owner',\n description: 'Badge for indicating the owner'\n }\n});\n\nclass ContactList extends React.Component {\n render() {\n const { formatMessage } = this.props.intl;\n const showCheckmark = Array.isArray(this.props.topicSelected);\n const contactNodes = [];\n let contactsCount = 0;\n if (this.props.contacts && this.props.contacts.length > 0) {\n this.props.contacts.map((c) => {\n if (c.action) {\n // Action item\n contactNodes.push(\n );\n } else {\n // Normal contact\n const key = this.props.showMode ? c.user : (c.topic || c.user);\n // If filter function is provided, filter out the items\n // which don't satisfy the condition.\n if (this.props.filterFunc && this.props.filter) {\n const filterOn = [key];\n if (c.private && c.private.comment) {\n filterOn.push(('' + c.private.comment).toLowerCase());\n }\n if (c.public && c.public.fn) {\n filterOn.push(('' + c.public.fn).toLowerCase());\n }\n if (!this.props.filterFunc(this.props.filter, filterOn)) {\n return;\n }\n }\n\n const isChannel = Tinode.isChannelTopicName(key);\n const selected = showCheckmark ?\n (this.props.topicSelected.indexOf(key) > -1) :\n (this.props.topicSelected === key);\n const badges = [];\n if (this.props.showMode) {\n if (key == this.props.myUserId) {\n badges.push({name: formatMessage(messages.badge_you), color: 'green'});\n }\n if (c.acs && c.acs.isOwner()) {\n badges.push({name: formatMessage(messages.badge_owner), color: 'blue'});\n }\n }\n\n const comment = Array.isArray(c.private) ?\n c.private.join(',') : (c.private ? c.private.comment : null);\n let preview;\n let deliveryStatus;\n if (!this.props.showMode && c.latestMessage) {\n const msg = c.latestMessage(true);\n if (msg) {\n deliveryStatus = msg._status || c.msgStatus(msg, true);\n preview = typeof msg.content == 'string' ?\n msg.content.substr(0, MESSAGE_PREVIEW_LENGTH) :\n Drafty.preview(msg.content, MESSAGE_PREVIEW_LENGTH);\n }\n }\n contactNodes.push(\n \n );\n contactsCount ++;\n }\n }, this);\n }\n\n return (\n \n {contactsCount == 0 ?\n
\n :\n null\n }\n {contactNodes.length > 0 ?\n
\n :\n null\n }\n
\n );\n }\n};\n\nexport default injectIntl(ContactList);\n","// The X menu to be displayed in title bars.\nimport React from 'react';\n\nexport default class MenuCancel extends React.PureComponent {\n render() {\n return (\n {e.preventDefault(); this.props.onCancel();}}>\n close\n \n );\n }\n}\n","import React from 'react';\n\nimport MenuCancel from './menu-cancel.jsx';\n\nexport default class ErrorPanel extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n show: false,\n };\n\n this.hide = this.hide.bind(this);\n }\n\n componentDidUpdate(prevProps) {\n if (prevProps.level !== this.props.level) {\n this.setState({\n show: !(!this.props.level)\n });\n }\n }\n\n hide() {\n this.setState({show: false});\n if (this.props.onClearError) {\n this.props.onClearError();\n }\n }\n\n render() {\n const icons = {err: 'error', warn: 'warning', info: 'info'}\n const level = icons[this.props.level] || '';\n const className = 'info-box ' + level;\n return (\n \n );\n }\n};\n","import React from 'react';\n\nimport LetterTile from './letter-tile.jsx';\n\nexport default class Chip extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n handleCancel(e) {\n e.preventDefault();\n this.props.onCancel(this.props.topic, this.props.index);\n }\n\n render() {\n const title = this.props.title || this.props.topic;\n const className = this.props.invalid ? 'chip invalid' : 'chip';\n return (\n \n {this.props.noAvatar ?\n
:\n
\n \n
\n }\n
{title}\n {this.props.onCancel && !this.props.required ?\n
×\n :
}\n
\n );\n }\n};\n","import React from 'react';\n\nimport Chip from './chip.jsx';\n\nimport { makeImageDataUrl } from '../lib/blob-helpers.js';\n\n/* BEGIN ChipInput: group membership widget */\nexport default class ChipInput extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = ChipInput.deriveStateFromProps(props);\n this.state.input = '';\n this.state.focused = false;\n\n this.handleTextInput = this.handleTextInput.bind(this);\n this.removeChipAt = this.removeChipAt.bind(this);\n this.handleChipCancel = this.handleChipCancel.bind(this);\n this.handleFocusGained = this.handleFocusGained.bind(this);\n this.handleFocusLost = this.handleFocusLost.bind(this);\n this.handleKeyDown = this.handleKeyDown.bind(this);\n }\n\n static deriveStateFromProps(props) {\n return {\n placeholder: props.chips ? '' : props.prompt,\n sortedChips: ChipInput.sortChips(props.chips, props.staticMembers),\n chipIndex: ChipInput.indexChips(props.chips)\n };\n }\n\n componentDidUpdate(prevProps, prevState) {\n if (prevProps.chips != this.props.chips ||\n prevProps.staticMembers != this.props.staticMembers ||\n prevProps.prompt != this.props.prompt) {\n this.setState(ChipInput.deriveStateFromProps(this.props));\n }\n if (!prevState || this.props.chips.length > prevState.sortedChips.length) {\n this.setState({input: ''});\n }\n }\n\n // Map chip index to user name\n static indexChips(chips) {\n const index = {};\n let count = 0;\n chips.map((item) => {\n index[item.user] = count;\n count ++;\n });\n return index;\n }\n\n // Have non-removable chips appear before all other chips.\n static sortChips(chips, keep) {\n const required = [];\n const normal = [];\n chips.map((item) => {\n if (keep && keep.includes(item.user)) {\n required.push(item);\n } else {\n normal.push(item);\n }\n });\n return required.concat(normal);\n }\n\n handleTextInput(e) {\n this.setState({input: e.target.value});\n if (this.props.filterFunc) {\n this.props.filterFunc(e.target.value);\n }\n }\n\n removeChipAt(idx) {\n const removed = this.state.sortedChips[idx];\n this.props.onChipRemoved(removed.user, this.state.chipIndex[removed.user]);\n }\n\n handleChipCancel(item, idx) {\n this.removeChipAt(idx);\n }\n\n handleFocusGained() {\n this.setState({focused: true});\n }\n\n handleFocusLost() {\n this.setState({focused: false});\n if (this.props.onFocusLost) {\n this.props.onFocusLost(this.state.input);\n }\n }\n\n handleKeyDown(e) {\n if (e.key === 'Backspace') {\n if (this.state.input.length == 0 && this.state.sortedChips.length > 0) {\n const at = this.state.sortedChips.length - 1;\n if (this.state.sortedChips[at].user !== this.props.staticMembers) {\n this.removeChipAt(at);\n }\n }\n } else if (e.key === 'Enter') {\n if (this.props.onEnter) {\n this.props.onEnter(this.state.input);\n }\n } else if (e.key === 'Escape') {\n if (this.props.onCancel) {\n this.props.onCancel();\n }\n }\n }\n\n render() {\n const chips = [];\n let count = 0;\n const staticMembers = this.props.staticMembers || [];\n this.state.sortedChips.map((item) => {\n chips.push(\n \n );\n count++;\n });\n const className = \"chip-input\" + (this.state.focused ? ' focused' : '');\n return (\n \n {chips}\n \n
\n );\n }\n};\n","// GroupMembers: control for managing a list of group members.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport ChipInput from './chip-input.jsx';\nimport ContactList from './contact-list.jsx';\n\nconst messages = defineMessages({\n no_contacts: {\n id: 'no_contacts',\n defaultMessage: 'You have no contacts :-(',\n description: 'Shown in ContactsView when the user has no contacts'\n },\n contacts_not_found_short: {\n id: 'contacts_not_found_short',\n defaultMessage: \"No contacts match ''{query}''\",\n description: 'Shown in ContactsView when search returned no results'\n }\n});\n\nclass GroupManager extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n // Array of topic members\n members: props.members,\n index: GroupManager.indexMembers(props.members),\n staticMembers: GroupManager.staticMembers(props.members, props.keepInitialMembers, props.requiredMember),\n contactFilter: '',\n noContactsMessage: props.intl.formatMessage(messages.no_contacts),\n selectedContacts: GroupManager.selectedContacts(props.members)\n };\n\n this.handleContactSelected = this.handleContactSelected.bind(this);\n this.handleMemberRemoved = this.handleMemberRemoved.bind(this);\n this.handleContactFilter = this.handleContactFilter.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n static indexMembers(members) {\n let index = {};\n members.map((m) => {\n index[m.user] = {delta: 0, present: true}; // Delta: 0 unchanged, +1 added, -1 removed\n });\n return index;\n }\n\n static staticMembers(members, keepInitial, requiredMember) {\n let stat = [];\n members.map((m) => {\n if (keepInitial || m.user == requiredMember) {\n stat.push(m.user);\n }\n });\n return stat;\n }\n\n static selectedContacts(members) {\n let sel = [];\n members.map((m) => {\n sel.push(m.user);\n });\n return sel;\n }\n\n handleContactSelected(userId, index) {\n let status = this.state.index[userId];\n if (status) {\n if (status.present) {\n // Prevent duplicate members\n return;\n }\n status.delta += 1;\n status.present = true;\n } else {\n status = {delta: 1, present: true};\n }\n\n let m = this.state.members.slice();\n m.push(this.props.contacts[index]);\n\n const sel = GroupManager.selectedContacts(m);\n\n const i = this.state.index;\n i[userId] = status;\n\n this.setState({members: m, index: i, selectedContacts: sel});\n }\n\n handleMemberRemoved(userId, index) {\n const status = this.state.index[userId];\n if (!status || !status.present) {\n return;\n }\n status.present = false;\n status.delta -= 1;\n\n let m = this.state.members.slice();\n m.splice(index, 1);\n\n const sel = GroupManager.selectedContacts(m);\n\n const i = this.state.index;\n i[userId] = status;\n\n this.setState({members: m, index: i, selectedContacts: sel});\n }\n\n handleContactFilter(val) {\n const {formatMessage} = this.props.intl;\n const msg = !val ?\n formatMessage(messages.no_contacts) :\n formatMessage(messages.contacts_not_found_short, {query: val});\n\n this.setState({contactFilter: val, noContactsMessage: msg});\n }\n\n static doContactFiltering(filter, values) {\n if (filter) {\n for (let i=0; i= 0) {\n return true;\n }\n }\n return false;\n }\n return true;\n }\n\n handleSubmit() {\n var instance = this;\n var members = [];\n var added = [];\n var removed = [];\n\n var keys = Object.keys(this.state.index);\n keys.map(function(k) {\n if (instance.state.index[k].present) {\n members.push(k);\n }\n\n if (instance.state.index[k].delta > 0) {\n added.push(k);\n } else if (instance.state.index[k].delta < 0) {\n removed.push(k);\n }\n });\n this.props.onSubmit(members, added, removed);\n }\n\n handleCancel() {\n this.props.onCancel();\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n return (\n \n
\n \n
\n
\n \n
\n
\n \n
\n
\n
\n \n \n
\n
\n );\n }\n};\n\nexport default injectIntl(GroupManager);\n","// Password with a visiblity toggle.\nimport React from 'react';\n\nexport default class VisiblePassword extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n value: this.props.value,\n visible: false\n };\n\n this.handleVisibility = this.handleVisibility.bind(this);\n this.handeTextChange = this.handeTextChange.bind(this);\n this.handleKeyDown = this.handleKeyDown.bind(this);\n this.handleEditingFinished = this.handleEditingFinished.bind(this);\n }\n\n handeTextChange(e) {\n this.setState({value: e.target.value});\n if (this.props.onChange) {\n this.props.onChange(e);\n }\n }\n\n handleVisibility(e) {\n e.preventDefault();\n this.setState({visible: !this.state.visible});\n }\n\n handleKeyDown(e) {\n if (e.keyCode == 27) {\n // Escape pressed\n this.setState({value: this.props.value, visible: false});\n if (this.props.onFinished) {\n this.props.onFinished();\n }\n } else if (e.keyCode == 13) {\n // Enter pressed\n this.handleEditingFinished();\n }\n }\n\n handleEditingFinished(e) {\n if (e) {\n let currentTarget = e.currentTarget;\n setTimeout(() => {\n if (!currentTarget.contains(document.activeElement)) {\n if (this.props.onFinished) {\n this.props.onFinished(this.state.value);\n }\n }\n }, 0);\n } else if (this.props.onFinished) {\n this.props.onFinished(this.state.value.trim());\n }\n }\n\n render() {\n return (\n \n \n \n \n {this.state.visible ? 'visibility' : 'visibility_off'}\n \n \n
\n );\n }\n}\n","// In-place text editor. Shows text with an icon which toggles it into an input field.\nimport React from 'react';\nimport ReactDOM from 'react-dom';\n\nimport VisiblePassword from './visible-password.jsx';\n\nexport default class InPlaceEdit extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n active: props.active,\n initialValue: props.value || '',\n value: props.value || ''\n };\n\n this.handeTextChange = this.handeTextChange.bind(this);\n this.handleKeyDown = this.handleKeyDown.bind(this);\n this.handleStartEditing = this.handleStartEditing.bind(this);\n this.handleEditingFinished = this.handleEditingFinished.bind(this);\n this.handlePasswordFinished = this.handlePasswordFinished.bind(this);\n }\n\n componentDidUpdate(prevProps, prevState) {\n // If text has changed while in read mode, update text and discard changes.\n // Ignore update if in edit mode.\n const newValue = this.props.value || '';\n if (prevState.initialValue != newValue && !prevState.active) {\n this.setState({\n initialValue: newValue,\n value: newValue\n });\n }\n }\n\n handeTextChange(e) {\n this.setState({value: e.target.value});\n }\n\n handleKeyDown(e) {\n if (e.keyCode === 27) {\n // Escape pressed\n this.setState({value: this.props.value, active: false});\n } else if (e.keyCode === 13) {\n // Enter pressed\n this.handleEditingFinished(e);\n }\n }\n\n handleStartEditing() {\n if (!this.props.readOnly) {\n ReactDOM.findDOMNode(this).focus();\n this.setState({active: true});\n }\n }\n\n handleEditingFinished(event) {\n if (this.props.required && !event.target.checkValidity()) {\n // Empty input\n this.setState({value: this.props.value, active: false});\n return;\n }\n this.setState({active: false});\n let value = this.state.value.trim();\n if ((value || this.props.value) && (value !== this.props.value)) {\n this.props.onFinished(value);\n }\n }\n\n handlePasswordFinished(value) {\n this.setState({active: false});\n if (value && (value !== this.props.value)) {\n this.props.onFinished(value);\n }\n }\n\n render() {\n if (this.state.active) {\n var fieldType = this.props.type || 'text';\n } else {\n var spanText = this.props.type == 'password' ? '••••••••' : this.state.value;\n var spanClass = 'in-place-edit' +\n (this.props.readOnly ? ' disabled' : '');\n if (!spanText) {\n spanText = this.props.placeholder;\n spanClass += ' placeholder';\n }\n if (spanText.length > 20) {\n // FIXME: this is wrong for RTL languages.\n spanText = spanText.substring(0, 19) + '...';\n }\n }\n return (\n this.state.active ?\n (fieldType == 'password' ?\n \n :\n \n )\n :\n \n {spanText}\n \n );\n }\n};\n","// Toggle [Title text >] -> [Title text v]\n\nimport React from 'react';\n\nexport default class MoreButton extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n open: props.open\n };\n this.handleToggle = this.handleToggle.bind(this);\n }\n\n handleToggle() {\n const open = !this.state.open;\n this.setState({open: open});\n if (this.props.onToggle) {\n this.props.onToggle(open);\n }\n }\n\n render() {\n return ();\n }\n}\n","// PermissionsEditor: Component for editing permissions\n// \nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport CheckBox from './checkbox.jsx';\nimport Contact from './contact.jsx';\n\nimport { makeImageDataUrl } from '../lib/blob-helpers.js';\n\n// Translatable permission names.\nconst messages = defineMessages({\n joiner: {\n id: 'permission_join',\n defaultMessage: \"Join ({val})\",\n description: 'Name of J permission'\n },\n reader: {\n id: 'permission_read',\n defaultMessage: \"Read ({val})\",\n description: 'Name of R permission'\n },\n writer: {\n id: 'permission_write',\n defaultMessage: \"Write ({val})\",\n description: 'Name of W permission'\n },\n preser: {\n id: 'permission_pres',\n defaultMessage: \"Get notified ({val})\",\n description: 'Name of P permission'\n },\n approver: {\n id: 'permission_admin',\n defaultMessage: \"Approve ({val})\",\n description: 'Name of A permission'\n },\n sharer: {\n id: 'permission_share',\n defaultMessage: \"Share ({val})\",\n description: 'Name of S permission'\n },\n deleter: {\n id: 'permission_delete',\n defaultMessage: \"Delete ({val})\",\n description: 'Name of D permission'\n },\n owner: {\n id: 'permission_owner',\n defaultMessage: \"Owner ({val})\",\n description: 'Name of O permission'\n }\n});\n\nclass PermissionsEditor extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n mode: (props.mode || '').replace('N', '')\n };\n\n this.handleChange = this.handleChange.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n handleChange(val) {\n let mode = this.state.mode;\n let idx = mode.indexOf(val);\n if (idx == -1) {\n mode += val;\n } else {\n mode = mode.replace(val, '');\n }\n this.setState({mode: mode});\n }\n\n handleSubmit() {\n // Normalize string, otherwise cannot check if mode has changed.\n var mode = (this.state.mode || 'N').split('').sort().join('');\n var before = (this.props.mode || 'N').split('').sort().join('')\n if (mode !== before) {\n this.props.onSubmit(mode);\n } else {\n this.props.onCancel();\n }\n }\n\n handleCancel() {\n this.props.onCancel();\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n const all = 'JRWPASDO';\n const names = {\n 'J': formatMessage(messages.joiner, {val: 'J'}),\n 'R': formatMessage(messages.reader, {val: 'R'}),\n 'W': formatMessage(messages.writer, {val: 'W'}),\n 'P': formatMessage(messages.preser, {val: 'P'}),\n 'A': formatMessage(messages.approver, {val: 'A'}),\n 'S': formatMessage(messages.sharer, {val: 'S'}),\n 'D': formatMessage(messages.deleter, {val: 'D'}),\n 'O': formatMessage(messages.owner, {val: 'O'})\n };\n\n let skip = this.props.skip || '';\n let mode = this.state.mode;\n let compare = (this.props.compare || '').replace('N', '');\n let items = [];\n for (let i=0; i= 0 && mode.indexOf(c) < 0) {\n // Permission is marked as inactive: hide unchecked permissions, disable checked permissions\n continue;\n }\n items.push(\n \n | {names[c]} | \n {skip.indexOf(c) < 0 ?\n = 0)} onChange={this.handleChange}/>\n :\n = 0)} />\n } | {this.props.compare ? \n = 0)}/>\n | : null}\n
\n );\n }\n\n return (\n \n {this.props.userTitle ?\n
: null}\n
\n
\n {this.props.compare ?\n \n | {this.props.modeTitle} | \n {this.props.compareTitle} | \n
:\n null}\n \n {items}\n
\n
\n
\n \n \n
\n
\n );\n }\n};\n\nexport default injectIntl(PermissionsEditor);\n","// TagManager: edit topic or user tags.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport ChipInput from './chip-input.jsx';\n\nimport { MAX_TAG_COUNT, MAX_TAG_LENGTH, MIN_TAG_LENGTH } from '../config.js';\nimport { arrayEqual } from '../lib/utils.js';\n\nexport default class TagManager extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n tags: this.props.tags,\n tagInput: '',\n activated: this.props.activated\n };\n\n this.handleTagInput = this.handleTagInput.bind(this);\n this.handleAddTag = this.handleAddTag.bind(this);\n this.handleRemoveTag = this.handleRemoveTag.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n static getDerivedStateFromProps(nextProps, prevState) {\n if (!arrayEqual(nextProps.tags, prevState.tags) && !prevState.activated) {\n return {tags: nextProps.tags};\n }\n return null;\n }\n\n handleTagInput(text) {\n this.setState({tagInput: text});\n if (text.length > 0) {\n const last = text[text.length-1];\n if (text[0] == '\"') {\n // This is a quoted string.\n if (text.length > 1 && last == '\"') {\n this.handleAddTag(text.substring(1, text.length-1));\n }\n } else if (last == ',' || last == ' ' || last == ';' || last == '\"') {\n // User entered ',', ' ' or ';'\n this.handleAddTag(text.substring(0, text.length-1).trim());\n }\n }\n }\n\n handleAddTag(tag) {\n const maxTagCount = this.props.tinode.getServerLimit('maxTagCount', MAX_TAG_COUNT);\n\n if (tag.length > 0 && this.state.tags.length < maxTagCount) {\n const tags = this.state.tags.slice(0);\n tags.push(tag);\n\n this.setState({tags: tags, tagInput: ''});\n if (this.props.onTagsChanged) {\n this.props.onTagsChanged(tags);\n }\n return tags;\n }\n return this.state.tags;\n }\n\n handleRemoveTag(tag, index) {\n const tags = this.state.tags.slice(0);\n tags.splice(index, 1);\n this.setState({tags: tags});\n if (this.props.onTagsChanged) {\n this.props.onTagsChanged(tags);\n }\n }\n\n handleSubmit() {\n // Add unprocessed input to tags, submit the list.\n this.props.onSubmit(this.handleAddTag(this.state.tagInput.trim()));\n this.setState({activated: false, tags: this.props.tags});\n }\n\n handleCancel() {\n this.setState({activated: false, tagInput: '', tags: this.props.tags});\n if (this.props.onCancel) {\n this.props.onCancel();\n }\n }\n\n render() {\n const minTagLength = this.props.tinode.getServerLimit('minTagLength', MIN_TAG_LENGTH);\n const maxTagLength = this.props.tinode.getServerLimit('maxTagLength', MAX_TAG_LENGTH);\n\n let tags = [];\n if (this.state.activated) {\n this.state.tags.map((tag) => {\n tags.push({user: tag, invalid: (tag.length < minTagLength || tag.length > maxTagLength)});\n });\n } else {\n this.state.tags.map((tag) => {\n tags.push({tag});\n });\n if (tags.length == 0) {\n tags = (\n \n \n \n );\n }\n }\n return (\n \n
\n \n
\n {this.state.activated ?\n
\n
{\n (add_tags_prompt) => \n }\n {this.props.onSubmit || this.props.onCancel ?\n
\n \n \n
\n : null}\n
\n :\n
\n }\n
\n );\n }\n};\n","// InfoView: panel with topic/user info.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport AvatarUpload from '../widgets/avatar-upload.jsx';\nimport BadgeList from '../widgets/badge-list.jsx';\nimport CheckBox from '../widgets/checkbox.jsx';\nimport ContactList from '../widgets/contact-list.jsx';\nimport ErrorPanel from '../widgets/error-panel.jsx';\nimport GroupManager from '../widgets/group-manager.jsx';\nimport InPlaceEdit from '../widgets/in-place-edit.jsx';\nimport MenuCancel from '../widgets/menu-cancel.jsx';\nimport MoreButton from '../widgets/more-button.jsx';\nimport PermissionsEditor from '../widgets/permissions-editor.jsx';\nimport TagManager from '../widgets/tag-manager.jsx';\n\nimport { MAX_TITLE_LENGTH, NO_ACCESS_MODE } from '../config.js';\n\nimport { makeImageDataUrl } from '../lib/blob-helpers.js';\nimport { arrayEqual, theCard } from '../lib/utils.js';\n\nconst messages = defineMessages({\n requested: {\n id: 'requested_permissions',\n defaultMessage: 'Requested',\n description: 'Title for permissions'\n },\n granted: {\n id: 'granted_permissions',\n defaultMessage: 'Granted',\n description: 'Title for permissions'\n },\n edit_permissions: {\n id: 'menu_item_edit_permissions',\n defaultMessage: 'Edit permissions',\n description: 'Menu item [Edit permissions]'\n },\n other_user: {\n id: 'label_other_user',\n defaultMessage: 'Other',\n description: 'Label for the other user when the user is unnamed'\n },\n clear_messages: {\n id: 'action_clear_messages',\n defaultMessage: 'Clear Messages',\n description: 'Flat button [Clear Messages] (soft-delete messages)'\n },\n clear_messages_warning: {\n id: 'clear_messages_warning',\n defaultMessage: 'Are you sure you want to clear all messages? It cannot be undone.',\n description: 'Alert dialog warning when deleting all messages.'\n },\n delete_messages: {\n id: 'action_delete_messages',\n defaultMessage: 'Clear Messages for All',\n description: 'Flat button [Clear for All] (hard-delete all messages)'\n },\n delete_messages_warning: {\n id: 'delete_messages_warning',\n defaultMessage: 'Are you sure you want to delete all messages for everyone? It cannot be undone.',\n description: 'Alert dialog warning when hard-deleting all messages.'\n },\n leave_chat: {\n id: 'action_leave_chat',\n defaultMessage: 'Leave Conversation',\n description: 'Flat button [Leave Conversation]'\n },\n leave_chat_warning: {\n id: 'leave_chat_warning',\n defaultMessage: 'Are you sure you want to leave this conversation?',\n description: 'Alert dialog warning when unsubscribing from a chat.'\n },\n block_contact: {\n id: 'action_block_contact',\n defaultMessage: \"Block Contact\",\n description: \"Flat button [Block Contact]\"\n },\n block_contact_warning: {\n id: 'block_contact_warning',\n defaultMessage: 'Are you sure you want to block this contact?',\n description: 'Alert dialog warning when blocking a contact.'\n },\n report_chat: {\n id: 'action_report_chat',\n defaultMessage: 'Report Conversation',\n description: 'Flat button [Report Group]'\n },\n report_chat_warning: {\n id: 'report_chat_warning',\n defaultMessage: 'Are you sure you want to block and report this conversation?',\n description: 'Alert dialog warning when reporting a conversation for abuse'\n },\n});\n\nclass InfoView extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n topic: null,\n owner: false,\n admin: false,\n sharer: false,\n deleter: false,\n muted: false,\n address: null,\n groupTopic: undefined,\n channel: undefined,\n fullName: undefined,\n avatar: null,\n private: null,\n selectedContact: null,\n access: null,\n modeGiven: null,\n modeWant: null,\n modeGiven2: null, // P2P topic, the other user mode given\n modeWant2: null, // P2P topic, the other user mode want\n auth: null,\n anon: null,\n contactList: [],\n tags: [],\n trustedBadges: [],\n showMemberPanel: false,\n showPermissionEditorFor: undefined,\n moreInfoExpanded: false,\n previousMetaDesc: undefined,\n previousSubsUpdated: undefined,\n previousTagsUpdated: undefined\n };\n\n this.resetSubs = this.resetSubs.bind(this);\n this.resetDesc = this.resetDesc.bind(this);\n this.onMetaDesc = this.onMetaDesc.bind(this);\n this.onSubsUpdated = this.onSubsUpdated.bind(this);\n this.onTagsUpdated = this.onTagsUpdated.bind(this);\n this.handleFullNameUpdate = this.handleFullNameUpdate.bind(this);\n this.handlePrivateUpdate = this.handlePrivateUpdate.bind(this);\n this.handleImageChanged = this.handleImageChanged.bind(this);\n this.handleMuted = this.handleMuted.bind(this);\n this.handlePermissionsChanged = this.handlePermissionsChanged.bind(this);\n this.handleLaunchPermissionsEditor = this.handleLaunchPermissionsEditor.bind(this);\n this.handleHidePermissionsEditor = this.handleHidePermissionsEditor.bind(this);\n this.handleShowAddMembers = this.handleShowAddMembers.bind(this);\n this.handleHideAddMembers = this.handleHideAddMembers.bind(this);\n this.handleMemberUpdateRequest = this.handleMemberUpdateRequest.bind(this);\n this.handleDeleteMessages = this.handleDeleteMessages.bind(this);\n this.handleLeave = this.handleLeave.bind(this);\n this.handleBlock = this.handleBlock.bind(this);\n this.handleReport = this.handleReport.bind(this);\n this.handleMemberSelected = this.handleMemberSelected.bind(this);\n this.handleMoreInfo = this.handleMoreInfo.bind(this);\n this.handleTagsUpdated = this.handleTagsUpdated.bind(this);\n this.handleContextMenu = this.handleContextMenu.bind(this);\n }\n\n // No need to separately handle component mount.\n componentDidUpdate(props) {\n const topic = this.props.tinode.getTopic(props.topic);\n if (!topic) {\n return;\n }\n\n if (this.onMetaDesc != topic.onMetaDesc) {\n this.previousMetaDesc = topic.onMetaDesc;\n topic.onMetaDesc = this.onMetaDesc;\n\n this.previousSubsUpdated = topic.onSubsUpdated;\n topic.onSubsUpdated = this.onSubsUpdated;\n\n if (topic.getType() == 'grp') {\n this.previousTagsUpdated = topic.onTagsUpdated;\n topic.onTagsUpdated = this.onTagsUpdated;\n } else {\n this.previousTagsUpdated = undefined;\n }\n }\n\n if (this.state.topic != props.topic) {\n this.setState({topic: props.topic});\n this.resetDesc(topic, props);\n this.resetSubs(topic, props);\n }\n }\n\n componentWillUnmount() {\n const topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n this.setState({topic: null});\n topic.onMetaDesc = this.previousMetaDesc;\n topic.onSubsUpdated = this.previousSubsUpdated;\n topic.onTagsUpdated = this.previousTagsUpdated;\n }\n\n resetSubs(topic, props) {\n const newState = {contactList: []};\n if (topic.getType() == 'p2p') {\n // Fetch the other party in the p2p conversation.\n // Topic may not be ready yet, so check if user is found.\n const user2 = topic.subscriber(props.topic);\n if (user2) {\n newState.modeGiven2 = user2.acs.getGiven();\n newState.modeWant2 = user2.acs.getWant();\n } else {\n newState.modeGiven2 = NO_ACCESS_MODE;\n newState.modeWant2 = NO_ACCESS_MODE;\n }\n } else {\n topic.subscribers((sub) => {\n newState.contactList.push(sub);\n }, this);\n }\n\n this.setState(newState);\n }\n\n resetDesc(topic, props) {\n const defacs = topic.getDefaultAccess() || {};\n const acs = topic.getAccessMode();\n\n const badges = [];\n if (topic.trusted) {\n for (const [key, val] of Object.entries(topic.trusted)) {\n if (val) {\n badges.push(key);\n }\n }\n }\n this.setState({\n owner: acs && acs.isOwner(),\n admin: acs && acs.isAdmin(),\n sharer: acs && acs.isSharer(),\n deleter: acs && acs.isDeleter(),\n muted: acs && acs.isMuted(),\n\n fullName: topic.public ? topic.public.fn : undefined,\n avatar: makeImageDataUrl(topic.public ? topic.public.photo : null),\n trustedBadges: badges,\n private: topic.private ? topic.private.comment : null,\n address: topic.name,\n groupTopic: topic.isGroupType(),\n channel: topic.isChannelType(),\n showMemberPanel: false,\n access: acs ? acs.getMode() : undefined,\n modeGiven: acs ? acs.getGiven() : undefined,\n modeWant: acs ? acs.getWant() : undefined,\n auth: defacs.auth,\n anon: defacs.anon\n });\n\n if (topic.getType() == 'grp' && acs && acs.isOwner()) {\n // Requesting tags: owner is editing the topic.\n topic.getMeta(topic.startMetaQuery().withTags().build());\n }\n }\n\n onMetaDesc(desc) {\n const topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n this.resetDesc(topic, this.props);\n\n if (this.previousMetaDesc && this.previousMetaDesc != this.onMetaDesc) {\n this.previousMetaDesc(desc);\n }\n }\n\n onSubsUpdated(subs) {\n const topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n this.resetSubs(topic, this.props);\n\n if (this.previousSubsUpdated && this.previousSubsUpdated != this.onSubsUpdated) {\n this.previousSubsUpdated(subs);\n }\n }\n\n onTagsUpdated(tags) {\n this.setState({tags: tags});\n\n if (this.previousTagsUpdated && this.previousTagsUpdated != this.onTagsUpdated) {\n this.previousTagsUpdated();\n }\n }\n\n handleFullNameUpdate(fn) {\n fn = fn.trim().substring(0, MAX_TITLE_LENGTH);\n if (this.state.fullName !== fn) {\n this.setState({fullName: fn});\n this.props.onTopicDescUpdate(this.props.topic, theCard(fn, null), null);\n }\n }\n\n handlePrivateUpdate(comment) {\n comment = comment.trim().substring(0, MAX_TITLE_LENGTH);\n if (this.state.private !== comment) {\n this.setState({private: comment});\n this.props.onTopicDescUpdate(this.props.topic, null, comment || Tinode.DEL_CHAR);\n }\n }\n\n handleImageChanged(img) {\n this.setState({avatar: img});\n this.props.onTopicDescUpdate(this.props.topic, theCard(null, img || Tinode.DEL_CHAR), null);\n }\n\n handleMuted(ignored, checked) {\n this.setState({muted: checked});\n this.props.onChangePermissions(this.props.topic, checked ? '-P' : '+P');\n }\n\n handlePermissionsChanged(perm) {\n switch (this.state.showPermissionEditorFor) {\n case 'auth':\n this.props.onTopicDescUpdate(this.props.topic, null, null, {auth: perm});\n break;\n case 'anon':\n this.props.onTopicDescUpdate(this.props.topic, null, null, {anon: perm});\n break;\n case 'mode':\n case 'want':\n this.props.onChangePermissions(this.props.topic, perm);\n break;\n case 'given':\n this.props.onChangePermissions(this.props.topic, perm, this.props.topic);\n break;\n case 'user':\n this.props.onChangePermissions(this.props.topic, perm, this.state.userPermissionsEdited);\n break;\n }\n\n this.setState({showPermissionEditorFor: undefined});\n }\n\n handleLaunchPermissionsEditor(which, uid) {\n const {formatMessage} = this.props.intl;\n let toEdit, toCompare, toSkip, titleEdit, titleCompare, userTitle, userAvatar;\n switch (which) {\n case 'mode':\n toEdit = this.state.access;\n break;\n case 'want':\n toEdit = this.state.modeWant;\n toCompare = this.state.modeGiven;\n if (this.state.owner) {\n // Don't allow owner to unset 'O' permission. All other permissions are OK.\n toSkip = 'O';\n } else {\n // Allow accepting any of 'ASDO' permissions but don't allow asking for them.\n toSkip = Tinode.AccessMode.encode(Tinode.AccessMode.diff('ASDO', this.state.modeGiven));\n if (this.state.channel) {\n // Channels are read-only.\n toSkip += 'W';\n }\n }\n titleEdit = formatMessage(messages.requested);\n titleCompare = formatMessage(messages.granted);\n break;\n case 'given':\n toEdit = this.state.modeGiven2;\n toCompare = this.state.modeWant2;\n toSkip = this.state.groupTopic ? (this.state.owner ? '' : 'O') : 'ASDO';\n titleEdit = formatMessage(messages.granted);\n titleCompare = formatMessage(messages.requested);\n break;\n case 'auth':\n toEdit = this.state.auth;\n toSkip = 'O';\n break;\n case 'anon':\n toEdit = this.state.anon;\n toSkip = 'O';\n break;\n case 'user': {\n const topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n const user = topic.subscriber(uid);\n if (!user || !user.acs) {\n return;\n }\n toEdit = user.acs.getGiven();\n toCompare = user.acs.getWant();\n toSkip = this.state.owner ? '' : 'O';\n titleEdit = formatMessage(messages.granted);\n titleCompare = formatMessage(messages.requested);\n if (user.public) {\n userTitle = user.public.fn;\n userAvatar = user.public.photo;\n }\n break;\n }\n default:\n console.log(\"Unknown permission editing mode '\" + which + \"'\");\n break;\n }\n this.setState({\n showPermissionEditorFor: which,\n userPermissionsEdited: uid,\n userPermissionsTitle: userTitle,\n userPermissionsAvatar: userAvatar,\n editedPermissions: toEdit,\n immutablePermissions: toCompare,\n editedPermissionsTitle: titleEdit,\n immutablePermissionsTitle: titleCompare,\n editedPermissionsSkipped: toSkip,\n });\n }\n\n handleHidePermissionsEditor() {\n this.setState({showPermissionEditorFor: undefined});\n }\n\n handleShowAddMembers(e) {\n e.preventDefault();\n this.props.onInitFind();\n this.setState({showMemberPanel: true});\n }\n\n handleHideAddMembers() {\n this.setState({showMemberPanel: false});\n }\n\n handleMemberUpdateRequest(members, added, removed) {\n this.props.onMemberUpdateRequest(this.props.topic, added, removed);\n this.setState({showMemberPanel: false});\n }\n\n handleDeleteMessages(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(this.state.deleter ? messages.delete_messages : messages.clear_messages), // title\n formatMessage(this.state.deleter ? messages.delete_messages_warning : messages.clear_messages_warning), // content\n (() => { this.props.onDeleteMessages(this.props.topic); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n\n handleLeave(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(messages.leave_chat), // title\n formatMessage(messages.leave_chat_warning), // content\n (() => { this.props.onLeaveTopic(this.props.topic); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n\n handleBlock(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(messages.block_contact), // title\n formatMessage(messages.block_contact_warning), // content\n (() => { this.props.onBlockTopic(this.props.topic); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n\n handleReport(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(messages.report_chat), // title\n formatMessage(messages.report_chat_warning), // content\n (() => { this.props.onReportTopic(this.props.topic); }), // onConfirm\n null, // \"OK\"\n true, // Show Reject button\n null // \"Cancel\"\n );\n }\n\n handleMemberSelected(uid) {\n this.setState({selectedContact: uid});\n }\n\n handleMoreInfo(open) {\n this.setState({moreInfoExpanded: open});\n }\n\n handleTagsUpdated(tags) {\n if (!arrayEqual(this.state.tags.slice(0), tags.slice(0))) {\n this.props.onTopicTagsUpdate(this.props.topic, tags);\n }\n }\n\n handleContextMenu(params) {\n const {formatMessage} = this.props.intl;\n const instance = this;\n const topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n const user = topic.subscriber(params.topicName);\n if (!user || !user.acs) {\n return;\n }\n\n const menuItems = [\n {title: formatMessage(messages.edit_permissions), handler: function() {\n instance.handleLaunchPermissionsEditor('user', params.topicName);\n }},\n 'member_delete',\n user.acs.isMuted() ? 'member_unmute' : 'member_mute',\n user.acs.isJoiner() ? 'member_block' : 'member_unblock'\n ];\n this.props.showContextMenu({\n topicName: this.props.topic,\n x: params.x,\n y: params.y,\n user: params.topicName}, menuItems);\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n\n return (\n \n
\n {this.props.displayMobile ?\n
: null}\n {this.state.showMemberPanel ?\n
\n :\n this.state.showPermissionEditorFor ?\n
\n :\n
\n
\n
\n
\n
Unknown}\n readOnly={!this.state.owner}\n value={this.state.fullName}\n required={true}\n onFinished={this.handleFullNameUpdate} />
\n
\n \n
\n
\n {\n (private_placeholder) => \n }\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n
\n
{\n (more) => \n }\n {this.state.moreInfoExpanded ?\n
\n
\n \n {this.state.address}\n
\n {this.state.groupTopic ?\n
\n \n \n {this.state.access}\n \n
\n :\n
\n
\n \n
\n
\n
\n \n {this.state.access}\n
\n
{this.state.fullName ? this.state.fullName : formatMessage(messages.other_user)}:\n \n {this.state.modeGiven2}\n \n
\n
\n
\n }\n {this.state.sharer && (this.state.auth || this.state.anon) ?\n
\n
\n \n
\n
\n
Auth: {this.state.admin ?\n \n {this.state.auth}\n \n :\n {this.state.auth}\n }\n
\n
Anon: {this.state.admin ?\n \n {this.state.anon}\n \n :\n {this.state.anon}\n }\n
\n
\n
\n :\n null\n }\n
\n :\n null\n }\n
\n
\n {this.state.owner ?\n <>\n
{\n (tags) => \n }\n
\n >\n :\n null\n }\n
\n {this.state.groupTopic && this.state.sharer ?\n <>\n
\n
\n
\n \n
\n
\n
{\n (no_members) => \n }\n
\n >\n :\n null\n }\n
\n }\n
\n );\n }\n};\n\nexport default injectIntl(InfoView);\n","// File uload/download progress indicator with a cancel inside.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nexport default class FileProgress extends React.PureComponent {\n render() {\n return (\n \n );\n }\n}\n","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport FileProgress from './file-progress.jsx';\nimport { bytesToHumanSize } from '../lib/strformat.js';\nimport { isUrlRelative, sanitizeUrl } from '../lib/utils.js';\n\nexport default class Attachment extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n downloader: null,\n progress: 0\n };\n\n this.downloadFile = this.downloadFile.bind(this);\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n downloadFile(url, filename, mimetype) {\n if (!url) {\n this.props.onError(\"Invalid download URL '\" + url + \"'\");\n return;\n }\n\n const downloader = this.props.tinode.getLargeFileHelper();\n this.setState({downloader: downloader});\n downloader.download(url, filename, mimetype,\n (loaded) => {\n this.setState({progress: loaded / this.props.size});\n },\n (err) => {\n this.props.onError(err, 'err');\n }\n ).then(() => {\n this.setState({downloader: null, progress: 0});\n }).catch((err) => {\n if (err) {\n this.props.onError(\"Error downloading file: \" + err.message, 'err');\n }\n this.setState({downloader: null, progress: 0});\n });\n }\n\n handleCancel() {\n if (this.props.uploading) {\n this.props.onCancelUpload();\n } else if (this.state.downloader) {\n this.state.downloader.cancel();\n }\n }\n\n render() {\n let filename = this.props.filename || 'file_attachment';\n if (filename.length > 36) {\n filename = filename.substr(0, 16) + '...' + filename.substr(-16);\n }\n let size = this.props.size > 0 ?\n ({bytesToHumanSize(this.props.size)}) :\n null;\n\n // Detect if the download URL is relative or absolute.\n // If the URL is relative use LargeFileHelper to attach authentication\n // credentials to the request.\n let url, helperFunc;\n if (!this.props.uploading && !this.state.downloader && isUrlRelative(this.props.downloadUrl)) {\n // Relative URL. Use download helper.\n url = '#';\n helperFunc = (e) => {\n e.preventDefault();\n this.downloadFile(this.props.downloadUrl, this.props.filename, this.props.mimetype);\n };\n } else {\n url = sanitizeUrl(this.props.downloadUrl);\n helperFunc = null;\n }\n const downloadWidget = <>file_download >;\n return (\n \n
insert_drive_file
\n
\n
{filename} {size}
\n {this.props.uploading || this.state.downloader ?\n
\n :\n
\n }\n
\n
\n );\n }\n};\n","// Image view with progress bar and a cancel button.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport FileProgress from './file-progress.jsx';\n\nexport default class UploadingImage extends React.PureComponent {\n constructor(props) {\n super(props);\n }\n\n render() {\n return (\n \n {React.createElement('img', this.props)}\n
\n \n
\n
\n );\n }\n};\n","// Single message, sent or received.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\nimport { Drafty } from 'tinode-sdk';\n\nimport Attachment from './attachment.jsx';\nimport LetterTile from './letter-tile.jsx';\nimport ReceivedMarker from './received-marker.jsx'\nimport UploadingImage from './uploading-image.jsx'\nimport { sanitizeImageUrl, sanitizeUrl } from '../lib/utils.js';\n\nexport default class ChatMessage extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n progress: 0\n };\n\n if (props.uploader) {\n props.uploader.onProgress = this.handleProgress.bind(this);\n }\n\n this.handleImagePreview = this.handleImagePreview.bind(this);\n this.handleFormButtonClick = this.handleFormButtonClick.bind(this);\n this.handleContextClick = this.handleContextClick.bind(this);\n this.handleCancelUpload = this.handleCancelUpload.bind(this);\n }\n\n handleImagePreview(e) {\n e.preventDefault();\n this.props.onImagePreview({\n url: e.target.src,\n filename: e.target.title,\n width: e.target.dataset.width,\n height: e.target.dataset.height,\n size: e.target.dataset.size,\n type: e.target.dataset.mime\n });\n }\n\n handleFormButtonClick(e) {\n e.preventDefault();\n const data = {\n seq: this.props.seq\n };\n data.resp = {};\n if (e.target.dataset.name) {\n data.resp[e.target.dataset.name] = e.target.dataset.val ? e.target.dataset.val :\n e.target.dataset.val === undefined ? 1 : '' + e.target.dataset.val;\n }\n if (e.target.dataset.act == 'url') {\n data.ref = sanitizeUrl(e.target.dataset.ref) || 'about:blank';\n }\n const text = e.target.dataset.title || 'unknown';\n this.props.onFormResponse(e.target.dataset.act, text, data);\n }\n\n handleContextClick(e) {\n e.preventDefault();\n e.stopPropagation();\n const menuItems = this.props.received == Tinode.MESSAGE_STATUS_FAILED ? ['menu_item_send_retry'] : [];\n this.props.showContextMenu({ seq: this.props.seq, content: this.props.content,\n y: e.pageY, x: e.pageX }, menuItems);\n }\n\n handleProgress(ratio) {\n this.setState({progress: ratio});\n }\n\n handleCancelUpload() {\n this.props.onCancelUpload(this.props.seq, this.props.uploader);\n }\n\n render() {\n const sideClass = this.props.deleted ? 'center' :\n (this.props.sequence + ' ' + (this.props.response ? 'left' : 'right'));\n const bubbleClass = (this.props.sequence == 'single' || this.props.sequence == 'last') ? 'bubble tip' : 'bubble';\n const avatar = this.props.deleted ? null : (this.props.userAvatar || true);\n const fullDisplay = (this.props.userFrom && this.props.response && !this.props.deleted &&\n (this.props.sequence == 'single' || this.props.sequence == 'last'));\n\n let content = this.props.content;\n const attachments = [];\n if (this.props.mimeType == Drafty.getContentType() && Drafty.isValid(content)) {\n Drafty.attachments(content, function(att, i) {\n if (att.mime == 'application/json') {\n // Don't show json objects as attachments.\n // They are not meant for users.\n return;\n }\n attachments.push();\n }, this);\n content = React.createElement(React.Fragment, null, Drafty.format(content, draftyFormatter, this));\n } else if (this.props.deleted) {\n // Message represents a range of deleted messages.\n content = <>block \n \n >\n } else if (typeof content != 'string') {\n content = <>\n error_outline \n \n >\n }\n\n return (\n \n {this.props.userFrom && this.props.response ?\n \n {fullDisplay ?\n :\n null}\n
:\n null}\n \n
\n
\n
\n {content}\n {attachments}\n
\n {this.props.timestamp ?\n
\n : null}\n
\n {this.props.deleted || !this.props.showContextMenu ?\n null :\n
\n \n expand_more\n \n \n }\n
\n {fullDisplay ?\n
\n {\n (notFound) => {return this.props.userName || {notFound}}\n }\n
\n : null\n }\n
\n \n );\n }\n};\n\n// Convert Drafty object to a tree of React elements.\nimport { BROKEN_IMAGE_SIZE, REM_SIZE } from '../config.js';\nimport { fitImageSize } from '../lib/blob-helpers.js';\n\n// Converts Drafty elements into React classes.\n// 'this' is set by the caller.\nfunction draftyFormatter(style, data, values, key) {\n if (style == 'EX') {\n // attachments are handled elsewhere.\n return null;\n }\n\n let el = Drafty.tagName(style);\n if (el) {\n const attr = Drafty.attrValue(style, data) || {};\n attr.key = key;\n switch (style) {\n case 'HL':\n // Highlighted text. Assign class name.\n attr.className = 'highlight';\n break;\n case 'IM':\n // Additional processing for images\n if (data) {\n attr.className = 'inline-image';\n const dim = fitImageSize(data.width, data.height,\n Math.min(this.props.viewportWidth - REM_SIZE * 6.5, REM_SIZE * 34.5), REM_SIZE * 24, false) ||\n {dstWidth: BROKEN_IMAGE_SIZE, dstHeight: BROKEN_IMAGE_SIZE};\n attr.style = {\n width: dim.dstWidth + 'px',\n height: dim.dstHeight + 'px',\n // Looks like a Chrome bug: broken image does not respect 'width' and 'height'.\n minWidth: dim.dstWidth + 'px',\n minHeight: dim.dstHeight + 'px'\n };\n if (!Drafty.isProcessing(data)) {\n attr.src = this.props.tinode.authorizeURL(sanitizeImageUrl(attr.src));\n attr.alt = data.name;\n if (attr.src) {\n attr.onClick = this.handleImagePreview;\n attr.className += ' image-clickable';\n attr.loading = 'lazy';\n } else {\n attr.src = 'img/broken_image.png';\n }\n } else {\n // Use custom element instead of
.\n el = UploadingImage;\n }\n }\n break;\n case 'BN':\n // Button\n attr.onClick = this.handleFormButtonClick;\n let inner = React.Children.map(values, (child) => {\n return typeof child == 'string' ? child : undefined;\n });\n if (!inner || inner.length == 0) {\n inner = [attr.name]\n }\n // Get text which will be sent back when the button is clicked.\n attr['data-title'] = inner.join('');\n break;\n case 'FM':\n // Form\n attr.className = 'bot-form';\n break;\n case 'RW':\n // Form element formatting is dependent on element content.\n break;\n default:\n if (el == '_UNKN') {\n // Unknown element.\n // TODO: make it prettier.\n el = <>extension>;\n }\n break;\n }\n return React.createElement(el, attr, values);\n } else {\n return values;\n }\n};\n","// Send message form.\nimport React from 'react';\nimport { defineMessages, injectIntl } from 'react-intl';\nimport { Drafty } from 'tinode-sdk';\n\nimport { KEYPRESS_DELAY } from '../config.js';\nimport { filePasted } from '../lib/blob-helpers.js';\n\nconst messages = defineMessages({\n messaging_disabled: {\n id: 'messaging_disabled_prompt',\n defaultMessage: 'Messaging disabled',\n description: 'Prompt in SendMessage in read-only topic'\n },\n type_new_message: {\n id: 'new_message_prompt',\n defaultMessage: 'New message',\n description: 'Prompt in send message field'\n },\n add_image_caption: {\n id: 'image_caption_prompt',\n defaultMessage: 'Image caption',\n description: 'Prompt in SendMessage for attached image'\n },\n file_attachment_too_large: {\n id: 'file_attachment_too_large',\n defaultMessage: 'The file size {size} exceeds the {limit} limit.',\n description: 'Error message when attachment is too large'\n },\n cannot_initiate_upload: {\n id: 'cannot_initiate_file_upload',\n defaultMessage: 'Cannot initiate file upload.',\n description: 'Generic error messagewhen attachment fails'\n },\n});\n\nclass SendMessage extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n message: '',\n // Make initial keypress time as if it happened 5001 milliseconds in the past.\n keypressTimestamp: new Date().getTime() - KEYPRESS_DELAY - 1\n };\n\n this.handlePasteEvent = this.handlePasteEvent.bind(this);\n this.handleAttachImage = this.handleAttachImage.bind(this);\n this.handleAttachFile = this.handleAttachFile.bind(this);\n this.handleSend = this.handleSend.bind(this);\n this.handleKeyPress = this.handleKeyPress.bind(this);\n this.handleMessageTyping = this.handleMessageTyping.bind(this);\n }\n\n componentDidMount() {\n if (this.messageEditArea) {\n this.messageEditArea.addEventListener('paste', this.handlePasteEvent, false);\n }\n }\n\n componentWillUnmount() {\n if (this.messageEditArea) {\n this.messageEditArea.removeEventListener('paste', this.handlePasteEvent, false);\n }\n }\n\n componentDidUpdate() {\n if (this.messageEditArea) {\n this.messageEditArea.focus();\n }\n }\n\n handlePasteEvent(e) {\n if (this.props.disabled) {\n return;\n }\n // FIXME: handle large files too.\n if (filePasted(e,\n (bits, mime, width, height, fname) => {\n this.props.onAttachImage(mime, bits, width, height, fname);\n },\n (mime, bits, fname) => {\n this.props.onAttachFile(mime, bits, fname);\n },\n this.props.onError)) {\n\n // If a file was pasted, don't paste base64 data into input field.\n e.preventDefault();\n }\n }\n\n handleAttachImage(e) {\n if (e.target.files && e.target.files.length > 0) {\n this.props.onAttachImage(e.target.files[0]);\n }\n // Clear the value so the same file can be uploaded again.\n e.target.value = '';\n }\n\n handleAttachFile(e) {\n const {formatMessage} = this.props.intl;\n if (e.target.files && e.target.files.length > 0) {\n this.props.onAttachFile(e.target.files[0]);\n }\n // Clear the value so the same file can be uploaded again.\n e.target.value = '';\n }\n\n handleSend(e) {\n e.preventDefault();\n const message = this.state.message.trim();\n if (message || this.props.acceptBlank || this.props.noInput) {\n this.props.onSendMessage(message);\n this.setState({message: ''});\n }\n }\n\n /* Send on Enter key */\n handleKeyPress(e) {\n // Remove this if you don't want Enter to trigger send\n if (e.key === 'Enter') {\n // Have Shift-Enter insert a line break instead\n if (!e.shiftKey) {\n e.preventDefault();\n e.stopPropagation();\n\n this.handleSend(e);\n }\n }\n }\n\n handleMessageTyping(e) {\n const newState = {message: e.target.value};\n if (this.props.onKeyPress) {\n const now = new Date().getTime();\n if (now - this.state.keypressTimestamp > KEYPRESS_DELAY) {\n this.props.onKeyPress();\n newState.keypressTimestamp = now;\n }\n }\n this.setState(newState);\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n const prompt = this.props.disabled ?\n formatMessage(messages.messaging_disabled) :\n (this.props.messagePrompt ?\n formatMessage(messages[this.props.messagePrompt]) :\n formatMessage(messages.type_new_message));\n return (\n \n );\n }\n};\n\nexport default injectIntl(SendMessage);\n","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\nimport SendMessage from '../widgets/send-message.jsx';\n\nimport { bytesToHumanSize } from '../lib/strformat.js';\n\n// Get material icon name from mime type.\nfunction iconFromMime(mime) {\n // If more icons become available in material icons, add them to this mime-to-icon mapping.\n const mimeToIcon = {default: 'insert_drive_file', image: 'image', text: 'description', video: 'theaters'};\n\n return mimeToIcon[mime] || mimeToIcon[(mime || '').split('/')[0]] || mimeToIcon['default'];\n}\n\nexport default class DocPreview extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleSendDoc = this.handleSendDoc.bind(this);\n }\n\n handleSendDoc(caption) {\n this.props.onClose();\n this.props.onSendMessage(this.props.content.file);\n }\n\n render() {\n if (!this.props.content) {\n return null;\n }\n return (\n \n
\n
\n
\n
{iconFromMime(this.props.content.type)}\n
{this.props.content.type || 'application/octet-stream'}
\n
{bytesToHumanSize(this.props.content.size)}
\n
\n
\n
\n
\n );\n }\n};\n","// GroupSubs: a list of group subscribers currently online.\n// Show in the topic title bar\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport { MAX_ONLINE_IN_TOPIC } from '../config.js'\n\nimport LetterTile from './letter-tile.jsx';\nimport { makeImageDataUrl } from '../lib/blob-helpers.js';\n\nexport default class GroupSubs extends React.Component {\n constructor(props) {\n super(props);\n }\n\n render() {\n const usersOnline = [];\n const totalCount = (this.props.subscribers || []).length;\n const countToShow = Math.min(MAX_ONLINE_IN_TOPIC, totalCount);\n\n (this.props.subscribers || []).some((sub) => {\n usersOnline.push(\n \n \n
\n );\n return usersOnline.length == countToShow;\n });\n\n return (\n {usersOnline} {totalCount > countToShow ?\n \n \n : null}\n
\n );\n }\n};\n","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\nimport SendMessage from '../widgets/send-message.jsx';\n\nimport { REM_SIZE } from '../config.js';\nimport { fitImageSize } from '../lib/blob-helpers.js';\nimport { bytesToHumanSize } from '../lib/strformat.js';\n\nexport default class ImagePreview extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n width: 0,\n height: 0\n };\n\n this.handleSendImage = this.handleSendImage.bind(this);\n }\n\n assignWidth(node) {\n if (node && !this.state.width) {\n const bounds = node.getBoundingClientRect();\n this.setState({\n width: bounds.width | 0,\n height: bounds.height | 0\n });\n }\n }\n\n handleSendImage(caption) {\n this.props.onClose();\n this.props.onSendMessage(caption, this.props.content.blob);\n }\n\n render() {\n if (!this.props.content) {\n return null;\n }\n\n const dim = fitImageSize(this.props.content.width, this.props.content.height,\n this.state.width, this.state.height, false);\n const size = dim ? { width: dim.dstWidth + 'px', height: dim.dstHeight + 'px' } :\n ((this.props.content.width > this.props.content.height) ? {width: '100%'} : {height: '100%'});\n size.maxWidth = '100%';\n size.maxHeight = '100%';\n\n let filename = this.props.content.filename;\n // Average font aspect ratio is ~0.5; File name takes 1/3 of the viewport width.\n const maxlength = Math.max(((this.state.width / REM_SIZE / 1.5) | 0) - 2, 12);\n if (filename.length > maxlength) {\n filename = filename.slice(0, maxlength/2 - 1) + '…' + filename.slice(1 - maxlength/2);\n }\n const width = this.props.content.width || '-';\n const height = this.props.content.height || '-';\n return (\n \n
\n
this.assignWidth(node)}>\n

\n
\n {this.props.onSendMessage ?\n
\n :\n }\n
\n );\n }\n};\n","// A single topic or user.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nexport default class Invitation extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleButtonAction = this.handleButtonAction.bind(this);\n }\n\n handleButtonAction(evt, data) {\n evt.preventDefault();\n this.props.onAction(data);\n }\n\n render() {\n return (\n \n
\n \n
\n
\n \n \n \n
\n
\n );\n }\n};\n","/* This is just a static page to display when no conversation is selected. */\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport { APP_NAME } from '../config.js';\n\nexport default class LogoView extends React.PureComponent {\n render() {\n const version = APP_NAME + ' (' + Tinode.getLibrary() + ')';\n return (\n \n
\n
\n
\n Tinode Web
\n \n
{version}
\n
{this.props.serverVersion} ({this.props.serverAddress})
\n
\n
\n );\n }\n};\n","// Utility class for hash navigation.\n\n// Parse hash as in http://www.example.com/path#hash as if it were\n// path and arguments.\nexport default class HashNavigation {\n static parseUrlHash(hash) {\n // Split path from args, path -> parts[0], args->path[1]\n const parts = hash.split('?', 2);\n const params = {};\n let path = [];\n if (parts[0]) {\n path = parts[0].substr(1).split('/');\n }\n if (parts[1]) {\n parts[1].split('&').forEach(function(part) {\n let item = part.split('=');\n if (item[0]) {\n params[decodeURIComponent(item[0])] = decodeURIComponent(item[1]);\n }\n });\n }\n return {path: path, params: params};\n }\n\n static navigateTo(url) {\n window.location.hash = url;\n }\n\n static composeUrlHash(path, params) {\n let url = path.join('/');\n const args = [];\n for (const key in params) {\n if (params.hasOwnProperty(key)) {\n args.push(key + '=' + params[key]);\n }\n }\n if (args.length > 0) {\n url += '?' + args.join('&');\n }\n return url;\n }\n\n static addUrlParam(hash, key, value) {\n const parsed = this.parseUrlHash(hash);\n parsed.params[key] = value;\n return this.composeUrlHash(parsed.path, parsed.params);\n }\n\n static removeUrlParam(hash, key) {\n const parsed = this.parseUrlHash(hash);\n delete parsed.params[key];\n return this.composeUrlHash(parsed.path, parsed.params);\n }\n\n static setUrlSidePanel(hash, sidepanel) {\n const parsed = this.parseUrlHash(hash);\n parsed.path[0] = sidepanel;\n return this.composeUrlHash(parsed.path, parsed.params);\n }\n\n static setUrlTopic(hash, topic) {\n const parsed = this.parseUrlHash(hash);\n parsed.path[1] = topic;\n // Close InfoView on topic change.\n delete parsed.params.info;\n return this.composeUrlHash(parsed.path, parsed.params);\n }\n}\n","import React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\nconst Drafty = Tinode.Drafty;\n\nimport ChatMessage from '../widgets/chat-message.jsx';\nimport ContactBadges from '../widgets/contact-badges.jsx';\nimport DocPreview from '../widgets/doc-preview.jsx';\nimport ErrorPanel from '../widgets/error-panel.jsx';\nimport GroupSubs from '../widgets/group-subs.jsx';\nimport ImagePreview from '../widgets/image-preview.jsx';\nimport Invitation from '../widgets/invitation.jsx';\nimport LetterTile from '../widgets/letter-tile.jsx';\nimport LoadSpinner from '../widgets/load-spinner.jsx';\nimport LogoView from './logo-view.jsx';\nimport SendMessage from '../widgets/send-message.jsx';\n\nimport { DEFAULT_P2P_ACCESS_MODE, IMAGE_PREVIEW_DIM, KEYPRESS_DELAY, MESSAGES_PAGE,\n MAX_EXTERN_ATTACHMENT_SIZE, MAX_IMAGE_DIM, MAX_INBAND_ATTACHMENT_SIZE, READ_DELAY } from '../config.js';\nimport { SUPPORTED_IMAGE_FORMATS, blobToBase64, filePasted, fileToBase64,\n imageScaled, makeImageDataUrl } from '../lib/blob-helpers.js';\nimport HashNavigation from '../lib/navigation.js';\nimport { bytesToHumanSize, shortDateFormat } from '../lib/strformat.js';\n\n// Run timer with this frequency (ms) for checking notification queue.\nconst NOTIFICATION_EXEC_INTERVAL = 300;\n\nconst messages = defineMessages({\n online_now: {\n id: 'online_now',\n defaultMessage: 'online now',\n description: 'Indicator that the user or topic is currently online',\n },\n last_seen: {\n id: 'last_seen_timestamp',\n defaultMessage: 'Last seen',\n description: 'Label for the timestamp of when the user or topic was last online'\n },\n not_found: {\n id: 'title_not_found',\n defaultMessage: 'Not found',\n description: 'Title shown when topic is not found'\n },\n channel: {\n id: 'channel',\n defaultMessage: 'channel',\n description: 'Subtitle shown for channels in MessagesView instead of last seen'\n },\n file_attachment_too_large: {\n id: 'file_attachment_too_large',\n defaultMessage: 'The file size {size} exceeds the {limit} limit.',\n description: 'Error message when attachment is too large'\n }\n});\n\n// Checks if the access permissions are granted but not yet accepted.\nfunction isUnconfirmed(acs) {\n if (acs) {\n const ex = acs.getExcessive() || '';\n return acs.isJoiner('given') && (ex.includes('R') || ex.includes('W'));\n }\n return false;\n}\n\nfunction isPeerRestricted(acs) {\n if (acs) {\n const ms = acs.getMissing() || '';\n return acs.isJoiner('want') && (ms.includes('R') || ms.includes('W'));\n }\n return false;\n}\n\nclass MessagesView extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = MessagesView.getDerivedStateFromProps(props, {});\n\n this.leave = this.leave.bind(this);\n this.sendImageAttachment = this.sendImageAttachment.bind(this);\n this.sendFileAttachment = this.sendFileAttachment.bind(this);\n this.sendKeyPress = this.sendKeyPress.bind(this);\n this.handleScrollReference = this.handleScrollReference.bind(this);\n this.handleScrollEvent = this.handleScrollEvent.bind(this);\n this.handleDescChange = this.handleDescChange.bind(this);\n this.handleSubsUpdated = this.handleSubsUpdated.bind(this);\n this.handleNewMessage = this.handleNewMessage.bind(this);\n this.handleAllMessagesReceived = this.handleAllMessagesReceived.bind(this);\n this.handleInfoReceipt = this.handleInfoReceipt.bind(this);\n this.handleImagePostview = this.handleImagePostview.bind(this);\n this.handleClosePreview = this.handleClosePreview.bind(this);\n this.handleFormResponse = this.handleFormResponse.bind(this);\n this.handleContextClick = this.handleContextClick.bind(this);\n this.handleShowContextMenuMessage = this.handleShowContextMenuMessage.bind(this);\n this.handleNewChatAcceptance = this.handleNewChatAcceptance.bind(this);\n this.handleEnablePeer = this.handleEnablePeer.bind(this);\n this.handleAttachFile = this.handleAttachFile.bind(this);\n this.handleAttachImage = this.handleAttachImage.bind(this);\n this.handleCancelUpload = this.handleCancelUpload.bind(this);\n this.postReadNotification = this.postReadNotification.bind(this);\n this.clearNotificationQueue = this.clearNotificationQueue.bind(this);\n\n this.readNotificationQueue = [];\n this.readNotificationTimer = null;\n }\n\n componentDidMount() {\n // this.propsChange(this.props, this.state);\n if (this.messagesScroller) {\n this.messagesScroller.addEventListener('scroll', this.handleScrollEvent);\n }\n }\n\n componentWillUnmount() {\n if (this.messagesScroller) {\n this.messagesScroller.removeEventListener('scroll', this.handleScrollEvent);\n }\n\n // Flush all notifications.\n this.clearNotificationQueue();\n }\n\n // Scroll last message into view on component update e.g. on message received\n // or vertical shrinking.\n componentDidUpdate(prevProps, prevState) {\n if (this.messagesScroller) {\n if (prevState.topic != this.state.topic || prevState.messages.length != this.state.messages.length) {\n // New message\n this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight - this.state.scrollPosition;\n } else if (prevProps.viewportHeight > this.props.viewportHeight) {\n // Componet changed height.\n this.messagesScroller.scrollTop += prevProps.viewportHeight - this.props.viewportHeight;\n }\n }\n\n const topic = this.props.tinode ? this.props.tinode.getTopic(this.state.topic) : undefined;\n if (this.state.topic != prevState.topic) {\n if (prevState.topic && !Tinode.isNewGroupTopicName(prevState.topic)) {\n this.leave(prevState.topic);\n }\n\n if (topic) {\n topic.onData = this.handleNewMessage;\n topic.onAllMessagesReceived = this.handleAllMessagesReceived;\n topic.onInfo = this.handleInfoReceipt;\n topic.onMetaDesc = this.handleDescChange;\n topic.onSubsUpdated = this.handleSubsUpdated;\n topic.onPres = this.handleSubsUpdated;\n }\n }\n\n if (!this.props.applicationVisible) {\n // If application is not visible, flush all unsent 'read' notifications.\n this.clearNotificationQueue();\n } else {\n // Otherwise assume there are unread messages.\n this.postReadNotification(0);\n }\n\n if (topic && !topic.isSubscribed() && this.props.ready &&\n ((this.state.topic != prevState.topic) || !prevProps.ready)) {\n // Is this a new topic?\n const newTopic = (this.props.newTopicParams && this.props.newTopicParams._topicName == this.props.topic);\n\n // Don't request the tags. They are useless unless the user\n // is the owner and is editing the topic.\n let getQuery = topic.startMetaQuery().withLaterDesc().withLaterSub();\n if (this.state.isReader || newTopic) {\n // Reading is either permitted or we don't know because it's a new topic. Ask for messages.\n getQuery = getQuery.withLaterData(MESSAGES_PAGE);\n if (this.state.isReader) {\n getQuery = getQuery.withLaterDel();\n }\n // And show \"loading\" spinner.\n this.setState({ fetchingMessages: true });\n }\n const setQuery = newTopic ? this.props.newTopicParams : undefined;\n topic.subscribe(getQuery.build(), setQuery)\n .then((ctrl) => {\n if (ctrl.code == 303) {\n // Redirect to another topic requested.\n HashNavigation.navigateTo(HashNavigation.setUrlTopic('', ctrl.params.topic));\n return;\n }\n if (this.state.topic != ctrl.topic) {\n this.setState({topic: ctrl.topic});\n }\n this.props.onNewTopicCreated(this.props.topic, ctrl.topic);\n // If there are unsent messages, try sending them now.\n topic.queuedMessages((pub) => {\n if (!pub._sending && topic.isSubscribed()) {\n topic.publishMessage(pub);\n }\n });\n })\n .catch((err) => {\n console.log(\"Failed subscription to\", this.state.topic);\n this.props.onError(err.message, 'err');\n const blankState = MessagesView.getDerivedStateFromProps({}, {});\n blankState.title = this.props.intl.formatMessage(messages.not_found);\n this.setState(blankState);\n });\n }\n }\n\n static getDerivedStateFromProps(nextProps, prevState) {\n let nextState = {};\n if (!nextProps.topic) {\n // Default state: no topic.\n nextState = {\n messages: [],\n onlineSubs: [],\n topic: null,\n title: '',\n avatar: null,\n isVerified: false,\n isStaff: false,\n isDangerous: false,\n docPreview: null,\n imagePreview: null,\n imagePostview: null,\n typingIndicator: false,\n scrollPosition: 0,\n fetchingMessages: false,\n peerMessagingDisabled: false,\n channel: false\n };\n } else if (nextProps.topic != prevState.topic) {\n const topic = nextProps.tinode.getTopic(nextProps.topic);\n nextState = {\n topic: nextProps.topic,\n docPreview: null,\n imagePreview: null,\n imagePostview: null,\n typingIndicator: false,\n scrollPosition: 0,\n fetchingMessages: false\n };\n\n if (topic) {\n // Topic exists.\n const msgs = [];\n const subs = [];\n\n if (nextProps.connected) {\n topic.subscribers((sub) => {\n if (sub.online && sub.user != nextProps.myUserId) {\n subs.push(sub);\n }\n });\n }\n\n topic.messages(function(msg) {\n if (!msg.deleted) {\n msgs.push(msg);\n }\n });\n\n Object.assign(nextState, {\n messages: msgs,\n onlineSubs: subs\n });\n\n if (topic.public) {\n Object.assign(nextState, {\n title: topic.public.fn,\n avatar: makeImageDataUrl(topic.public.photo)\n });\n } else {\n Object.assign(nextState, {\n title: '',\n avatar: null\n });\n }\n\n const peer = topic.p2pPeerDesc();\n if (peer) {\n Object.assign(nextState, {\n peerMessagingDisabled: isPeerRestricted(peer.acs)\n });\n } else if (prevState.peerMessagingDisabled) {\n Object.assign(nextState, {\n peerMessagingDisabled: false\n });\n }\n Object.assign(nextState, {\n channel: topic.isChannelType()\n });\n } else {\n // Invalid topic.\n Object.assign(nextState, {\n messages: [],\n onlineSubs: [],\n title: '',\n avatar: null,\n peerMessagingDisabled: false,\n channel: false\n });\n }\n }\n\n if (nextProps.acs) {\n if (nextProps.acs.isWriter() != prevState.isWriter) {\n nextState.isWriter = !prevState.isWriter;\n }\n if (nextProps.acs.isReader() != prevState.isReader) {\n nextState.isReader = !prevState.isReader;\n }\n if (!nextProps.acs.isReader('given') != prevState.readingBlocked) {\n nextState.readingBlocked = !prevState.readingBlocked;\n }\n if (nextProps.acs.isSharer() != prevState.isSharer) {\n nextState.isSharer = !prevState.isSharer;\n }\n } else {\n if (prevState.isWriter) {\n nextState.isWriter = false;\n }\n if (prevState.isReader) {\n nextState.isReader = false;\n }\n if (!prevState.readingBlocked) {\n prevState.readingBlocked = true;\n }\n if (prevState.isSharer) {\n nextState.isSharer = false;\n }\n }\n\n if (isUnconfirmed(nextProps.acs) == !prevState.unconformed) {\n nextState.unconfirmed = !prevState.unconformed;\n }\n\n // Clear subscribers online when there is no connection.\n if (!nextProps.connected && prevState.onlineSubs && prevState.onlineSubs.length > 0) {\n nextState.onlineSubs = [];\n }\n\n return nextState;\n }\n\n leave(oldTopicName) {\n if (!oldTopicName || !this.props.tinode.isTopicCached(oldTopicName)) {\n return;\n }\n const oldTopic = this.props.tinode.getTopic(oldTopicName);\n if (oldTopic && oldTopic.isSubscribed()) {\n oldTopic.leave(false)\n .catch(() => { /* do nothing here */ })\n .finally(() => {\n // We don't care if the request succeeded or failed.\n // The topic is dead regardless.\n this.setState({fetchingMessages: false});\n oldTopic.onData = undefined;\n oldTopic.onAllMessagesReceived = undefined;\n oldTopic.onInfo = undefined;\n oldTopic.onMetaDesc = undefined;\n oldTopic.onSubsUpdated = undefined;\n oldTopic.onPres = undefined;\n });\n }\n }\n\n handleScrollReference(node) {\n if (node) {\n node.addEventListener('scroll', this.handleScrollEvent);\n this.messagesScroller = node;\n this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight - this.state.scrollPosition;\n }\n }\n\n // Get older messages\n handleScrollEvent(event) {\n this.setState({scrollPosition: event.target.scrollHeight - event.target.scrollTop});\n if (event.target.scrollTop <= 0) {\n this.setState((prevState, props) => {\n const newState = {};\n if (!prevState.fetchingMessages) {\n const topic = this.props.tinode.getTopic(this.state.topic);\n if (topic && topic.isSubscribed() && topic.msgHasMoreMessages()) {\n newState.fetchingMessages = true;\n topic.getMessagesPage(MESSAGES_PAGE).catch((err) => {\n this.setState({fetchingMessages: false});\n this.props.onError(err.message, 'err');\n });\n }\n }\n return newState;\n });\n }\n }\n\n handleDescChange(desc) {\n if (desc.public) {\n this.setState({\n title: desc.public.fn,\n avatar: makeImageDataUrl(desc.public.photo)\n });\n } else {\n this.setState({\n title: '',\n avatar: null\n });\n }\n\n if (desc.acs) {\n this.setState({\n isWriter: desc.acs.isWriter(),\n isReader: desc.acs.isReader(),\n readingBlocked: !desc.acs.isReader('given'),\n unconfirmed: isUnconfirmed(desc.acs),\n });\n }\n }\n\n postReadNotification(seq) {\n // Ignore notifications if the app is invisible.\n if (!this.props.applicationVisible) {\n return;\n }\n\n // Set up the timer if it's not running already.\n if (!this.readNotificationTimer) {\n this.readNotificationTimer = setInterval(() => {\n if (this.readNotificationQueue.length == 0) {\n // Shut down the timer if the queue is empty.\n clearInterval(this.readNotificationTimer);\n this.readNotificationTimer = null;\n return;\n }\n\n let seq = -1;\n while (this.readNotificationQueue.length > 0) {\n const n = this.readNotificationQueue[0];\n if (n.topicName != this.state.topic) {\n // Topic has changed. Drop the notification.\n this.readNotificationQueue.shift();\n continue;\n }\n\n const now = new Date();\n if (n.sendAt <= now) {\n // Remove expired notification from queue.\n this.readNotificationQueue.shift();\n seq = Math.max(seq, n.seq);\n } else {\n break;\n }\n }\n\n // Send only one notification for the whole batch of messages.\n if (seq >= 0) {\n const topic = this.props.tinode.getTopic(this.state.topic);\n if (topic) {\n topic.noteRead(seq);\n }\n }\n }, NOTIFICATION_EXEC_INTERVAL);\n }\n\n const now = new Date();\n this.readNotificationQueue.push({\n topicName: this.state.topic,\n seq: seq,\n sendAt: now.setMilliseconds(now.getMilliseconds() + READ_DELAY)\n });\n }\n\n // Clear notification queue and timer.\n clearNotificationQueue() {\n this.readNotificationQueue = [];\n if (this.readNotificationTimer) {\n clearInterval(this.readNotificationTimer);\n this.readNotificationTimer = null;\n }\n }\n\n handleSubsUpdated() {\n if (this.state.topic) {\n const subs = [];\n const topic = this.props.tinode.getTopic(this.state.topic);\n topic.subscribers((sub) => {\n if (sub.online && sub.user != this.props.myUserId) {\n subs.push(sub);\n }\n });\n const newState = {onlineSubs: subs};\n const peer = topic.p2pPeerDesc();\n if (peer) {\n Object.assign(newState, {\n peerMessagingDisabled: isPeerRestricted(peer.acs)\n });\n } else if (this.state.peerMessagingDisabled) {\n Object.assign(newState, {\n peerMessagingDisabled: false\n });\n }\n this.setState(newState);\n }\n }\n\n handleNewMessage(msg) {\n // Regenerate messages list\n const topic = this.props.tinode.getTopic(this.state.topic);\n const newState = {messages: []};\n topic.messages((m) => {\n if (!m.deleted) {\n newState.messages.push(m);\n }\n });\n\n // msg could be null if one or more messages were deleted.\n if (msg && !msg.deleted) {\n // If the message is added to the end of the message list,\n // scroll to the bottom.\n if (topic.isNewMessage(msg.seq)) {\n newState.scrollPosition = 0;\n }\n\n // Aknowledge messages except own messages. They are\n // automatically assumed to be read and recived.\n const status = topic.msgStatus(msg, true);\n if (status >= Tinode.MESSAGE_STATUS_SENT && msg.from != this.props.myUserId) {\n this.postReadNotification(msg.seq);\n }\n this.props.onData(msg);\n }\n this.setState(newState);\n }\n\n handleAllMessagesReceived(count) {\n this.setState({fetchingMessages: false});\n if (count > 0) {\n // 0 means \"latest\".\n this.postReadNotification(0);\n }\n }\n\n handleInfoReceipt(info) {\n switch (info.what) {\n case 'kp': {\n clearTimeout(this.keyPressTimer);\n var instance = this;\n this.keyPressTimer = setTimeout(function() {\n instance.setState({typingIndicator: false});\n }, KEYPRESS_DELAY + 1000);\n if (!this.state.typingIndicator) {\n this.setState({typingIndicator: true});\n }\n break;\n }\n case 'read':\n case 'recv':\n // Redraw due to changed recv/read status.\n this.forceUpdate();\n break;\n default:\n console.log(\"Other change in topic: \", info.what);\n }\n }\n\n handleImagePostview(content) {\n this.setState({ imagePostview: content });\n }\n\n handleClosePreview() {\n if (this.state.imagePreview && this.state.imagePreview.url) {\n URL.revokeObjectURL(this.state.imagePreview.url);\n }\n this.setState({ imagePostview: null, imagePreview: null, docPreview: null });\n }\n\n handleFormResponse(action, text, data) {\n if (action == 'pub') {\n this.props.sendMessage(Drafty.attachJSON(Drafty.parse(text), data));\n } else if (action == 'url') {\n const url = new URL(data.ref);\n const params = url.searchParams;\n for (let key in data.resp) {\n if (data.resp.hasOwnProperty(key)) {\n params.set(key, data.resp[key]);\n }\n }\n ['name', 'seq'].map((key) => {\n if (data[key]) {\n params.set(key, data[key]);\n }\n });\n params.set('uid', this.props.myUserId);\n params.set('topic', this.state.topic);\n url.search = params;\n window.open(url, '_blank');\n } else {\n console.log(\"Unknown action in form\", action);\n }\n }\n\n handleContextClick(e) {\n e.preventDefault();\n e.stopPropagation();\n this.props.showContextMenu({ topicName: this.state.topic, y: e.pageY, x: e.pageX });\n }\n\n handleShowContextMenuMessage(params, messageSpecificMenuItems) {\n params.topicName = this.state.topic;\n const menuItems = messageSpecificMenuItems || [];\n const topic = this.props.tinode.getTopic(params.topicName);\n if (topic) {\n if (!topic.isChannelType()) {\n menuItems.push('message_delete');\n }\n const acs = topic.getAccessMode();\n if (acs && acs.isDeleter()) {\n menuItems.push('message_delete_hard');\n }\n }\n this.props.showContextMenu(params, menuItems);\n }\n\n handleNewChatAcceptance(action) {\n this.props.onNewChat(this.state.topic, action);\n }\n\n handleEnablePeer(e) {\n e.preventDefault();\n this.props.onChangePermissions(this.state.topic, DEFAULT_P2P_ACCESS_MODE, this.state.topic);\n }\n\n sendKeyPress() {\n const topic = this.props.tinode.getTopic(this.state.topic);\n if (topic.isSubscribed()) {\n topic.noteKeyPress();\n }\n }\n\n // Send attachment as Drafty message:\n // - if file is too large, upload it and send a s link.\n // - if file is small enough, just send it in-band.\n sendFileAttachment(file) {\n // Server-provided limit reduced for base64 encoding and overhead.\n const maxInbandAttachmentSize = (this.props.tinode.getServerLimit('maxMessageSize',\n MAX_INBAND_ATTACHMENT_SIZE) * 0.75 - 1024) | 0;\n\n if (file.size > maxInbandAttachmentSize) {\n // Too large to send inband - uploading out of band and sending as a link.\n const uploader = this.props.tinode.getLargeFileHelper();\n if (!uploader) {\n this.props.onError(this.props.intl.formatMessage(messages.cannot_initiate_upload));\n return;\n }\n const uploadCompletionPromise = uploader.upload(file);\n const msg = Drafty.attachFile(null, {\n mime: file.type,\n filename: file.name,\n size: file.size,\n urlPromise: uploadCompletionPromise\n });\n // Pass data and the uploader to the TinodeWeb.\n this.props.sendMessage(msg, uploadCompletionPromise, uploader);\n } else {\n // Small enough to send inband.\n fileToBase64(file,\n (mime, bits, fname) => {\n this.props.sendMessage(Drafty.attachFile(null, {mime: mime, data: bits, filename: fname}));\n },\n this.props.onError\n );\n }\n }\n\n // handleAttachFile method is called when [Attach file] button is clicked.\n handleAttachFile(file) {\n const maxExternAttachmentSize = this.props.tinode.getServerLimit('maxFileUploadSize', MAX_EXTERN_ATTACHMENT_SIZE);\n\n if (file.size > maxExternAttachmentSize) {\n // Too large.\n this.props.onError(this.props.intl.formatMessage(messages.file_attachment_too_large,\n {size: bytesToHumanSize(file.size), limit: bytesToHumanSize(maxExternAttachmentSize)}), 'err');\n } else {\n this.setState({ docPreview: {\n file: file,\n filename: file.name,\n size: file.size,\n type: file.type\n }});\n }\n }\n\n // sendImageAttachment sends the image bits inband as Drafty message.\n sendImageAttachment(caption, blob) {\n const mime = this.state.imagePreview.type;\n const width = this.state.imagePreview.width;\n const height = this.state.imagePreview.height;\n const fname = this.state.imagePreview.filename;\n\n // Server-provided limit reduced for base64 encoding and overhead.\n const maxInbandAttachmentSize = (this.props.tinode.getServerLimit('maxMessageSize',\n MAX_INBAND_ATTACHMENT_SIZE) * 0.75 - 1024) | 0;\n\n if (blob.size > maxInbandAttachmentSize) {\n // Too large to send inband - uploading out of band and sending as a link.\n const uploader = this.props.tinode.getLargeFileHelper();\n if (!uploader) {\n this.props.onError(this.props.intl.formatMessage(messages.cannot_initiate_upload));\n return;\n }\n const uploadCompletionPromise = uploader.upload(blob);\n\n // Make small preview to show while uploading.\n imageScaled(blob, IMAGE_PREVIEW_DIM, IMAGE_PREVIEW_DIM, -1, false,\n (tinyMine, tinyBlob) => {\n // Convert tiny image into base64 for serialization and previewing.\n blobToBase64(tinyBlob, (blobMime, tinyBits64) => {\n let msg = Drafty.insertImage(null, 0, {\n mime: mime,\n _tempPreview: tinyBits64, // This preview will not be serialized.\n width: width,\n height: height,\n filename: fname,\n size: blob.size,\n urlPromise: uploadCompletionPromise\n });\n if (caption) {\n msg = Drafty.appendLineBreak(msg);\n msg = Drafty.append(msg, Drafty.init(caption));\n }\n // Pass data and the uploader to the TinodeWeb.\n this.props.sendMessage(msg, uploadCompletionPromise, uploader);\n }\n )},\n // Failure\n (err) => {\n this.props.onError(err, 'err');\n }\n );\n return;\n }\n\n // Upload the image if it's too big to be send inband.\n blobToBase64(blob, (blobMime, bits64) => {\n let msg = Drafty.insertImage(null, 0, {\n mime: blobMime,\n preview: bits64, // Serializable preview\n width: width,\n height: height,\n filename: fname,\n size: blob.size\n });\n if (caption) {\n msg = Drafty.appendLineBreak(msg);\n msg = Drafty.append(msg, Drafty.init(caption));\n }\n this.props.sendMessage(msg);\n });\n }\n\n // handleAttachImage method is called when [Attach image] button is clicked.\n handleAttachImage(file) {\n const maxExternAttachmentSize = this.props.tinode.getServerLimit('maxFileUploadSize', MAX_EXTERN_ATTACHMENT_SIZE);\n\n // Get image dimensions and size, optionally scale it down.\n imageScaled(file, MAX_IMAGE_DIM, MAX_IMAGE_DIM, maxExternAttachmentSize, false,\n // Success\n (mime, blob, width, height, fname) => {\n this.setState({imagePreview: {\n url: URL.createObjectURL(blob),\n blob: blob,\n filename: fname,\n width: width,\n height: height,\n size: blob.size,\n type: mime\n }});\n },\n // Failure\n (err) => {\n this.props.onError(err, 'err');\n }\n );\n }\n\n handleCancelUpload(seq, uploader) {\n const found = this.state.messages.find(msg => msg.seq == seq);\n if (found) {\n found._cancelled = true;\n }\n uploader.cancel();\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n\n let component;\n if (this.props.hideSelf) {\n component = null;\n } else if (!this.state.topic) {\n component = (\n \n );\n } else {\n let component2;\n if (this.state.imagePreview) {\n // Preview image before sending.\n component2 = (\n \n );\n } else if (this.state.imagePostview) {\n // Expand received image.\n component2 = (\n \n );\n } else if (this.state.docPreview) {\n // Preview attachment before sending.\n component2 = (\n \n );\n } else {\n const topic = this.props.tinode.getTopic(this.state.topic);\n const isChannel = topic.isChannelType();\n const groupTopic = topic.isGroupType() && !isChannel;\n const icon_badges = [];\n if (topic.trusted) {\n if (topic.trusted.verified) {\n icon_badges.push({icon: 'verified', color: 'badge-inv'});\n }\n if (topic.trusted.staff) {\n icon_badges.push({icon: 'staff', color: 'badge-inv'});\n }\n if (topic.trusted.danger) {\n icon_badges.push({icon: 'dangerous', color: 'badge-inv'});\n }\n }\n let messageNodes = [];\n let previousFrom = null;\n let chatBoxClass = null;\n for (let i=0; i\n );\n }\n\n let lastSeen = null;\n if (isChannel) {\n lastSeen = formatMessage(messages.channel);\n } else {\n const cont = this.props.tinode.getMeTopic().getContact(this.state.topic);\n if (cont && Tinode.isP2PTopicName(cont.topic)) {\n if (cont.online) {\n lastSeen = formatMessage(messages.online_now);\n } else if (cont.seen) {\n lastSeen = formatMessage(messages.last_seen) + \": \" +\n shortDateFormat(cont.seen.when, this.props.intl.locale);\n // TODO: also handle user agent in c.seen.ua\n }\n }\n }\n const avatar = this.state.avatar || true;\n const online = this.props.online ? 'online' + (this.state.typingIndicator ? ' typing' : '') : 'offline';\n\n component2 = (\n <>\n \n {this.props.displayMobile ?\n \n : null}\n \n \n
\n {!this.state.isReader ?\n
\n {this.state.readingBlocked ?\n
\n \n
\n : null }\n
\n : null }\n
\n {this.state.peerMessagingDisabled && !this.state.unconfirmed ?\n : null}\n {this.state.unconfirmed ?\n \n :\n }\n >\n );\n }\n\n component = {component2}
\n }\n return component;\n }\n};\n\nexport default injectIntl(MessagesView);\n","// The <- button to be displayed in title bars.\nimport React from 'react';\n\nexport default class ButtonBack extends React.PureComponent {\n render() {\n return (\n {e.preventDefault(); this.props.onBack();}}>\n arrow_back\n \n );\n }\n}\n","import React from 'react';\n\nexport default class MenuContacts extends React.PureComponent {\n render() {\n return (\n \n );\n }\n};\n","import React from 'react';\n\nexport default class MenuStart extends React.PureComponent {\n render() {\n return (\n \n );\n }\n};\n","import React from 'react';\n\n\nimport LetterTile from './letter-tile.jsx';\nimport ButtonBack from './button-back.jsx';\nimport ContactBadges from './contact-badges.jsx';\nimport MenuContacts from './menu-contacts.jsx';\nimport MenuStart from './menu-start.jsx';\n\nexport default class SideNavbar extends React.PureComponent {\n render() {\n const icon_badges = [];\n if (this.props.trustedBadges) {\n this.props.trustedBadges.map((b) => {\n icon_badges.push({icon: b, color: 'badge-inv'});\n });\n }\n return (\n \n {this.props.onCancel ?
: null}\n {this.props.avatar ?\n
\n \n
\n :\n null}\n
{this.props.title}
\n {this.props.state === 'login' ?\n
:\n this.props.state === 'contacts' ?\n
:\n null}\n
\n );\n }\n};\n","// Image zoom and crop widget.\n\nimport React from 'react';\n\nconst DEFAULT_MAX_ZOOM = 2.5;\n\nexport default class Cropper extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n // Coordinates of the top left corner of the image in container coordinates.\n panX: 0,\n panY: 0,\n // Origin of the zoom in image coordinates.\n originX: 0,\n originY: 0,\n // Zoom scale.\n zoom: 1,\n // Zoom min and max.\n minZoom: 0,\n maxZoom: DEFAULT_MAX_ZOOM,\n };\n\n // References to DOM elements for hooking up events and getting screen dimensions.\n // The transparent element which captures mouse moves (moves with the image).\n this.overlay = React.createRef();\n // The image cutout window or view port (static).\n this.cutout = React.createRef();\n // Scaled and translated image preview.\n this.preview = React.createRef();\n // The main container (static).\n this.boundingBox = React.createRef();\n\n // The original image width and height before any scaling.\n this.imageWidth = 0;\n this.imageHeight = 0;\n\n // Mouse position when dragging.\n this.mouseX = 0\n this.mouseY = 0;\n // Length of the previous mouse move when dragging.\n this.prevDistance = 0;\n\n // Bounding rectangles of static elements.\n this.cutoutRect = {};\n this.bBoxRect = {};\n // Center of the bounding box.\n this.originX = 0;\n this.originY = 0;\n\n this.initScaling = this.initScaling.bind(this);\n this.handleZoom = this.handleZoom.bind(this);\n this.mouseDown = this.mouseDown.bind(this);\n this.mouseUp = this.mouseUp.bind(this);\n this.mouseMove = this.mouseMove.bind(this);\n this.mouseTouch = this.mouseTouch.bind(this);\n this.positionAll = this.positionAll.bind(this);\n this.translate = this.translate.bind(this);\n }\n\n componentDidMount() {\n this.overlay.current.addEventListener('mousedown', this.mouseDown, { passive: true });\n this.overlay.current.addEventListener('touchstart', this.mouseDown, { passive: true });\n\n // The rectangle position is in viewport coordinates.\n this.bBoxRect = this.boundingBox.current.getBoundingClientRect();\n // Center of the bounding box in container coordinates.\n this.originX = this.bBoxRect.width / 2;\n this.originY = this.bBoxRect.height / 2;\n\n // The rectangle position is in viewport coordinates.\n this.cutoutRect = this.cutout.current.getBoundingClientRect();\n }\n\n componentWillUnmount() {\n this.overlay.current.removeEventListener('mousedown', this.mouseDown);\n this.overlay.current.removeEventListener('touchstart', this.mouseDown);\n }\n\n // Position all elements.\n positionAll(panX, panY, zoom) {\n // Zoom origin in image preview coordinates.\n this.setState({\n panX: panX,\n panY: panY,\n zoom: zoom,\n originX: this.originX - panX,\n originY: this.originY - panY,\n });\n }\n\n // Check if new location is within the limits.\n static checkBound(currPan, img, cutout, delta) {\n let nextDiff = Math.min(0, cutout[0] - img[0] - delta, img[1] - cutout[1] + delta);\n if (nextDiff == 0) {\n // Cutout is completely within the image.\n currPan += delta;\n } else if (Math.min(0, cutout[0] - img[0], img[1] - cutout[1]) < nextDiff) {\n // Cutout is outside of the range but closer to the bound.\n currPan += delta;\n }\n return currPan;\n }\n\n initScaling() {\n // Initialize components.\n const imgRect = this.preview.current.getBoundingClientRect();\n\n this.imageWidth = imgRect.width;\n this.imageHeight = imgRect.height;\n\n // Set zoom slider's min and max values. Make sure the scaled image cannot be smaller than the cutout.\n const minZoom = Math.max(this.cutoutRect.width / imgRect.width, this.cutoutRect.height / imgRect.height);\n this.setState({\n minZoom: minZoom,\n maxZoom: Math.max(DEFAULT_MAX_ZOOM, minZoom + 1)\n });\n\n // Initial zoom level fills the bounding box at the smallest image dimension and overflows the largest, i.e. \"fill\" not \"fit\".\n let zoom = Math.max((this.bBoxRect.width / imgRect.width), (this.bBoxRect.height / imgRect.height));\n // Converting from viewport coordinates to container, then panning.\n let panX = (this.cutoutRect.left - this.bBoxRect.left) - ((imgRect.width - this.cutoutRect.width) / 2);\n let panY = (this.cutoutRect.top - this.bBoxRect.top) - ((imgRect.height - this.cutoutRect.height) / 2);\n\n this.positionAll(panX, panY, zoom);\n }\n\n handleZoom(e) {\n let zoom = e.target.value;\n\n let panX = this.state.panX;\n let panY = this.state.panY;\n\n // Ensure that the image at the new zoom is not outside of the cutout boundaries.\n // Calculate image coordinates at the new zoom.\n const imgLeft = this.originX - (this.originX - panX) * zoom;\n const imgRight = imgLeft + this.imageWidth * zoom;\n // Cutout in the same coordinates as the image.\n const coLeft = this.cutoutRect.left - this.bBoxRect.left;\n const coRight = coLeft + this.cutoutRect.width;\n if (coLeft < imgLeft) {\n panX -= imgLeft - coLeft;\n } else if (coRight > imgRight) {\n panX += coRight - imgRight;\n }\n const imgTop = this.originY - (this.originY - panY) * zoom;\n const imgBottom = imgTop + this.imageHeight * zoom;\n const coTop = this.cutoutRect.top - this.bBoxRect.top;\n const coBottom = coTop + this.cutoutRect.height;\n if (coTop < imgTop) {\n panY -= imgTop - coTop;\n } else if (coBottom > imgBottom) {\n panY += coBottom - imgBottom;\n }\n\n this.positionAll(panX, panY, zoom);\n }\n\n mouseDown(e) {\n if (e.touches) {\n this.mouseX = e.touches[0].pageX;\n this.mouseY = e.touches[0].pageY;\n } else {\n this.mouseX = e.pageX;\n this.mouseY = e.pageY;\n }\n\n window.addEventListener('mousemove', this.mouseMove, { passive: false });\n window.addEventListener('touchmove', this.mouseTouch, { passive: false });\n window.addEventListener('mouseup', this.mouseUp, { passive: true });\n window.addEventListener('touchend', this.mouseUp, { passive: true });\n // Disable text selection in the entire document.\n document.body.style['userSelect'] = 'none';\n }\n\n // Perform image panning.\n translate(pageX, pageY) {\n const dX = pageX - this.mouseX;\n const dY = pageY - this.mouseY;\n\n this.mouseX = pageX;\n this.mouseY = pageY;\n\n // Make sure the image is within the cutout window.\n const imgRect = this.preview.current.getBoundingClientRect();\n\n // Check if the new position is within the boundaries, and if not if it's closer to them.\n let panX = Cropper.checkBound(this.state.panX, [imgRect.left, imgRect.right],\n [this.cutoutRect.left, this.cutoutRect.right], dX);\n let panY = Cropper.checkBound(this.state.panY, [imgRect.top, imgRect.bottom],\n [this.cutoutRect.top, this.cutoutRect.bottom], dY);\n\n this.positionAll(panX, panY, this.state.zoom);\n }\n\n // Image panning.\n mouseMove(e) {\n e.preventDefault();\n this.translate(e.pageX, e.pageY);\n }\n\n mouseTouch(e) {\n e.preventDefault();\n\n if (e.touches.length == 1) {\n this.translate(e.touches[0].pageX, e.touches[0].pageY);\n return;\n }\n\n // Image zooming by pinching.\n const [touch0, touch1] = e.touches;\n const distance = Math.sqrt((touch0.pageX - touch1.pageX) * (touch0.pageX - touch1.pageX) +\n (touch0.pageY - touch1.pageY) * (touch0.pageY - touch1.pageY));\n\n if (!this.prevDistance) {\n this.prevDistance = distance / this.state.zoom;\n }\n\n let scale = (distance / this.prevDistance).toFixed(3);\n this.setState({zoom: Math.max(this.minZoom, Math.min(this.maxZoom, scale))});\n }\n\n mouseUp(e) {\n window.removeEventListener('mousemove', this.mouseMove);\n window.removeEventListener('touchmove', this.mouseTouch);\n window.removeEventListener('mouseup', this.mouseUp);\n window.removeEventListener('touchend', this.mouseUp);\n // Re-enable text selection.\n document.body.style['userSelect'] = '';\n\n this.positionAll(this.state.panX, this.state.panY, this.state.zoom);\n }\n\n render() {\n // transform3d: position and scale.\n const t3d = `translate3d(${this.state.panX}px, ${this.state.panY}px, 0) scale(${this.state.zoom})`;\n // transformOrigin: zoom origin.\n const orig = `${this.state.originX}px ${this.state.originY}px`;\n\n // Overlay position and size are exactly equal to the position and size of the transformed image\n // except it uses different coordinates.\n const overlay = {\n top: `${this.originY - this.state.originY * this.state.zoom}px`,\n left: `${this.originX - this.state.originX * this.state.zoom}px`,\n width: `${this.imageWidth * this.state.zoom}px`,\n height: `${this.imageHeight * this.state.zoom}px`\n };\n return (\n \n
\n

\n
\n
\n
\n
\n \n
\n
\n );\n }\n}\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport Cropper from '../widgets/cropper.jsx';\n\nexport default class AvatarCropView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleSubmit = this.handleSubmit.bind(this);\n }\n\n handleSubmit() {\n if (this.props.onSubmit) {\n this.props.onSubmit();\n }\n }\n\n render() {\n return (\n \n
\n \n
\n
\n \n
\n
\n );\n }\n};\n","/* ContactsView holds all contacts-related stuff */\nimport React from 'react';\nimport { FormattedMessage, defineMessages } from 'react-intl';\n\nimport ContactList from '../widgets/contact-list.jsx';\n\nimport { updateFavicon } from '../lib/utils.js';\n\nconst messages = defineMessages({\n archived_contacts_title: {\n id: \"archived_contacts\",\n defaultMessage: \"Archived contacts ({count})\",\n description: \"Label for archived chats\"\n }\n});\n\nexport default class ContactsView extends React.Component {\n constructor(props) {\n super(props);\n\n this.handleAction = this.handleAction.bind(this);\n\n this.state = ContactsView.deriveStateFromProps(props);\n }\n\n static deriveStateFromProps(props) {\n const contacts = [];\n let unreadThreads = 0;\n let archivedCount = 0;\n props.chatList.map((c) => {\n const blocked = c.acs && !c.acs.isJoiner();\n // Show only blocked contacts only when props.blocked == true.\n if (blocked && props.blocked) {\n contacts.push(c);\n }\n if (blocked || props.blocked) {\n return;\n }\n\n if (c.private && c.private.arch) {\n if (props.archive) {\n contacts.push(c);\n } else {\n archivedCount ++;\n }\n } else if (!props.archive) {\n contacts.push(c);\n unreadThreads += c.unread > 0 ? 1 : 0;\n }\n });\n\n contacts.sort((a, b) => {\n return (b.touched || 0) - (a.touched || 0);\n });\n\n if (archivedCount > 0) {\n contacts.push({\n action: 'archive',\n title: messages.archived_contacts_title,\n values: {count: archivedCount}\n });\n }\n\n return {\n contactList: contacts,\n unreadThreads: unreadThreads\n };\n }\n\n componentDidUpdate(prevProps, prevState) {\n if (prevProps.chatList != this.props.chatList ||\n prevProps.archive != this.props.archive ||\n prevProps.blocked != this.props.blocked) {\n const newState = ContactsView.deriveStateFromProps(this.props);\n this.setState(newState);\n if (newState.unreadThreads != prevState.unreadThreads) {\n updateFavicon(newState.unreadThreads);\n }\n }\n }\n\n handleAction(action_ignored) {\n this.props.onShowArchive();\n }\n\n render() {\n return (\n ¯∖_(ツ)_/¯\"\n description=\"HTML message shown in ContactList when no contacts are found\">{\n (no_contacts) => \n }\n );\n }\n};\n","// Helper functions for storing values in localStorage.\n// By default localStorage can store only strings, not objects or other types.\n\nexport default class LocalStorageUtil {\n // Replace old object with the new one.\n static setObject(key, value) {\n localStorage.setItem(key, JSON.stringify(value));\n }\n\n // Get stored object.\n static getObject(key) {\n const value = localStorage.getItem(key);\n return value && JSON.parse(value);\n }\n\n // Partially or wholly update stored object.\n static updateObject(key, value) {\n const oldVal = this.getObject(key);\n this.setObject(key, Object.assign(oldVal || {}, value));\n }\n\n // Just a wrapper.\n static removeItem(key) {\n localStorage.removeItem(key);\n }\n}\n","// Account registration.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport AvatarUpload from '../widgets/avatar-upload.jsx';\nimport CheckBox from '../widgets/checkbox.jsx';\nimport VisiblePassword from '../widgets/visible-password.jsx';\n\nimport LocalStorageUtil from '../lib/local-storage.js';\nimport { theCard } from '../lib/utils.js';\n\nimport { MAX_TITLE_LENGTH } from '../config.js';\n\nexport default class CreateAccountView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n login: '',\n password: '',\n email: '',\n fn: '', // full/formatted name\n imageDataUrl: null,\n errorCleared: false,\n saveToken: LocalStorageUtil.getObject('keep-logged-in')\n };\n\n this.handleLoginChange = this.handleLoginChange.bind(this);\n this.handlePasswordChange = this.handlePasswordChange.bind(this);\n this.handleEmailChange = this.handleEmailChange.bind(this);\n this.handleFnChange = this.handleFnChange.bind(this);\n this.handleImageChanged = this.handleImageChanged.bind(this);\n this.handleToggleSaveToken = this.handleToggleSaveToken.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n }\n\n handleLoginChange(e) {\n this.setState({login: e.target.value});\n }\n\n handlePasswordChange(password) {\n this.setState({password: password});\n }\n\n handleEmailChange(e) {\n this.setState({email: e.target.value})\n }\n\n handleFnChange(e) {\n this.setState({fn: e.target.value});\n }\n\n handleImageChanged(img) {\n this.setState({imageDataUrl: img});\n }\n\n handleToggleSaveToken() {\n LocalStorageUtil.setObject('keep-logged-in', !this.state.saveToken);\n this.setState({saveToken: !this.state.saveToken});\n }\n\n handleSubmit(e) {\n e.preventDefault();\n this.setState({errorCleared: false});\n this.props.onCreateAccount(\n this.state.login.trim(),\n this.state.password.trim(),\n theCard(this.state.fn.trim().substring(0, MAX_TITLE_LENGTH), this.state.imageDataUrl),\n {'meth': 'email', 'val': this.state.email});\n }\n\n render() {\n let submitClasses = 'primary';\n if (this.props.disabled) {\n submitClasses += ' disabled';\n }\n\n return (\n \n );\n }\n};\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport AvatarUpload from '../widgets/avatar-upload.jsx';\nimport BadgeList from '../widgets/badge-list.jsx';\n\nimport { makeImageDataUrl } from '../lib/blob-helpers.js';\n\nexport default class EditAccountView extends React.Component {\n constructor(props) {\n super(props);\n\n const me = this.props.tinode.getMeTopic();\n this.state = {\n fullName: me.public ? me.public.fn : undefined,\n avatar: makeImageDataUrl(me.public ? me.public.photo : null)\n };\n }\n\n render() {\n return (\n \n
\n
\n
\n
{this.state.fullName}
\n
\n \n {this.props.myUserId}\n
\n
\n
\n
\n
\n
\n
\n
\n
\n );\n }\n};\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport AvatarUpload from '../widgets/avatar-upload.jsx';\nimport InPlaceEdit from '../widgets/in-place-edit.jsx';\nimport TagManager from '../widgets/tag-manager.jsx';\n\nimport { MAX_TITLE_LENGTH } from '../config.js';\nimport { makeImageDataUrl } from '../lib/blob-helpers.js';\nimport { arrayEqual, asEmail, asPhone, theCard } from '../lib/utils.js';\n\nexport default class AccGeneralView extends React.Component {\n constructor(props) {\n super(props);\n\n const me = this.props.tinode.getMeTopic();\n this.state = {\n fullName: me.public ? me.public.fn : undefined,\n avatar: makeImageDataUrl(me.public ? me.public.photo : null),\n tags: me.tags(),\n credentials: me.getCredentials() || [],\n addCredActive: false,\n addCredInvalid: false,\n newCred: '',\n previousOnTags: me.onTagsUpdated\n };\n\n this.tnNewTags = this.tnNewTags.bind(this);\n this.tnCredsUpdated = this.tnCredsUpdated.bind(this);\n this.handleFullNameUpdate = this.handleFullNameUpdate.bind(this);\n this.handleImageChanged = this.handleImageChanged.bind(this);\n this.handleCredChange = this.handleCredChange.bind(this);\n this.handleCredKeyDown = this.handleCredKeyDown.bind(this);\n this.handleCredEntered = this.handleCredEntered.bind(this);\n this.handleTagsUpdated = this.handleTagsUpdated.bind(this);\n }\n\n componentDidMount() {\n const me = this.props.tinode.getMeTopic();\n me.onCredsUpdated = this.tnCredsUpdated;\n me.onTagsUpdated = this.tnNewTags;\n }\n\n componentWillUnmount() {\n const me = this.props.tinode.getMeTopic();\n me.onTagsUpdated = this.state.previousOnTags;\n me.onCredsUpdated = undefined;\n }\n\n tnNewTags(tags) {\n this.setState({tags: tags});\n }\n\n tnCredsUpdated(creds) {\n this.setState({credentials: creds || []});\n }\n\n handleFullNameUpdate(fn) {\n fn = fn.trim().substring(0, MAX_TITLE_LENGTH);\n if (fn) {\n this.setState({fullName: fn});\n this.props.onUpdateAccount(undefined, theCard(fn, null));\n }\n }\n\n handleImageChanged(img) {\n this.setState({avatar: img});\n // this.props.onUpdateAccount(undefined, theCard(null, img || Tinode.DEL_CHAR));\n }\n\n handleCredChange(e) {\n this.setState({newCred: e.target.value, addCredInvalid: false});\n }\n\n handleCredKeyDown(e) {\n if (e.keyCode === 27) {\n // Escape pressed\n this.setState({newCred: '', addCredActive: false});\n } else if (e.keyCode === 13) {\n // Enter pressed\n this.handleCredEntered(e);\n }\n }\n\n handleCredEntered(e) {\n let value = this.state.newCred.trim();\n if (!value) {\n this.setState({addCredActive: false, addCredInvalid: false});\n return;\n }\n\n let val = asPhone(value);\n let method;\n if (val) {\n method = 'tel';\n } else {\n val = asEmail(value);\n if (val) {\n method = 'email';\n }\n }\n if (method) {\n this.props.onCredAdd(method, val);\n this.setState({addCredActive: false, newCred: ''});\n } else {\n this.setState({addCredInvalid: true});\n }\n }\n\n handleTagsUpdated(tags) {\n // Check if tags have actually changed.\n if (arrayEqual(this.state.tags.slice(0), tags.slice(0))) {\n return;\n }\n this.props.onUpdateTags(tags);\n }\n\n render() {\n const credentials = [];\n this.state.credentials.map((cred) => {\n credentials.push();\n });\n\n return (\n \n
\n
\n
\n
{\n (full_name_placeholder) => \n }
\n
\n
\n
\n
\n
{\n (title_tag_manager) => \n }\n
\n
\n
\n
\n {credentials}\n {this.state.addCredActive ?\n
\n : null}\n
\n
\n
\n
\n );\n }\n};\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport CheckBox from '../widgets/checkbox.jsx';\n\nexport default class EditAccountView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.handleCheckboxClick = this.handleCheckboxClick.bind(this);\n }\n\n handleCheckboxClick(what, checked) {\n if (what == 'sound') {\n this.props.onToggleMessageSounds(checked);\n } else if (what == 'alert') {\n this.props.onTogglePushNotifications(checked);\n } else if (what == 'incognito') {\n this.props.onToggleIncognitoMode(checked);\n }\n }\n\n render() {\n return (\n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n );\n }\n};\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport InPlaceEdit from '../widgets/in-place-edit.jsx';\nimport PermissionsEditor from '../widgets/permissions-editor.jsx';\n\nconst messages = defineMessages({\n delete_account: {\n id: 'delete_account',\n defaultMessage: 'Delete account',\n description: 'Title for delete account warning'\n },\n delete_account_warning: {\n id: 'delete_account_arning',\n defaultMessage: 'Are you sure you want to delete your account? It cannot be undone.',\n description: 'Warning message when deleting an account'\n }\n});\n\nclass AccSecurityView extends React.Component {\n constructor(props) {\n super(props);\n\n const me = this.props.tinode.getMeTopic();\n let blockedCount = 0;\n me.contacts((c) => {\n if (c.acs && !c.acs.isJoiner()) {\n blockedCount ++;\n }\n });\n const defacs = me.getDefaultAccess();\n this.state = {\n auth: defacs ? defacs.auth : null,\n anon: defacs ? defacs.anon : null,\n showPermissionEditorFor: undefined,\n blockedCount: blockedCount,\n };\n\n this.handlePasswordUpdate = this.handlePasswordUpdate.bind(this);\n this.handleLaunchPermissionsEditor = this.handleLaunchPermissionsEditor.bind(this);\n this.handleHidePermissionsEditor = this.handleHidePermissionsEditor.bind(this);\n this.handlePermissionsChanged = this.handlePermissionsChanged.bind(this);\n this.handleDeleteAccount = this.handleDeleteAccount.bind(this);\n }\n\n handlePasswordUpdate(pwd) {\n this.setState({password: pwd});\n this.props.onUpdateAccount(pwd);\n }\n\n handleLaunchPermissionsEditor(which) {\n this.setState({\n showPermissionEditorFor: which,\n editedPermissions: this.state[which]\n });\n }\n\n handleHidePermissionsEditor() {\n this.setState({showPermissionEditorFor: undefined});\n }\n\n handlePermissionsChanged(perm) {\n let defacs = {};\n defacs[this.state.showPermissionEditorFor] = perm;\n this.props.onUpdateAccount(undefined, undefined, defacs);\n\n let newState = {showPermissionEditorFor: undefined};\n newState[this.state.showPermissionEditorFor] = perm;\n this.setState(newState);\n }\n\n handleDeleteAccount(e) {\n e.preventDefault();\n const {formatMessage} = this.props.intl;\n this.props.onShowAlert(\n formatMessage(messages.delete_account), // title\n formatMessage(messages.delete_account_warning), // content\n (() => { this.props.onDeleteAccount(); }), // onConfirm\n null, // use default text \"OK\"\n true, // Show Reject button\n null // use default text \"Cancel\"\n );\n }\n\n render() {\n return (\n {this.state.showPermissionEditorFor ?\n \n :\n \n
\n
\n
\n {\n (password_unchanged) => \n }\n
\n
\n
\n
\n
\n
\n
\n \n
\n
\n
Auth: {this.state.auth}
\n
Anon: {this.state.anon}
\n
\n
\n {this.state.blockedCount > 0 ?\n <>\n
\n
\n >\n : null }\n
\n }\n );\n }\n};\n\nexport default injectIntl(AccSecurityView);\n","// Edit account parameters.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport { APP_NAME, LINK_CONTACT_US, LINK_PRIVACY_POLICY, LINK_TERMS_OF_SERVICE } from '../config.js';\n\nexport default class AccSupportView extends React.PureComponent {\n render() {\n return (\n \n
\n
\n
\n
\n \n {APP_NAME}\n
\n
\n \n {Tinode.getLibrary()}\n
\n
\n \n {this.props.serverVersion}\n
\n
\n \n {this.props.serverAddress}\n
\n
\n
\n );\n }\n};\n","// Login form.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport CheckBox from '../widgets/checkbox.jsx';\nimport VisiblePassword from '../widgets/visible-password.jsx';\n\nimport LocalStorageUtil from '../lib/local-storage.js';\n\nexport default class LoginView extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n login: props.login,\n password: '',\n hostName: props.serverAddress,\n saveToken: props.persist\n };\n this.handleLoginChange = this.handleLoginChange.bind(this);\n this.handlePasswordChange = this.handlePasswordChange.bind(this);\n this.handleToggleSaveToken = this.handleToggleSaveToken.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n }\n\n handleLoginChange(e) {\n this.setState({login: e.target.value});\n }\n\n handlePasswordChange(e) {\n this.setState({password: e.target.value});\n }\n\n handleToggleSaveToken() {\n this.props.onPersistenceChange(!this.state.saveToken);\n this.setState({saveToken: !this.state.saveToken});\n }\n\n handleSubmit(e) {\n e.preventDefault();\n this.props.onLogin(this.state.login.trim(), this.state.password.trim());\n }\n\n render() {\n let submitClasses = 'primary';\n if (this.props.disabled) {\n submitClasses += ' disabled';\n }\n\n return (\n \n );\n }\n};\n/* END Login */\n","import React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nconst messages = defineMessages({\n invalid_id: {\n id: 'error_invalid_id',\n defaultMessage: 'Invalid ID',\n description: 'Error message'\n }\n});\n\nclass NewTopicById extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n groupId: '',\n };\n\n this.handleChange = this.handleChange.bind(this);\n this.handleKeyPress = this.handleKeyPress.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n }\n\n handleChange(e) {\n this.setState({groupId: e.target.value});\n }\n\n handleKeyPress(e) {\n if (e.key === 'Enter') {\n this.handleSubmit(e);\n }\n }\n\n handleSubmit(e) {\n e.preventDefault();\n if (this.state.groupId) {\n const name = this.state.groupId.trim();\n const prefix = name.substr(0, 3);\n if (name.length > 3 && ['usr', 'grp', 'chn'].includes(prefix)) {\n this.props.onSubmit(name);\n } else {\n this.props.onError(this.props.intl.formatMessage(messages.invalid_id), 'err');\n }\n }\n }\n\n render() {\n return (\n \n
\n {\n (prompt) => \n }\n
\n
\n \n
\n
\n );\n }\n};\n\nexport default injectIntl(NewTopicById);\n","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport AvatarUpload from './avatar-upload.jsx';\nimport CheckBox from './checkbox.jsx';\nimport TagManager from './tag-manager.jsx';\n\nimport { MAX_TITLE_LENGTH } from '../config.js';\n\nexport default class NewTopicGroup extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n fn: '', // full/formatted name\n private: '',\n imageDataUrl: null,\n tags: [],\n isChannel: false\n };\n\n this.handleFnChange = this.handleFnChange.bind(this);\n this.handlePrivateChange = this.handlePrivateChange.bind(this);\n this.handleImageChanged = this.handleImageChanged.bind(this);\n this.handleTagsChanged = this.handleTagsChanged.bind(this);\n this.handleChannelToggle = this.handleChannelToggle.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n }\n\n handleFnChange(e) {\n this.setState({fn: e.target.value});\n }\n\n handlePrivateChange(e) {\n this.setState({private: e.target.value});\n }\n\n handleImageChanged(img) {\n this.setState({imageDataUrl: img});\n }\n\n handleTagsChanged(tags) {\n this.setState({tags: tags});\n }\n\n handleChannelToggle() {\n this.setState({isChannel: !this.state.isChannel});\n }\n\n handleSubmit(e) {\n e.preventDefault();\n\n const fn = this.state.fn.trim().substring(0, MAX_TITLE_LENGTH);\n const comment = this.state.private.trim().substring(0, MAX_TITLE_LENGTH);\n if (fn) {\n this.props.onSubmit(fn, this.state.imageDataUrl, comment, this.state.tags, this.state.isChannel);\n }\n }\n\n render() {\n let submitClasses = 'primary';\n if (this.props.disabled) {\n submitClasses += ' disabled';\n }\n return (\n \n );\n }\n};\n","import React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nexport default class SearchContacts extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n edited: false,\n search: ''\n };\n\n this.handleSearchChange = this.handleSearchChange.bind(this);\n this.handleSearch = this.handleSearch.bind(this);\n this.handleClear = this.handleClear.bind(this);\n this.handleKeyDown = this.handleKeyDown.bind(this);\n }\n\n componentWillUnmount() {\n if (this.state.edited) {\n this.setState({search: '', edited: false});\n this.props.onSearchContacts(Tinode.DEL_CHAR);\n }\n }\n\n handleSearchChange(e) {\n this.setState({search: e.target.value});\n }\n\n handleSearch(e) {\n e.preventDefault();\n var query = this.state.search.trim();\n this.setState({edited: (query.length > 0)});\n this.props.onSearchContacts(query.length > 0 ? query : Tinode.DEL_CHAR);\n }\n\n handleClear(e) {\n e.preventDefault();\n if (this.state.edited) {\n this.props.onSearchContacts(Tinode.DEL_CHAR);\n }\n this.setState({search: '', edited: false});\n }\n\n handleKeyDown(e) {\n if (e.key === 'Enter') {\n this.handleSearch(e);\n } else if (e.key === 'Escape') {\n this.handleClear();\n }\n }\n\n render() {\n return (\n \n
\n
search\n
{\n (search_placeholder) => \n }\n
\n close\n \n
\n
\n );\n }\n};\n","// Create new topic and invite users or send an invite.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport Tinode from 'tinode-sdk';\n\nimport ContactList from '../widgets/contact-list.jsx';\nimport NewTopicById from '../widgets/new-topic-by-id.jsx';\nimport NewTopicGroup from '../widgets/new-topic-group.jsx';\nimport SearchContacts from '../widgets/search-contacts.jsx';\n\nimport HashNavigation from '../lib/navigation.js';\nimport { theCard } from '../lib/utils.js';\n\nconst messages = defineMessages({\n search_for_contacts: {\n id: \"search_for_contacts\",\n defaultMessage: \"Use search to find contacts\",\n description: \"Text shown in contacts view when user entered no search query.\"\n },\n search_no_results: {\n id: \"search_no_results\",\n defaultMessage: \"Search returned no results\",\n description: \"Text shown in contacts view when query returned no results.\"\n }\n});\n\nclass NewTopicView extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = {\n tabSelected: 'p2p',\n searchQuery: null\n };\n\n this.handleTabClick = this.handleTabClick.bind(this);\n this.handleSearchContacts = this.handleSearchContacts.bind(this);\n this.handleContactSelected = this.handleContactSelected.bind(this);\n this.handleNewGroupSubmit = this.handleNewGroupSubmit.bind(this);\n this.handleGroupByID = this.handleGroupByID.bind(this);\n }\n\n componentDidMount() {\n this.props.onInitFind();\n }\n\n handleTabClick(e) {\n e.preventDefault();\n HashNavigation.navigateTo(HashNavigation.addUrlParam(window.location.hash, 'tab', e.currentTarget.dataset.id));\n this.setState({tabSelected: e.currentTarget.dataset.id});\n }\n\n handleSearchContacts(query) {\n this.props.onSearchContacts(query);\n this.setState({searchQuery: Tinode.isNullValue(query) ? null : query});\n }\n\n handleContactSelected(sel) {\n if (this.state.tabSelected === 'p2p') {\n HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab'));\n this.props.onCreateTopic(sel);\n }\n }\n\n handleNewGroupSubmit(name, dataUrl, priv, tags, isChannel) {\n HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab'));\n this.props.onCreateTopic(undefined, theCard(name, dataUrl), priv, tags, isChannel);\n }\n\n handleGroupByID(topicName) {\n HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab'));\n this.props.onCreateTopic(topicName);\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n const no_contacts_placeholder = formatMessage(this.state.searchQuery ?\n messages.search_no_results : messages.search_for_contacts);\n return (\n \n
\n {this.state.tabSelected === 'grp' ?\n
:\n this.state.tabSelected === 'byid' ?\n
:\n
\n \n \n
}\n
\n );\n }\n};\n\nexport default injectIntl(NewTopicView);\n","// A password reset form.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport VisiblePassword from '../widgets/visible-password.jsx';\n\nimport HashNavigation from '../lib/navigation.js';\n\nexport default class PasswordResetView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n email: '',\n password: ''\n };\n\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleEmailChange = this.handleEmailChange.bind(this);\n this.handlePasswordChange = this.handlePasswordChange.bind(this);\n }\n\n componentDidMount() {\n let parsed = HashNavigation.parseUrlHash(window.location.hash);\n this.setState({token: parsed.params.token, scheme: parsed.params.scheme});\n }\n\n handleSubmit(e) {\n e.preventDefault();\n if (this.state.token) {\n this.props.onReset(this.state.scheme, this.state.password.trim(), this.state.token);\n } else {\n this.props.onRequest('email', this.state.email.trim());\n }\n }\n\n handleEmailChange(e) {\n this.setState({email: e.target.value});\n }\n\n handlePasswordChange(e) {\n this.setState({password: e.target.value});\n }\n\n render() {\n let reset = (this.state.token && this.state.scheme);\n return (\n \n );\n }\n}\n","import React from 'react';\n\nimport { KNOWN_HOSTS } from '../config.js';\n\n/* Combobox for selecting host name */\n\nexport default class HostSelector extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n hostName: props.serverAddress,\n changed: false\n };\n\n this.handleHostNameChange = this.handleHostNameChange.bind(this);\n this.handleEditingFinished = this.handleEditingFinished.bind(this);\n }\n\n handleHostNameChange(e) {\n this.setState({hostName: e.target.value, changed: true});\n }\n\n handleEditingFinished() {\n if (this.state.changed) {\n this.setState({changed: false});\n this.props.onServerAddressChange(this.state.hostName.trim());\n }\n }\n\n render() {\n var hostOptions = [];\n for (let key in KNOWN_HOSTS) {\n let item = KNOWN_HOSTS[key];\n hostOptions.push(\n \n );\n }\n return (\n \n \n \n
\n );\n }\n}\n","// Tinode config panel.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport HostSelector from '../widgets/host-selector.jsx';\n\nexport default class SettingsView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n transport: props.transport || 'def',\n serverAddress: props.serverAddress,\n };\n\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleTransportSelected = this.handleTransportSelected.bind(this);\n this.handleServerAddressChange = this.handleServerAddressChange.bind(this);\n }\n\n handleSubmit(e) {\n e.preventDefault();\n this.props.onUpdate({\n transport: this.state.transport,\n serverAddress: this.state.serverAddress\n });\n }\n\n handleTransportSelected(e) {\n this.setState({transport: e.currentTarget.value});\n }\n\n handleServerAddressChange(name) {\n this.setState({serverAddress: name});\n }\n\n render() {\n const names = {def: \"default\", ws: \"websocket\", lp: \"long polling\"};\n const transportOptions = [];\n ['def', 'ws', 'lp'].map((item) => {\n const id = 'transport-' + item;\n const name = names[item];\n transportOptions.push(\n \n \n \n \n );\n });\n\n return (\n \n );\n }\n};\n","// ValidationView: panel for confirming credentials, like email or phone.\nimport React from 'react';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nconst messages = defineMessages({\n phone: {\n id: 'phone_dative',\n defaultMessage: 'phone',\n description: \"Dative case of 'phone', i.e. 'phone' in 'by phone'\",\n },\n email: {\n id: 'email_dative',\n defaultMessage: 'email',\n description: \"Dative case of 'email', i.e. 'email' in 'by email'\",\n }\n});\n\nclass ValidationView extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.state = {\n code: props.credCode || ''\n };\n\n this.handleChange = this.handleChange.bind(this);\n this.handleKeyPress = this.handleKeyPress.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleCancel = this.handleCancel.bind(this);\n }\n\n handleChange(e) {\n this.setState({code: e.target.value});\n }\n\n handleKeyPress(e) {\n if (e.key === 'Enter') {\n this.handleSubmit(e);\n } else if (e.key == 'Escape') {\n this.handleCancel(e);\n }\n }\n\n handleSubmit(e) {\n e.preventDefault();\n if (this.state.code && this.state.code.trim()) {\n this.props.onSubmit(this.props.credMethod, this.state.code.trim());\n }\n }\n\n handleCancel(e) {\n e.preventDefault();\n this.props.onCancel();\n }\n\n\n render() {\n const { formatMessage } = this.props.intl;\n const methods = {'email': formatMessage(messages.email), 'tel': formatMessage(messages.phone)};\n let method = methods[this.props.credMethod] || this.props.credMethod;\n return (\n \n
\n \n
\n
\n {\n (numbers_only) => \n }\n
\n
\n \n \n
\n
\n );\n }\n};\n\nexport default injectIntl(ValidationView);\n","import React from 'react';\nimport { defineMessages, injectIntl } from 'react-intl';\n\nimport ErrorPanel from '../widgets/error-panel.jsx';\nimport LoadSpinner from '../widgets/load-spinner.jsx';\nimport SideNavbar from '../widgets/side-navbar.jsx';\n\nimport AvatarCropView from './avatar-crop-view.jsx';\nimport ContactsView from './contacts-view.jsx';\nimport CreateAccountView from './create-account-view.jsx';\nimport EditAccountView from './edit-account-view.jsx';\nimport AccGeneralView from './acc-general-view.jsx';\nimport AccNotificationsView from './acc-notifications-view.jsx';\nimport AccSecurityView from './acc-security-view.jsx';\nimport AccSupportView from './acc-support-view.jsx';\nimport LoginView from './login-view.jsx';\nimport NewTopicView from './new-topic-view.jsx';\nimport PasswordResetView from './password-reset-view.jsx';\nimport SettingsView from './settings-view.jsx';\nimport ValidationView from './validation-view.jsx';\n\n// Panel titles for translation.\nconst messages = defineMessages({\n login: {\n id: 'sidepanel_title_login',\n description: 'Sidepanel title for LoginView.',\n defaultMessage: 'Sign In'\n },\n register: {\n id: 'sidepanel_title_register',\n description: 'Sidepanel title for CreateAccountView.',\n defaultMessage: 'Create Account'\n },\n settings: {\n id: 'sidepanel_title_settings',\n description: 'Sidepanel title for SettingsView.',\n defaultMessage: 'Settings'\n },\n edit: {\n id: 'sidepanel_title_account_settings',\n description: 'Sidepanel title for EditAccountView.',\n defaultMessage: 'Account Settings'\n },\n general: {\n id: 'sidepanel_title_acc_general',\n description: 'Sidepanel title for AccGeneralView.',\n defaultMessage: 'General'\n },\n security: {\n id: 'sidepanel_title_acc_security',\n description: 'Sidepanel title for AccSecurityView.',\n defaultMessage: 'Security'\n },\n notif: {\n id: 'sidepanel_title_acc_notifications',\n description: 'Sidepanel title for AccNotificationsView.',\n defaultMessage: 'Notifications'\n },\n support: {\n id: 'sidepanel_title_acc_support',\n description: 'Sidepanel title for AccSupportView.',\n defaultMessage: 'Support'\n },\n newtpk: {\n id: 'sidepanel_title_newtpk',\n description: 'Sidepanel title for NewTopicView.',\n defaultMessage: 'Start New Chat'\n },\n cred: {\n id: 'sidepanel_title_cred',\n description: 'Sidepanel title for ValidationView.',\n defaultMessage: 'Confirm Credentials'\n },\n crop: {\n id: 'sidepanel_title_crop',\n description: 'Sidepanel title for AvatarCropView.',\n defaultMessage: 'Drag to Adjust'\n },\n reset: {\n id: 'sidepanel_title_reset',\n description: 'Sidepanel title for PasswordResetView.',\n defaultMessage: 'Reset Password'\n },\n archive: {\n id: 'sidepanel_title_archive',\n description: 'Sidepanel title for ContactsView-Archive.',\n defaultMessage: 'Archived Chats'\n },\n blocked: {\n id: 'sidepanel_title_blocked',\n description: 'Sidepanel title for ContactsView-Blocked.',\n defaultMessage: 'Blocked Chats'\n }\n});\n\nclass SidepanelView extends React.Component {\n constructor(props) {\n super(props);\n\n this.handleNewTopic = this.handleNewTopic.bind(this);\n }\n\n handleNewTopic() {\n this.props.onBasicNavigate('newtpk');\n }\n\n render() {\n const {formatMessage} = this.props.intl;\n const view = this.props.state || (this.props.myUserId ? 'contacts' : 'login');\n\n let title, avatar, badges;\n if (view == 'contacts') {\n title = this.props.title;\n avatar = this.props.avatar ? this.props.avatar : true;\n badges = this.props.trustedBadges;\n } else {\n title = formatMessage(messages[view]);\n avatar = false;\n badges = null;\n }\n\n let onCancel;\n if (['login', 'contacts'].indexOf(view) == -1) {\n onCancel = this.props.onCancel;\n }\n\n return (\n \n
\n\n
\n\n
\n\n {view === 'login' ?\n
:\n\n view === 'register' ?\n
:\n\n view === 'settings' ?\n
:\n\n view === 'edit' ?\n
:\n\n view === 'general' ?\n
:\n\n view === 'notif' ?\n
:\n\n view === 'security' ?\n
:\n\n view === 'support' ?\n
:\n\n (view === 'contacts' || view == 'archive' || view == 'blocked') ?\n
:\n\n view === 'newtpk' ?\n
:\n\n view === 'cred' ?\n
:\n\n view === 'reset' ?\n
:\n\n view === 'crop' ?\n
:\n\n null}\n
\n );\n }\n};\n\nexport default injectIntl(SidepanelView);\n","import { KNOWN_HOSTS, DEFAULT_HOST } from '../config.js';\n\n// Detect server address from the URL\nexport function detectServerAddress() {\n let host = DEFAULT_HOST;\n if (typeof window.location == 'object') {\n if (window.location.protocol == 'file:' || window.location.hostname == 'localhost') {\n host = KNOWN_HOSTS.local;\n } else if (window.location.hostname) {\n host = window.location.hostname + (window.location.port ? ':' + window.location.port : '');\n }\n }\n return host;\n}\n\n// Detect if the page is served over HTTPS.\nexport function isSecureConnection() {\n if (typeof window.location == 'object') {\n return window.location.protocol == 'https:';\n }\n return false;\n}\n\nexport function isLocalHost() {\n if (typeof window.location == 'object') {\n return window.location.hostname == 'localhost';\n }\n return false;\n}\n","// The top-level class to hold all functionality together.\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport { FormattedMessage, defineMessages, injectIntl } from 'react-intl';\n\nimport * as firebase from 'firebase/app';\nimport 'firebase/messaging';\n\nimport Tinode from 'tinode-sdk';\n\nimport Alert from '../widgets/alert.jsx';\nimport ContextMenu from '../widgets/context-menu.jsx';\n\nimport InfoView from './info-view.jsx';\nimport MessagesView from './messages-view.jsx';\nimport SidepanelView from './sidepanel-view.jsx';\n\nimport { API_KEY, APP_NAME, DEFAULT_P2P_ACCESS_MODE, LOGGING_ENABLED,\n MEDIA_BREAKPOINT, RECEIVED_DELAY } from '../config.js';\nimport { PACKAGE_VERSION } from '../version.js';\nimport { base64ReEncode, makeImageDataUrl } from '../lib/blob-helpers.js';\nimport { detectServerAddress, isLocalHost, isSecureConnection } from '../lib/host-name.js';\nimport LocalStorageUtil from '../lib/local-storage.js';\nimport HashNavigation from '../lib/navigation.js';\nimport { secondsToTime } from '../lib/strformat.js'\nimport { updateFavicon } from '../lib/utils.js';\n\n// Sound to play on message received.\nconst POP_SOUND = new Audio('audio/msg.mp3');\n\nconst messages = defineMessages({\n reconnect_countdown: {\n id: 'reconnect_countdown',\n defaultMessage: 'Disconnected. Reconnecting in {seconds}…',\n description: 'Message shown when an app update is available.'\n },\n reconnect_now: {\n id: 'reconnect_now',\n defaultMessage: 'Try now',\n description: 'Prompt for reconnecting now'\n },\n push_init_failed: {\n id: 'push_init_failed',\n defaultMessage: 'Failed to initialize push notifications',\n description: 'Error message when push notifications have failed to initialize.'\n },\n invalid_security_token: {\n id: 'invalid_security_token',\n defaultMessage: 'Invalid security token',\n description: 'Error message when resetting password.'\n },\n no_connection: {\n id: 'no_connection',\n defaultMessage: 'No connection',\n description: 'Warning that the user is offline.'\n },\n code_doesnot_match: {\n id: 'code_doesnot_match',\n defaultMessage: 'Code does not match',\n description: 'Error message when the credential validation code is incorrect.'\n },\n menu_item_info: {\n id: 'menu_item_info',\n defaultMessage: 'Info',\n description: 'Show extended topic information'\n }\n});\n\nclass TinodeWeb extends React.Component {\n constructor(props) {\n super(props);\n\n this.state = this.getBlankState();\n\n this.handleResize = this.handleResize.bind(this);\n this.handleHashRoute = this.handleHashRoute.bind(this);\n this.handleOnline = this.handleOnline.bind(this);\n this.checkForAppUpdate = this.checkForAppUpdate.bind(this);\n this.handleVisibilityEvent = this.handleVisibilityEvent.bind(this);\n this.handleError = this.handleError.bind(this);\n this.handleLoginRequest = this.handleLoginRequest.bind(this);\n this.handlePersistenceChange = this.handlePersistenceChange.bind(this);\n this.handleConnected = this.handleConnected.bind(this);\n this.handleAutoreconnectIteration = this.handleAutoreconnectIteration.bind(this);\n this.doLogin = this.doLogin.bind(this);\n this.handleCredentialsRequest = this.handleCredentialsRequest.bind(this);\n this.handleLoginSuccessful = this.handleLoginSuccessful.bind(this);\n this.handleDisconnect = this.handleDisconnect.bind(this);\n this.tnMeMetaDesc = this.tnMeMetaDesc.bind(this);\n this.tnMeContactUpdate = this.tnMeContactUpdate.bind(this);\n this.tnMeSubsUpdated = this.tnMeSubsUpdated.bind(this);\n this.resetContactList = this.resetContactList.bind(this);\n this.tnData = this.tnData.bind(this);\n this.tnInitFind = this.tnInitFind.bind(this);\n this.tnFndSubsUpdated = this.tnFndSubsUpdated.bind(this);\n this.handleSearchContacts = this.handleSearchContacts.bind(this);\n this.handleTopicSelected = this.handleTopicSelected.bind(this);\n this.handleHideMessagesView = this.handleHideMessagesView.bind(this);\n this.handleSendMessage = this.handleSendMessage.bind(this);\n this.handleNewChatInvitation = this.handleNewChatInvitation.bind(this);\n this.handleNewAccount = this.handleNewAccount.bind(this);\n this.handleNewAccountRequest = this.handleNewAccountRequest.bind(this);\n this.handleUpdateAccountRequest = this.handleUpdateAccountRequest.bind(this);\n this.handleUpdateAccountTagsRequest = this.handleUpdateAccountTagsRequest.bind(this);\n this.handleToggleIncognitoMode = this.handleToggleIncognitoMode.bind(this);\n this.handleSettings = this.handleSettings.bind(this);\n this.handleGlobalSettings = this.handleGlobalSettings.bind(this);\n this.handleShowArchive = this.handleShowArchive.bind(this);\n this.handleShowBlocked = this.handleShowBlocked.bind(this);\n this.handleToggleMessageSounds = this.handleToggleMessageSounds.bind(this);\n this.handleCredAdd = this.handleCredAdd.bind(this);\n this.handleCredDelete = this.handleCredDelete.bind(this);\n this.handleCredConfirm = this.handleCredConfirm.bind(this);\n this.handleImageReceived = this.handleImageReceived.bind(this);\n this.initFCMessaging = this.initFCMessaging.bind(this);\n this.togglePushToken = this.togglePushToken.bind(this);\n this.requestPushToken = this.requestPushToken.bind(this);\n this.handlePushMessage = this.handlePushMessage.bind(this);\n this.handleSidepanelCancel = this.handleSidepanelCancel.bind(this);\n this.handleStartTopicRequest = this.handleStartTopicRequest.bind(this);\n this.handleNewTopicCreated = this.handleNewTopicCreated.bind(this);\n this.handleTopicUpdateRequest = this.handleTopicUpdateRequest.bind(this);\n this.handleChangePermissions = this.handleChangePermissions.bind(this);\n this.handleTagsUpdated = this.handleTagsUpdated.bind(this);\n this.handleLogout = this.handleLogout.bind(this);\n this.handleDeleteAccount = this.handleDeleteAccount.bind(this);\n this.handleDeleteMessagesRequest = this.handleDeleteMessagesRequest.bind(this);\n this.handleLeaveUnsubRequest = this.handleLeaveUnsubRequest.bind(this);\n this.handleBlockTopicRequest = this.handleBlockTopicRequest.bind(this);\n this.handleReportTopic = this.handleReportTopic.bind(this);\n this.handleShowContextMenu = this.handleShowContextMenu.bind(this);\n this.defaultTopicContextMenu = this.defaultTopicContextMenu.bind(this);\n this.handleHideContextMenu = this.handleHideContextMenu.bind(this);\n this.handleShowAlert = this.handleShowAlert.bind(this);\n this.handleShowInfoView = this.handleShowInfoView.bind(this);\n this.handleHideInfoView = this.handleHideInfoView.bind(this);\n this.handleMemberUpdateRequest = this.handleMemberUpdateRequest.bind(this);\n this.handleValidateCredentialsRequest = this.handleValidateCredentialsRequest.bind(this);\n this.handlePasswordResetRequest = this.handlePasswordResetRequest.bind(this);\n this.handleResetPassword = this.handleResetPassword.bind(this);\n this.handleContextMenuAction = this.handleContextMenuAction.bind(this);\n }\n\n getBlankState() {\n const settings = LocalStorageUtil.getObject('settings') || {};\n const persist = !!LocalStorageUtil.getObject('keep-logged-in');\n\n return {\n connected: false,\n // Connected and subscribed to 'me'\n ready: false,\n // Try to re-login on new connection.\n autoLogin: 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 incognitoMode: false,\n desktopAlerts: persist && settings.desktopAlerts,\n desktopAlertsEnabled: (isSecureConnection() || isLocalHost()) &&\n (typeof firebase != 'undefined') && (typeof navigator != 'undefined') &&\n (typeof FIREBASE_INIT != 'undefined'),\n firebaseToken: persist ? LocalStorageUtil.getObject('firebase-token') : null,\n\n applicationVisible: !document.hidden,\n\n errorText: '',\n errorLevel: null,\n errorAction: undefined,\n errorActionText: null,\n\n sidePanelSelected: 'login',\n sidePanelTitle: null,\n sidePanelAvatar: null,\n myTrustedBadges: [],\n loadSpinnerVisible: false,\n\n login: '',\n password: '',\n persist: persist,\n myUserId: null,\n liveConnection: navigator.onLine,\n topicSelected: '',\n topicSelectedOnline: false,\n topicSelectedAcs: null,\n newTopicParams: null,\n loginDisabled: false,\n displayMobile: (window.innerWidth <= MEDIA_BREAKPOINT),\n showInfoPanel: false,\n mobilePanel: 'sidepanel',\n\n contextMenuVisible: false,\n contextMenuBounds: null,\n contextMenuClickAt: null,\n contextMenuParams: null,\n contextMenuItems: [],\n\n // Modal alert dialog.\n alertVisible: false,\n alertParams: {},\n\n // Chats as shown in the ContactsView\n chatList: [],\n // Contacts returned by a search query.\n searchResults: [],\n // Merged results of a search query and p2p chats.\n searchableContacts: [],\n credMethod: undefined,\n credCode: undefined,\n // Topic to go to after login.\n requestedTopic: undefined\n };\n }\n\n componentDidMount() {\n window.addEventListener('resize', this.handleResize);\n window.addEventListener('online', (e) => { this.handleOnline(true); });\n window.addEventListener('offline', (e) => { this.handleOnline(false); });\n window.addEventListener('hashchange', this.handleHashRoute);\n // Process background notifications from the service worker.\n const serviceWorkerChannel = new BroadcastChannel('tinode-sw');\n serviceWorkerChannel.addEventListener('message', this.handlePushMessage);\n\n // Window/tab visible or invisible for pausing timers.\n document.addEventListener('visibilitychange', this.handleVisibilityEvent);\n\n this.setState({\n viewportWidth: document.documentElement.clientWidth,\n viewportHeight: document.documentElement.clientHeight\n });\n\n new Promise((resolve, reject) => {\n this.tinode = TinodeWeb.tnSetup(this.state.serverAddress, this.state.transport,\n this.props.intl.locale, this.state.persist, resolve);\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\n this.tinode.onAutoreconnectIteration = this.handleAutoreconnectIteration;\n }).then(() => {\n // Initialize desktop alerts.\n if (this.state.desktopAlertsEnabled) {\n this.initFCMessaging().then(() => {\n if (this.state.desktopAlerts) {\n if (!this.state.firebaseToken) {\n this.togglePushToken(true);\n } else {\n this.tinode.setDeviceToken(this.state.firebaseToken);\n }\n }\n }).catch(() => {\n // do nothing: handled earlier.\n // catch needed to pervent unnecessary logging of error.\n });\n }\n\n // Parse and save the hash navigation params.\n const parsedNav = HashNavigation.parseUrlHash(window.location.hash);\n\n // Read contacts from cache.\n this.resetContactList();\n\n const token = this.state.persist ? LocalStorageUtil.getObject('auth-token') : undefined;\n if (token) {\n this.setState({autoLogin: true});\n\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((err) => {\n // Socket error\n this.handleError(err.message, 'err');\n });\n }\n\n // Save possible topic name and navigate to blank state.\n this.setState({requestedTopic: parsedNav.path[1]});\n HashNavigation.navigateTo('');\n\n this.readTimer = null;\n this.readTimerCallback = null;\n\n this.handleHashRoute();\n });\n }\n\n componentWillUnmount() {\n window.removeEventListener('resize', this.handleResize);\n window.removeEventListener('hashchange', this.handleHashRoute);\n document.removeEventListener('visibilitychange', this.handleVisibilityEvent);\n }\n\n // Setup transport (usually websocket) and server address. This will terminate connection with the server.\n static tnSetup(serverAddress, transport, locale, persistentCache, onSetupCompleted) {\n const tinode = new Tinode({appName: APP_NAME, host: serverAddress, apiKey: API_KEY, transport: transport,\n secure: isSecureConnection(), persist: persistentCache}, onSetupCompleted);\n tinode.setHumanLanguage(locale);\n tinode.enableLogging(LOGGING_ENABLED, true);\n return tinode;\n }\n\n // Notifiy Tinode that a push message was received from the server.\n handlePushMessage(data) {\n if (data.what == 'msg' && Tinode.isChannelTopicName(data.topic)) {\n // The last argument is a fake user Id: otherwise the update is seen as one from the current user.\n this.tinode.oobNotification(data.topic, data.seq, 'fake-uid');\n }\n }\n\n initFCMessaging() {\n const {formatMessage, locale} = this.props.intl;\n const onError = (msg, err) => {\n console.log(msg, err);\n this.handleError(formatMessage(messages.push_init_failed), 'err');\n this.setState({desktopAlertsEnabled: false});\n }\n\n try {\n this.fbPush = firebase.initializeApp(FIREBASE_INIT, APP_NAME).messaging();\n this.fbPush.usePublicVapidKey(FIREBASE_INIT.messagingVapidKey);\n\n return navigator.serviceWorker.register('/service-worker.js').then((reg) => {\n this.checkForAppUpdate(reg);\n this.fbPush.useServiceWorker(reg);\n reg.active.postMessage(JSON.stringify({locale: locale, version: PACKAGE_VERSION}));\n\n // Google could not be bothered to mention that onTokenRefresh is never called.\n this.fbPush.onTokenRefresh(() => {\n this.requestPushToken();\n });\n\n // Handhe FCM pushes\n // (a) for channels always,\n // (b) pushes when the app is in foreground but has not focus.\n this.fbPush.onMessage(payload => { this.handlePushMessage(payload.data); });\n\n return reg;\n }).catch((err) => {\n // registration failed :(\n onError(\"Failed to register service worker:\", err);\n return Promise.reject(err);\n });\n } catch (err) {\n onError(\"Failed to initialize push notifications\", err);\n return Promise.reject(err);\n }\n }\n\n handleResize() {\n const mobile = document.documentElement.clientWidth <= MEDIA_BREAKPOINT;\n this.setState({\n viewportWidth: document.documentElement.clientWidth,\n viewportHeight: document.documentElement.clientHeight\n });\n if (this.state.displayMobile != mobile) {\n this.setState({displayMobile: mobile});\n }\n }\n\n // Check if a newer version of TinodeWeb app is available at the server.\n checkForAppUpdate(reg) {\n reg.onupdatefound = () => {\n const installingWorker = reg.installing;\n installingWorker.onstatechange = () => {\n if (installingWorker.state == 'installed' && navigator.serviceWorker.controller) {\n const msg = <>\n \n \n .>;\n this.handleError(msg, 'info');\n }\n }\n }\n }\n\n // Handle for hashchange event: display appropriate panels.\n handleHashRoute() {\n const hash = HashNavigation.parseUrlHash(window.location.hash);\n if (hash.path && hash.path.length > 0) {\n // Left-side panel selector.\n if (['register','settings','edit','notif','security','support','general',\n 'cred','crop','reset','newtpk','archive','blocked','contacts',''].includes(hash.path[0])) {\n this.setState({sidePanelSelected: hash.path[0]});\n } else {\n console.log(\"Unknown sidepanel view\", hash.path[0]);\n }\n\n // Topic for MessagesView selector.\n const topicName = hash.path[1];\n if (Tinode.topicType(topicName) && topicName != this.state.topicSelected) {\n const newState = {\n topicSelected: topicName\n };\n const acs = this.tinode.getTopicAccessMode(topicName);\n if (acs) {\n newState.topicSelectedAcs = acs;\n }\n this.setState(newState);\n }\n } else {\n // Empty hashpath\n this.setState({sidePanelSelected: ''});\n }\n\n // Save validation credentials, if available.\n if (hash.params.method) {\n this.setState({ credMethod: hash.params.method });\n }\n if (hash.params.code) {\n this.setState({ credCode: hash.params.code });\n }\n\n // Additional parameters of panels.\n this.setState({\n showInfoPanel: hash.params.info,\n newTopicTabSelected: hash.params.tab\n });\n }\n\n handleOnline(online) {\n if (online) {\n this.handleError();\n clearInterval(this.reconnectCountdown);\n this.tinode.reconnect();\n } else {\n this.handleError(this.props.intl.formatMessage(messages.no_connection), 'warn');\n }\n this.setState({liveConnection: online});\n }\n\n handleVisibilityEvent() {\n this.setState({applicationVisible: !document.hidden});\n }\n\n handleError(err, level, action, actionText) {\n this.setState({errorText: err, errorLevel: level, errorAction: action, errorActionText: actionText});\n }\n\n // User clicked Login button in the side panel.\n handleLoginRequest(login, password) {\n this.setState({\n loginDisabled: true,\n login: login,\n password: password,\n loadSpinnerVisible: true,\n autoLogin: true\n });\n this.handleError('', null);\n\n if (this.tinode.isConnected()) {\n this.doLogin(login, password, {meth: this.state.credMethod, resp: this.state.credCode});\n } else {\n this.tinode.connect().catch((err) => {\n // Socket error\n this.setState({loginDisabled: false, autoLogin: false, loadSpinnerVisible: false});\n this.handleError(err.message, 'err');\n });\n }\n }\n\n // Enable or disable saving the password and IndexedDB.\n handlePersistenceChange(persist) {\n if (persist) {\n this.tinode.initStorage().then(() => {\n console.log(\"storage initialized\");\n LocalStorageUtil.setObject('keep-logged-in', true);\n this.setState({persist: true});\n });\n } else {\n this.tinode.clearStorage().then(() => {\n console.log(\"storage cleared\");\n LocalStorageUtil.setObject('keep-logged-in', false);\n this.setState({persist: false});\n });\n }\n }\n\n // Connection succeeded.\n handleConnected() {\n // Just to be sure.\n clearInterval(this.reconnectCountdown);\n this.handleError();\n\n const params = this.tinode.getServerInfo();\n this.setState({\n serverVersion: params.ver + ' ' + (params.build ? params.build : 'none')\n });\n\n if (this.state.autoLogin) {\n this.doLogin(this.state.login, this.state.password, {meth: this.state.credMethod, resp: this.state.credCode});\n }\n }\n\n // Called for each auto-reconnect iteration.\n handleAutoreconnectIteration(sec, prom) {\n clearInterval(this.reconnectCountdown);\n\n if (sec < 0) {\n // Clear error\n this.handleError();\n return;\n }\n\n if (prom) {\n // Reconnecting now\n prom.then(() => {\n // Reconnected: clear error\n this.handleError();\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n return;\n }\n\n const {formatMessage} = this.props.intl;\n let count = sec / 1000;\n count = count | count;\n this.reconnectCountdown = setInterval(() => {\n const timeLeft = (count > 99) ? secondsToTime(count) : count;\n this.handleError(\n formatMessage(messages.reconnect_countdown, {seconds: timeLeft}),\n 'warn',\n () => {\n clearInterval(this.reconnectCountdown);\n this.tinode.reconnect();\n },\n formatMessage(messages.reconnect_now)\n );\n count -= 1;\n }, 1000);\n }\n\n // Connection lost\n handleDisconnect(err) {\n this.setState({\n connected: false,\n ready: false,\n topicSelectedOnline: false,\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 doLogin(login, password, cred) {\n if (this.tinode.isAuthenticated()) {\n // Already logged in. Go to default screen.\n HashNavigation.navigateTo('');\n return;\n }\n // Sanitize and package credentail.\n cred = Tinode.credential(cred);\n // Try to login with login/password. If they are not available, try token. If no token, ask for login/password.\n let promise = null;\n const token = this.tinode.getAuthToken();\n if (login && password) {\n this.setState({password: null});\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((ctrl) => {\n if (ctrl.code >= 300 && ctrl.text === 'validate credentials') {\n this.setState({loadSpinnerVisible: false});\n if (cred) {\n this.handleError(this.props.intl.formatMessage(messages.code_doesnot_match), 'warn');\n }\n this.handleCredentialsRequest(ctrl.params);\n } else {\n this.handleLoginSuccessful();\n }\n }).catch((err) => {\n // Login failed, report error.\n this.setState({\n loginDisabled: false,\n credMethod: undefined,\n credCode: undefined,\n loadSpinnerVisible: false,\n autoLogin: false\n });\n this.handleError(err.message, 'err');\n localStorage.removeItem('auth-token');\n HashNavigation.navigateTo('');\n });\n } else {\n // No login credentials provided.\n // Make sure we are on the login page.\n HashNavigation.navigateTo('');\n this.setState({loginDisabled: false});\n }\n }\n\n handleCredentialsRequest(params) {\n const parsed = HashNavigation.parseUrlHash(window.location.hash);\n parsed.path[0] = 'cred';\n parsed.params['method'] = params.cred[0];\n HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsed.path, parsed.params));\n }\n\n handleLoginSuccessful() {\n this.handleError();\n\n // Refresh authentication token.\n if (LocalStorageUtil.getObject('keep-logged-in')) {\n LocalStorageUtil.setObject('auth-token', this.tinode.getAuthToken());\n }\n\n const goToTopic = this.state.requestedTopic;\n // Logged in fine, subscribe to 'me' attaching callbacks from the contacts view.\n const 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 autoLogin: true,\n requestedTopic: undefined,\n });\n // Subscribe, fetch topic desc, the list of subscriptions. Messages are not fetched.\n me.subscribe(\n me.startMetaQuery().\n withLaterSub().\n withDesc().\n withTags().\n withCred().\n build()\n ).catch((err) => {\n this.tinode.disconnect();\n localStorage.removeItem('auth-token');\n this.handleError(err.message, 'err');\n HashNavigation.navigateTo('');\n }).finally(() => {\n this.setState({loadSpinnerVisible: false});\n });\n let urlHash = HashNavigation.setUrlSidePanel(window.location.hash, 'contacts');\n if (goToTopic) {\n urlHash = HashNavigation.setUrlTopic(urlHash, goToTopic);\n }\n HashNavigation.navigateTo(urlHash);\n }\n\n tnMeMetaDesc(desc) {\n if (desc) {\n if (desc.public) {\n this.setState({\n sidePanelTitle: desc.public.fn,\n sidePanelAvatar: makeImageDataUrl(desc.public.photo)\n });\n }\n if (desc.trusted) {\n const badges = [];\n for (const [key, val] of Object.entries(desc.trusted)) {\n if (val) {\n badges.push(key);\n }\n }\n this.setState({\n myTrustedBadges: badges,\n });\n }\n if (desc.acs) {\n this.setState({\n incognitoMode: !desc.acs.isPresencer()\n });\n }\n }\n }\n\n // Reactions to updates to the contact list.\n tnMeContactUpdate(what, cont) {\n if (what == 'on' || what == 'off') {\n this.resetContactList();\n if (this.state.topicSelected == cont.topic) {\n this.setState({topicSelectedOnline: (what == 'on')});\n }\n } else if (what == 'read') {\n this.resetContactList();\n } else if (what == 'msg') {\n // Check if the topic is archived. If so, don't play a sound.\n const topic = this.tinode.getTopic(cont.topic);\n const archived = topic && topic.isArchived();\n\n // New message received. Maybe the message is from the current user, then unread is 0.\n if (cont.unread > 0 && this.state.messageSounds && !archived) {\n // Skip update if the topic is currently open, otherwise the badge will annoyingly flash.\n if (document.hidden || this.state.topicSelected != cont.topic) {\n POP_SOUND.play();\n }\n }\n // Reorder contact list to use possibly updated 'touched'.\n this.resetContactList();\n } else if (what == 'recv') {\n // 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 }\n // Redraw without the deleted topic.\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({topicSelectedAcs: cont.acs});\n }\n } else if (what == 'del') {\n // TODO: messages deleted (hard or soft) -- update pill counter.\n } else if (what == 'upd') {\n // upd - handled by the SDK. Explicitly ignoring here.\n } else {\n // TODO(gene): handle other types of notifications:\n // * ua -- user agent changes (maybe display a pictogram for mobile/desktop).\n console.log(\"Unsupported (yet) presence update:\" + what + \" in: \" + cont.topic);\n }\n }\n\n tnMeSubsUpdated(unused) {\n this.resetContactList();\n }\n\n // Merge search results and contact list to create a single flat\n // list of known contacts for GroupManager to use.\n static prepareSearchableContacts(chatList, foundContacts) {\n const merged = {};\n\n // For chatList topics merge only p2p topics and convert them to the\n // same format as foundContacts.\n for (const c of chatList) {\n if (Tinode.isP2PTopicName(c.topic)) {\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 }\n\n // Add all foundCountacts if they have not been added already.\n for (const c of foundContacts) {\n if (!merged[c.user]) {\n merged[c.user] = c;\n }\n }\n\n return Object.values(merged);\n }\n\n resetContactList() {\n const newState = {\n chatList: []\n };\n\n if (!this.state.ready) {\n newState.ready = true;\n }\n\n this.tinode.getMeTopic().contacts((c) => {\n if (!c.topic && !c.user) {\n // Contacts expect c.topic to be set.\n c.topic = c.name;\n }\n\n newState.chatList.push(c);\n if (this.state.topicSelected == c.topic) {\n newState.topicSelectedOnline = c.online;\n newState.topicSelectedAcs = c.acs;\n }\n });\n // Merge search results and chat list.\n newState.searchableContacts = TinodeWeb.prepareSearchableContacts(newState.chatList, this.state.searchResults);\n this.setState(newState);\n }\n\n // Sending \"received\" notifications\n tnData(data) {\n const topic = this.tinode.getTopic(data.topic);\n if (topic.msgStatus(data, true) >= Tinode.MESSAGE_STATUS_SENT && data.from != this.state.myUserId) {\n clearTimeout(this.receivedTimer);\n this.receivedTimer = setTimeout(() => {\n this.receivedTimer = undefined;\n topic.noteRecv(data.seq);\n }, RECEIVED_DELAY);\n }\n }\n\n /* Fnd topic: find contacts by tokens */\n tnInitFind() {\n const fnd = this.tinode.getFndTopic();\n fnd.onSubsUpdated = this.tnFndSubsUpdated;\n if (fnd.isSubscribed()) {\n this.tnFndSubsUpdated();\n } else {\n fnd.subscribe(fnd.startMetaQuery().withSub().build()).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n tnFndSubsUpdated() {\n const foundContacts = [];\n // Don't attempt to create P2P topics which already exist. Server will reject the duplicates.\n this.tinode.getFndTopic().contacts((s) => {\n foundContacts.push(s);\n });\n this.setState({\n searchResults: foundContacts,\n searchableContacts: TinodeWeb.prepareSearchableContacts(this.state.chatList, foundContacts)\n });\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 handleSearchContacts(query) {\n const fnd = this.tinode.getFndTopic();\n fnd.setMeta({desc: {public: query}}).then((ctrl) => {\n return fnd.getMeta(fnd.startMetaQuery().withSub().build());\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n // User clicked on a contact in the side panel or deleted a contact.\n handleTopicSelected(topicName) {\n // Clear newTopicParams after use.\n if (this.state.newTopicParams && this.state.newTopicParams._topicName != topicName) {\n this.setState({\n newTopicParams: null\n });\n }\n\n if (topicName) {\n this.setState({\n errorText: '',\n errorLevel: null,\n mobilePanel: 'topic-view',\n showInfoPanel: false\n });\n // Different contact selected.\n if (this.state.topicSelected != topicName) {\n this.setState({\n topicSelectedOnline: this.tinode.isTopicOnline(topicName),\n topicSelectedAcs: this.tinode.getTopicAccessMode(topicName)\n });\n HashNavigation.navigateTo(HashNavigation.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 HashNavigation.navigateTo(HashNavigation.setUrlTopic('', null));\n }\n }\n\n // In mobile view user requested to show sidepanel\n handleHideMessagesView() {\n this.setState({\n mobilePanel: 'sidepanel'\n });\n HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, null));\n }\n\n // User is sending a message, either plain text or a drafty object, possibly\n // with attachments.\n // - msg - Drafty message with content\n // - promise - Promise to be resolved when the upload is completed\n // - uploader - for tracking progress\n handleSendMessage(msg, promise, uploader) {\n const topic = this.tinode.getTopic(this.state.topicSelected);\n\n msg = topic.createMessage(msg, false);\n // The uploader is used to show progress.\n msg._uploader = uploader;\n\n if (!topic.isSubscribed()) {\n if (!promise) {\n promise = Promise.resolve();\n }\n promise = promise.then(() => { return topic.subscribe(); });\n }\n\n if (promise) {\n promise = promise.catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n topic.publishDraft(msg, promise)\n .then((ctrl) => {\n if (topic.isArchived()) {\n return topic.archive(false);\n }\n })\n .catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleNewChatInvitation(topicName, action) {\n const topic = this.tinode.getTopic(topicName);\n let response = null;\n switch (action) {\n case 'accept':\n // Accept given permissions.\n const mode = topic.getAccessMode().getGiven();\n response = topic.setMeta({sub: {mode: mode}});\n if (topic.isP2PType()) {\n // For P2P topics change 'given' permission of the peer too.\n // In p2p topics the other user has the same name as the topic.\n response = response.then((ctrl) => {\n topic.setMeta({sub: {user: topicName, mode: mode}});\n });\n }\n break;\n case 'delete':\n // Ignore invitation by deleting it.\n response = topic.delTopic(true);\n break;\n case 'block':\n // Ban the topic making futher invites impossible.\n // Just self-ban.\n const am = topic.getAccessMode().updateWant('-JP').getWant();\n response = topic.setMeta({sub: {mode: am}}).then((ctrl) => {\n return this.handleTopicSelected(null);\n });\n break;\n default:\n console.log(\"Unknown invitation action\", '\"' + action + '\"\"');\n }\n\n if (response != null) {\n response.catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n // User chose a Sign Up menu item.\n handleNewAccount() {\n this.handleError();\n\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, 'register'));\n }\n\n // Actual registration of a new account.\n handleNewAccountRequest(login_, password_, public_, cred_, tags_) {\n // Clear old error, if any.\n this.handleError();\n\n this.tinode.connect(this.state.serverAddress)\n .then(() => {\n return this.tinode.createAccountBasic(login_, password_,\n {public: public_, tags: tags_, cred: Tinode.credential(cred_)});\n }).then((ctrl) => {\n if (ctrl.code >= 300 && ctrl.text == 'validate credentials') {\n this.handleCredentialsRequest(ctrl.params);\n } else {\n this.handleLoginSuccessful(this);\n }\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleUpdateAccountRequest(password, pub, defacs) {\n this.handleError();\n\n if (pub || defacs) {\n const params = {};\n if (pub) {\n params.public = pub;\n }\n if (defacs) {\n params.defacs = defacs;\n }\n this.tinode.getMeTopic().setMeta({desc: params}).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n if (password) {\n this.tinode.updateAccountBasic(null, this.tinode.getCurrentLogin(), password).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n handleToggleIncognitoMode(on) {\n const me = this.tinode.getMeTopic();\n const am = me.getAccessMode().updateWant(on ? '-P' : '+P').getWant();\n me.setMeta({sub: {mode: am}}).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleUpdateAccountTagsRequest(tags) {\n this.tinode.getMeTopic().setMeta({tags: tags})\n .catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n // User chose Settings menu item.\n handleSettings() {\n this.handleError();\n\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash,\n this.state.myUserId ? 'edit' : 'settings'));\n }\n\n // User updated global parameters.\n handleGlobalSettings(settings) {\n const serverAddress = settings.serverAddress || this.state.serverAddress;\n const transport = settings.transport || this.state.transport;\n if (this.tinode) {\n this.tinode.clearStorage();\n this.tinode.onDisconnect = undefined;\n this.tinode.disconnect();\n }\n this.tinode = TinodeWeb.tnSetup(serverAddress, transport, this.props.intl.locale,\n LocalStorageUtil.getObject('keep-logged-in'));\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\n this.tinode.onAutoreconnectIteration = this.handleAutoreconnectIteration;\n\n this.setState({\n serverAddress: serverAddress,\n transport: transport\n });\n LocalStorageUtil.setObject('settings', {\n serverAddress: serverAddress,\n transport: transport\n });\n\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, ''));\n }\n\n // User chose 'Archived chats'.\n handleShowArchive() {\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash,\n this.state.myUserId ? 'archive' : ''));\n }\n\n // User viewes 'Blocked chats'.\n handleShowBlocked() {\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash,\n this.state.myUserId ? 'blocked' : ''));\n }\n\n togglePushToken(enabled) {\n if (enabled) {\n if (!this.state.firebaseToken) {\n const fcm = this.fbPush ?\n Promise.resolve() :\n this.initFCMessaging();\n fcm.then(() => {\n return this.fbPush.requestPermission();\n }).then(() => {\n this.requestPushToken();\n }).catch((err) => {\n console.log(\"Failed to get notification permission.\", err);\n this.handleError(err.message, 'err');\n this.setState({desktopAlerts: false, firebaseToken: null});\n LocalStorageUtil.updateObject('settings', {desktopAlerts: false});\n });\n } else {\n this.setState({desktopAlerts: true});\n if (LocalStorageUtil.getObject('keep-logged-in')) {\n LocalStorageUtil.updateObject('settings', {desktopAlerts: true});\n }\n }\n } else if (this.state.firebaseToken && this.fbPush) {\n this.fbPush.deleteToken(this.state.firebaseToken).catch((err) => {\n console.log(\"Unable to delete token.\", err);\n }).finally(() => {\n LocalStorageUtil.updateObject('settings', {desktopAlerts: false});\n localStorage.removeItem('firebase-token');\n this.setState({desktopAlerts: false, firebaseToken: null});\n // Inform the server that the token was deleted.\n this.tinode.setDeviceToken(null);\n });\n } else {\n this.setState({desktopAlerts: false, firebaseToken: null});\n LocalStorageUtil.updateObject('settings', {desktopAlerts: false});\n }\n }\n\n requestPushToken() {\n this.fbPush.getToken().then((refreshedToken) => {\n const persist = LocalStorageUtil.getObject('keep-logged-in');\n if (refreshedToken != this.state.firebaseToken) {\n this.tinode.setDeviceToken(refreshedToken);\n if (persist) {\n LocalStorageUtil.setObject('firebase-token', refreshedToken);\n }\n }\n this.setState({firebaseToken: refreshedToken, desktopAlerts: true});\n if (persist) {\n LocalStorageUtil.updateObject('settings', {desktopAlerts: true});\n }\n }).catch((err) => {\n this.handleError(err.message, 'err');\n console.log(\"Failed to retrieve firebase token\", err);\n });\n }\n\n handleToggleMessageSounds(enabled) {\n this.setState({messageSounds: enabled});\n LocalStorageUtil.updateObject('settings', {\n messageSoundsOff: !enabled\n });\n }\n\n handleCredAdd(method, value) {\n const me = this.tinode.getMeTopic();\n me.setMeta({cred: {meth: method, val: value}}).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleCredDelete(method, value) {\n const me = this.tinode.getMeTopic();\n me.delCredential(method, value).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleCredConfirm(method, response) {\n this.handleCredentialsRequest({cred: [method]});\n }\n\n handleImageReceived(url) {\n this.setState({newAvatar: url});\n if (url) {\n this.basicNavigator('crop');\n }\n }\n\n // User clicked Cancel button in Setting or Sign Up panel.\n handleSidepanelCancel() {\n const parsed = HashNavigation.parseUrlHash(window.location.hash);\n let path = '';\n if (['security','support','general','notif'].includes(parsed.path[0])) {\n path = 'edit';\n } else if ('blocked' == parsed.path[0]) {\n path = 'security';\n } else if ('crop' == parsed.path[0]) {\n path = 'general';\n } else if (this.state.myUserId) {\n path = 'contacts';\n }\n parsed.path[0] = path;\n if (parsed.params) {\n delete parsed.params.code;\n delete parsed.params.method;\n delete parsed.params.tab;\n }\n HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsed.path, parsed.params));\n this.setState({errorText: '', errorLevel: null});\n }\n\n // Basic nagigator by hash value. No need to bind to this.\n basicNavigator(hash) {\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, hash));\n }\n\n // Request to start a topic, new or selected from search results, or \"by ID\".\n handleStartTopicRequest(topicName, pub, priv, tags, isChannel) {\n // Check if topic is indeed new. If not, launch it.\n if (topicName && this.tinode.isTopicCached(topicName)) {\n this.handleTopicSelected(topicName);\n return;\n }\n\n const params = {};\n if (Tinode.isP2PTopicName(topicName)) {\n // Because we are initialing the subscription, set 'want' to all permissions.\n params.sub = {mode: DEFAULT_P2P_ACCESS_MODE};\n // Give the other user all permissions too.\n params.desc = {defacs: {auth: DEFAULT_P2P_ACCESS_MODE}};\n } else {\n topicName = topicName || this.tinode.newGroupTopicName(isChannel);\n params.desc = {public: pub, private: {comment: priv}};\n params.tags = tags;\n }\n params._topicName = topicName;\n this.setState({newTopicParams: params}, () => {this.handleTopicSelected(topicName)});\n }\n\n // New topic was creted, here is the new topic name.\n 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({topicSelected: newName}, () => {\n HashNavigation.navigateTo(HashNavigation.setUrlTopic('', newName));\n });\n }\n }\n\n handleTopicUpdateRequest(topicName, pub, priv, permissions) {\n const topic = this.tinode.getTopic(topicName);\n if (topic) {\n const params = {};\n if (pub) {\n params.public = pub;\n }\n if (priv) {\n params.private = (priv === Tinode.DEL_CHAR) ?\n Tinode.DEL_CHAR : {comment: priv};\n }\n if (permissions) {\n params.defacs = permissions;\n }\n topic.setMeta({desc: params}).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n handleChangePermissions(topicName, mode, uid) {\n const topic = this.tinode.getTopic(topicName);\n if (topic) {\n const am = topic.getAccessMode();\n if (uid) {\n am.updateGiven(mode);\n mode = am.getGiven();\n } else {\n am.updateWant(mode);\n mode = am.getWant();\n }\n topic.setMeta({sub: {user: uid, mode: mode}}).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n handleTagsUpdated(topicName, tags) {\n const topic = this.tinode.getTopic(topicName);\n if (topic) {\n topic.setMeta({tags: tags}).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n }\n\n handleLogout() {\n updateFavicon(0);\n\n // Remove stored data.\n localStorage.removeItem('auth-token');\n localStorage.removeItem('firebase-token');\n localStorage.removeItem('settings');\n if (this.state.firebaseToken) {\n this.fbPush.deleteToken(this.state.firebaseToken)\n }\n\n clearInterval(this.reconnectCountdown);\n\n if (this.tinode) {\n this.tinode.clearStorage();\n this.tinode.onDisconnect = undefined;\n this.tinode.disconnect();\n }\n this.setState(this.getBlankState());\n\n this.tinode = TinodeWeb.tnSetup(this.state.serverAddress,\n this.state.transport, this.props.intl.locale, LocalStorageUtil.getObject('keep-logged-in'));\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\n this.tinode.onAutoreconnectIteration = this.handleAutoreconnectIteration;\n HashNavigation.navigateTo('');\n }\n\n handleDeleteAccount() {\n this.tinode.delCurrentUser(true).then((ctrl) => {\n this.handleLogout();\n });\n }\n\n handleDeleteMessagesRequest(topicName) {\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n // Request hard-delete all messages.\n topic.delMessagesAll(true).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleLeaveUnsubRequest(topicName) {\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n topic.leave(true).then((ctrl) => {\n // Hide MessagesView and InfoView panels.\n HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, ''));\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleBlockTopicRequest(topicName) {\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n topic.updateMode(null, '-JP').then((ctrl) => {\n // Hide MessagesView and InfoView panels.\n HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, ''));\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleReportTopic(topicName) {\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n // Publish spam report.\n this.tinode.publish(Tinode.TOPIC_SYS, Tinode.Drafty.attachJSON(null, {\n 'action': 'report',\n 'target': topicName\n }));\n\n // Remove J and P permissions.\n topic.updateMode(null, '-JP').then((ctrl) => {\n // Hide MessagesView and InfoView panels.\n HashNavigation.navigateTo(HashNavigation.setUrlTopic(window.location.hash, ''));\n }).catch((err) => {\n this.handleError(err.message, 'err');\n });\n }\n\n handleShowContextMenu(params, menuItems) {\n this.setState({\n contextMenuVisible: true,\n contextMenuClickAt: {x: params.x, y: params.y},\n contextMenuParams: params,\n contextMenuItems: menuItems || this.defaultTopicContextMenu(params.topicName),\n contextMenuBounds: ReactDOM.findDOMNode(this).getBoundingClientRect()\n });\n }\n\n defaultTopicContextMenu(topicName) {\n const topic = this.tinode.getTopic(topicName);\n\n let muted = false, blocked = false, self_blocked = false, subscribed = false, deleter = false, archived = false;\n if (topic) {\n subscribed = topic.isSubscribed();\n archived = topic.isArchived();\n\n const acs = topic.getAccessMode();\n if (acs) {\n muted = acs.isMuted();\n blocked = !acs.isJoiner();\n self_blocked = !acs.isJoiner('want');\n deleter = acs.isDeleter();\n }\n }\n\n return [\n subscribed ? {\n title: this.props.intl.formatMessage(messages.menu_item_info),\n handler: this.handleShowInfoView\n } : null,\n subscribed ? 'messages_clear' : null,\n subscribed && deleter ? 'messages_clear_hard' : null,\n muted ? (blocked ? null : 'topic_unmute') : 'topic_mute',\n self_blocked ? 'topic_unblock' : 'topic_block',\n !archived ? 'topic_restore' : 'topic_archive',\n 'topic_delete'\n ];\n }\n\n handleHideContextMenu() {\n this.setState({\n contextMenuVisible: false,\n contextMenuClickAt: null,\n contextMenuParams: null,\n contextMenuBounds: null\n });\n }\n\n handleContextMenuAction(action, promise, params) {\n if (action == 'topic_archive') {\n if (promise && params.topicName && params.topicName == this.state.topicSelected) {\n promise.then(() => {\n this.handleTopicSelected(null);\n });\n }\n }\n }\n\n handleShowAlert(title, content, onConfirm, confirmText, onReject, rejectText) {\n this.setState({\n alertVisible: true,\n alertParams: {\n title: title,\n content: content,\n onConfirm: onConfirm,\n confirm: confirmText,\n onReject: onReject,\n reject: rejectText\n }\n });\n }\n\n handleShowInfoView() {\n HashNavigation.navigateTo(HashNavigation.addUrlParam(window.location.hash, 'info', true));\n this.setState({showInfoPanel: true});\n }\n\n handleHideInfoView() {\n HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'info'));\n this.setState({showInfoPanel: false});\n }\n\n handleMemberUpdateRequest(topicName, added, removed) {\n if (!topicName) {\n return;\n }\n\n const topic = this.tinode.getTopic(topicName);\n if (!topic) {\n return;\n }\n\n if (added && added.length > 0) {\n added.map((uid) => {\n topic.invite(uid, null).catch((err) => {\n this.handleError(err.message, 'err');\n });\n });\n }\n\n if (removed && removed.length > 0) {\n removed.map((uid) => {\n topic.delSubscription(uid).catch((err) => {\n this.handleError(err.message, 'err');\n });\n });\n }\n }\n\n handleValidateCredentialsRequest(cred, code) {\n if (this.tinode.isAuthenticated()) {\n const me = this.tinode.getMeTopic();\n me.setMeta({cred: {meth: cred, resp: code}})\n .then(() => {\n HashNavigation.navigateTo('');\n })\n .catch((err) => {\n this.handleError(err.message, 'err');\n });\n } else {\n this.setState({credMethod: cred, credCode: code});\n this.doLogin(null, null, {meth: cred, resp: code});\n }\n }\n\n handlePasswordResetRequest(method, value) {\n // If already connected, connnect() will return a resolved promise.\n this.tinode.connect()\n .then(() => {\n return this.tinode.requestResetAuthSecret('basic', method, value);\n })\n .catch((err) => {\n // Socket error\n this.handleError(err.message, 'err');\n });\n }\n\n handleResetPassword(scheme, newPassword, token) {\n token = base64ReEncode(token);\n if (!token) {\n this.handleError(this.props.intl.formatMessage(messages.invalid_security_token), 'err');\n } else {\n this.tinode.connect()\n .then(() => {\n return this.tinode.updateAccountBasic(null, null, newPassword, {token: token});\n })\n .catch((err) => {\n // Socket error\n this.handleError(err.message, 'err');\n });\n }\n }\n\n render() {\n return (\n \n {this.state.contextMenuVisible ?\n
{\n if (topicName == this.state.topicSelected) {\n this.handleTopicSelected(null);\n }\n }}\n onError={this.handleError} />\n :\n null\n }\n { this.setState({alertVisible: false}); }) : null}\n reject={this.state.alertParams.reject}\n onConfirm={() => { this.setState({alertVisible: false}); this.state.alertParams.onConfirm(); }}\n confirm={this.state.alertParams.confirm}\n />\n \n\n \n\n {this.state.showInfoPanel ?\n \n :\n null\n }\n \n );\n }\n};\n\nexport default injectIntl(TinodeWeb);\n","// Put all packages together.\n// Used to generate umd/index.prod.js\n\nimport React from 'react';\nimport ReactDOM from 'react-dom';\n\nimport { IntlProvider } from 'react-intl';\n\nimport allMessages from './messages.json';\nimport TinodeWeb from './views/tinode-web.jsx';\nimport HashNavigation from './lib/navigation.js';\n\n// Detect human language to use in the UI:\n// Check parameters from URL hash #?hl=ru, then browser, then use 'en' as a fallback.\nconst { params } = HashNavigation.parseUrlHash(window.location.hash);\nconst language = (params && params.hl) ||\n (navigator.languages && navigator.languages[0]) ||\n navigator.language ||\n navigator.userLanguage ||\n 'en';\n\n// Get the base language 'en' from a more specific 'en_GB' or 'en-US' as a partial fallback.\nconst baseLanguage = language.toLowerCase().split(/[-_]/)[0];\n\n// Try the full locale first, then the locale without the region code, fallback to 'en'.\nconst messages =\n allMessages[language] ||\n allMessages[baseLanguage] ||\n allMessages.en;\n\nReactDOM.render(\n \n \n \n \n ,\n document.getElementById('mountPoint')\n);\n","// The require scope\nvar __webpack_require__ = {};\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// startup\n// Load entry module and return exports\n// This entry module doesn't tell about it's top-level declarations so it can't be inlined\nvar __webpack_exports__ = {};\n__webpack_modules__[163](0, __webpack_exports__, __webpack_require__);\n"],"sourceRoot":""}
\ No newline at end of file