= 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","// Odds and ends\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 VCard which represents topic 'public' info\nexport function vcard(fn, imageDataUrl) {\n let card = null;\n\n if ((fn && fn.trim()) || imageDataUrl) {\n card = {};\n if (fn) {\n card.fn = fn.trim();\n }\n if (imageDataUrl) {\n const dataStart = imageDataUrl.indexOf(',');\n card.photo = dataStart >= 0 ? {\n data: imageDataUrl.substring(dataStart+1),\n type: 'jpg'\n } : imageDataUrl;\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 !/^\\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.\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 if (!/^([a-z][a-z0-9+.-]*:|\\/\\/)/i.test(url)) {\n return url;\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:, or data: scheme. In case of data: scheme, the URL must start with\n// 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","// TagManager: edit topic or user tags.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport ChipInput from './chip-input.jsx';\n\nimport { 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 if (tag.length > 0) {\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 let tags = [];\n if (this.state.activated) {\n this.state.tags.map((tag) => {\n tags.push({user: tag, invalid: (tag.length < MIN_TAG_LENGTH)});\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 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 { makeImageUrl } from '../lib/blob-helpers.js';\nimport { arrayEqual, vcard } 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 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 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 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: makeImageUrl(topic.public ? topic.public.photo : null),\n private: topic.private ? topic.private.comment : null,\n address: topic.name,\n groupTopic: (topic.getType() == 'grp'),\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, vcard(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, vcard(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 toSkip = this.state.groupTopic ? 'O' : 'ASDO';\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 let topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n var 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 (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 ?\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 var downloader = this.props.tinode.getLargeFileHelper();\n this.setState({downloader: downloader});\n downloader.download(url, filename, mimetype, (loaded) => {\n this.setState({progress: loaded / this.props.size});\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.uploader) {\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.uploader && !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) || 'about:blank';\n helperFunc = null;\n }\n return (\n \n
insert_drive_file
\n
\n
{filename} {size}
\n {this.props.uploader || this.state.downloader ?\n
\n :\n
\n }\n
\n
\n );\n }\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';\n\nconst messages = defineMessages({\n 'sending': {\n 'id': 'message_sending',\n 'defaultMessage': 'sending...',\n 'description': 'Message being sent, in place of time stamp'\n },\n '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.sending);\n } else if (this.props.received == Tinode.MESSAGE_STATUS_FAILED) {\n timestamp = formatMessage(messages.failed);\n } else {\n timestamp = shortDateFormat(this.props.timestamp, this.props.intl.locale);\n }\n\n let marker = null;\n if (this.props.received <= Tinode.MESSAGE_STATUS_SENDING) {\n marker = (access_time); // watch face\n } else if (this.props.received == Tinode.MESSAGE_STATUS_FAILED) {\n marker = (warning); // yellow icon /!\\\n } else if (this.props.received == Tinode.MESSAGE_STATUS_SENT) {\n marker = (done); // checkmark\n } else if (this.props.received == Tinode.MESSAGE_STATUS_RECEIVED) {\n marker = (done_all); // double checkmark\n } else if (this.props.received == Tinode.MESSAGE_STATUS_READ) {\n marker = (done_all); // blue double checkmark\n }\n\n return (\n \n {timestamp}{'\\u00a0'}{marker}\n \n );\n }\n};\n\nexport default injectIntl(ReceivedMarker);\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 { 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 this.props.showContextMenu({ seq: this.props.seq, y: e.pageY, x: e.pageX });\n }\n\n handleProgress(ratio) {\n this.setState({progress: ratio});\n }\n\n handleCancelUpload() {\n this.props.uploader.cancel();\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 &&\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()) {\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('span', 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 = <>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 {content}\n {attachments}\n {this.props.timestamp ?\n \n : null}\n
\n {this.props.deleted ?\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\nfunction draftyFormatter(style, data, values, key) {\n let el = Drafty.tagName(style);\n if (el) {\n let attr = Drafty.attrValue(style, data) || {};\n attr.key = key;\n switch (style) {\n case 'IM':\n // Additional processing for images\n if (data) {\n attr.className = 'inline-image';\n let dim = fitImageSize(data.width, data.height,\n Math.min(this.props.viewportWidth - REM_SIZE * 4, REM_SIZE * 36), REM_SIZE * 24, false);\n dim = dim || {dstWidth: BROKEN_IMAGE_SIZE, dstHeight: BROKEN_IMAGE_SIZE};\n attr.style = { width: dim.dstWidth + 'px', height: dim.dstHeight + 'px' };\n attr.src = sanitizeImageUrl(attr.src);\n if (attr.src) {\n attr.onClick = this.handleImagePreview;\n attr.className += ' image-clickable';\n } else {\n attr.src = 'img/broken_image.png';\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 'FE':\n // Form element formatting is dependent on element content.\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 this.messageEditArea.addEventListener('paste', this.handlePasteEvent, false);\n }\n\n componentWillUnmount() {\n this.messageEditArea.removeEventListener('paste', this.handlePasteEvent, false);\n }\n\n componentDidUpdate() {\n this.messageEditArea.focus();\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) {\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(caption, this.props.content.type, this.props.content.bits,\n this.props.content.width, this.props.content.height, this.props.content.filename);\n }\n\n render() {\n if (!this.props.content) {\n return null;\n }\n const doc = iconFromMime(this.props.content.type);\n return (\n \n
\n
\n {doc}\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 { makeImageUrl } 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.type, this.props.content.bits,\n this.props.content.width, this.props.content.height, this.props.content.filename);\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 // Averate 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","import React from 'react';\n\nexport default class LoadSpinner extends React.PureComponent {\n render() {\n return (this.props.show ?\n : null);\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 var 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","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 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, KEYPRESS_DELAY, MESSAGES_PAGE, MAX_EXTERN_ATTACHMENT_SIZE,\n MAX_IMAGE_DIM, MAX_INBAND_ATTACHMENT_SIZE } from '../config.js';\nimport { SUPPORTED_IMAGE_FORMATS, filePasted, fileToBase64, imageFileToBase64,\n imageFileScaledToBase64, makeImageUrl } from '../lib/blob-helpers.js';\nimport { bytesToHumanSize, shortDateFormat } from '../lib/strformat.js';\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});\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.propsChange = this.propsChange.bind(this);\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 }\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\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 this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight - this.state.scrollPosition;\n } else if (prevProps.viewportHeight > this.props.viewportHeight) {\n this.messagesScroller.scrollTop += prevProps.viewportHeight - this.props.viewportHeight;\n }\n }\n\n const topic = this.props.tinode.getTopic(this.state.topic);\n if (this.state.topic != prevState.topic) {\n if (prevState.topic && !Tinode.isNewGroupTopicName(prevState.topic)) {\n this.leave(prevState.topic);\n this.props.readTimerHandler(null);\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 (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 // If 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 (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 docPreview: null,\n imagePreview: null,\n imagePostview: null,\n typingIndicator: false,\n scrollPosition: 0,\n fetchingMessages: false,\n peerMessagingDisabled: 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: makeImageUrl(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 } else {\n // Invalid topic.\n Object.assign(nextState, {\n messages: [],\n onlineSubs: [],\n title: '',\n avatar: null,\n peerMessagingDisabled: 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 } 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 }\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) {\n return;\n }\n let 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 }\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: makeImageUrl(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 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);\n if (status >= Tinode.MESSAGE_STATUS_SENT && msg.from != this.props.myUserId) {\n this.props.readTimerHandler(() => {\n topic.noteRead(msg.seq);\n });\n }\n this.props.onData(msg);\n }\n this.setState(newState);\n }\n\n handleAllMessagesReceived(count) {\n this.setState({fetchingMessages: false});\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 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(function(key) {\n if (data[key]) {\n params.set(key, data[key]);\n }\n });\n params.set('uid', this.props.myUserId);\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) {\n params.topicName = this.state.topic;\n const menuItems = ['message_delete'];\n const topic = this.props.tinode.getTopic(params.topicName);\n if (topic) {\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 sendFileAttachment(file) {\n if (file.size > MAX_INBAND_ATTACHMENT_SIZE) {\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(formatMessage(messages.cannot_initiate_upload));\n return;\n }\n const uploadCompletionPromise = uploader.upload(file);\n const msg = Drafty.attachFile(null, file.type, null, file.name, file.size, uploadCompletionPromise);\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, bits, fname));\n },\n this.props.onError\n );\n }\n }\n\n handleAttachFile(file) {\n if (file.size > MAX_EXTERN_ATTACHMENT_SIZE) {\n // Too large.\n this.props.onError(formatMessage(messages.file_attachment_too_large,\n {size: bytesToHumanSize(file.size), limit: bytesToHumanSize(MAX_EXTERN_ATTACHMENT_SIZE)}), '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(caption, mime, bits, width, height, fname) {\n let msg = Drafty.insertImage(null, 0, mime, bits, width, height, fname);\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 handleAttachImage(file) {\n // Check if the uploaded file is indeed an image and if it isn't too large.\n if (file.size > MAX_INBAND_ATTACHMENT_SIZE || SUPPORTED_IMAGE_FORMATS.indexOf(file.type) < 0) {\n // Convert image for size or format.\n imageFileScaledToBase64(file, MAX_IMAGE_DIM, MAX_IMAGE_DIM, false,\n // Success\n (bits, mime, width, height, fname) => {\n this.setState({imagePreview: {\n url: URL.createObjectURL(file),\n bits: bits,\n filename: fname,\n width: width,\n height: height,\n size: bits.length,\n type: mime\n }});\n },\n // Failure\n (err) => {\n this.props.onError(err, 'err');\n });\n } else {\n // Image can be uploaded as is. No conversion is needed.\n imageFileToBase64(file,\n // Success\n (bits, mime, width, height, fname) => {\n this.setState({imagePreview: {\n url: URL.createObjectURL(file),\n bits: bits,\n filename: fname,\n width: width,\n height: height,\n size: bits.length,\n type: mime\n }});\n },\n // Failure\n (err) => {\n this.props.onError(err, 'err');\n }\n );\n }\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 groupTopic = topic.getType() == 'grp';\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 const cont = this.props.tinode.getMeTopic().getContact(this.state.topic);\n if (cont && Tinode.topicType(cont.topic) == 'p2p') {\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 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","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\nimport LetterTile from './letter-tile.jsx';\nimport MenuCancel from './menu-cancel.jsx';\nimport MenuContacts from './menu-contacts.jsx';\nimport MenuStart from './menu-start.jsx';\n\nexport default class SideNavbar extends React.PureComponent {\n render() {\n return (\n \n
\n \n
\n
{this.props.title}
\n {this.props.state === 'login' ?\n
:\n this.props.state === 'contacts' ?\n
:\n null}\n {this.props.onCancel ?\n
: null}\n
\n );\n }\n};\n","/* ContactsView holds all contacts-related stuff */\nimport React from 'react';\nimport { FormattedHTMLMessage, 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 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 contacts.sort(function(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 || prevProps.archive != this.props.archive) {\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 { vcard } 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 vcard(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 = 'blue';\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 Tinode from 'tinode-sdk';\n\nimport AvatarUpload from '../widgets/avatar-upload.jsx';\nimport CheckBox from '../widgets/checkbox.jsx';\nimport InPlaceEdit from '../widgets/in-place-edit.jsx';\nimport PermissionsEditor from '../widgets/permissions-editor.jsx';\nimport TagManager from '../widgets/tag-manager.jsx';\n\nimport { LINK_CONTACT_US, LINK_PRIVACY_POLICY, LINK_TERMS_OF_SERVICE, MAX_TITLE_LENGTH } from '../config.js';\nimport { makeImageUrl } from '../lib/blob-helpers.js';\nimport { arrayEqual, asEmail, asPhone, vcard } from '../lib/utils.js';\n\nexport default class EditAccountView extends React.Component {\n constructor(props) {\n super(props);\n\n const me = this.props.tinode.getMeTopic();\n const defacs = me.getDefaultAccess();\n this.state = {\n fullName: me.public ? me.public.fn : undefined,\n avatar: makeImageUrl(me.public ? me.public.photo : null),\n auth: defacs ? defacs.auth : null,\n anon: defacs ? defacs.anon : null,\n tags: me.tags(),\n credentials: me.getCredentials() || [],\n addCredActive: false,\n addCredInvalid: false,\n newCred: '',\n showPermissionEditorFor: undefined,\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.handlePasswordUpdate = this.handlePasswordUpdate.bind(this);\n this.handleImageChanged = this.handleImageChanged.bind(this);\n this.handleCheckboxClick = this.handleCheckboxClick.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.handleLaunchPermissionsEditor = this.handleLaunchPermissionsEditor.bind(this);\n this.handleHidePermissionsEditor = this.handleHidePermissionsEditor.bind(this);\n this.handlePermissionsChanged = this.handlePermissionsChanged.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, vcard(fn, null));\n }\n }\n\n handlePasswordUpdate(pwd) {\n this.setState({password: pwd});\n this.props.onUpdateAccount(pwd);\n }\n\n handleImageChanged(img) {\n this.setState({avatar: img});\n this.props.onUpdateAccount(undefined, vcard(null, img || Tinode.DEL_CHAR));\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 }\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 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 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 let credentials = [];\n this.state.credentials.map((cred) => {\n credentials.push();\n });\n\n return (\n {this.state.showPermissionEditorFor ?\n \n :\n \n
\n
\n
\n
{\n (full_name_placeholder) => \n }
\n
\n
\n {\n (password_unchanged) => \n }\n
\n
\n
\n
\n
\n
\n \n \n
\n
\n \n \n
\n
\n
{\n (title_tag_manager) => \n }\n
\n
\n
\n \n
\n
\n {credentials}\n {this.state.addCredActive ?\n
\n : null}\n
\n
\n
\n
\n
\n
\n \n {this.props.myUserId}\n
\n
\n \n
\n
\n
Auth: {this.state.auth}
\n
Anon: {this.state.anon}
\n
\n
\n
\n
\n
\n
\n
\n \n
\n
\n
\n
\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: LocalStorageUtil.getObject('keep-logged-in')\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 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.props.onLogin(this.state.login.trim(), this.state.password.trim());\n }\n\n render() {\n var submitClasses = 'blue';\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 var name = this.state.groupId.trim();\n if (name.length > 3 && (name.substr(0, 3) == 'usr' || name.substr(0, 3) == 'grp')) {\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 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 };\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.handleTagsChanged = this.handleTagsChanged.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 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);\n }\n }\n\n render() {\n var submitClasses = 'blue';\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 { vcard } 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, undefined);\n }\n }\n\n handleNewGroupSubmit(name, dataUrl, priv, tags) {\n HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab'));\n this.props.onCreateTopic(undefined, vcard(name, dataUrl), priv, tags);\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 var transportOptions = [];\n var instance = this;\n ['def', 'ws', 'lp'].map(function(item) {\n var id = 'transport-' + item;\n var 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 ContactsView from './contacts-view.jsx';\nimport CreateAccountView from './create-account-view.jsx';\nimport EditAccountView from './edit-account-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_edit_account',\n description: 'Sidepanel title for EditAccountView.',\n defaultMessage: 'Edit Account'\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 '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});\n\nclass SidepanelView extends React.Component {\n constructor(props) {\n super(props);\n\n this.handleLoginRequested = this.handleLoginRequested.bind(this);\n }\n\n handleLoginRequested(login, password) {\n this.props.onLoginRequest(login, password);\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;\n if (view == 'contacts') {\n title = this.props.title;\n avatar = this.props.avatar ? this.props.avatar : true;\n } else {\n title = formatMessage(messages[view]);\n avatar = false;\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 === 'contacts' || view == 'archive') ?\n
:\n\n view === 'newtpk' ?\n
:\n\n view === 'cred' ?\n
:\n\n view === 'reset' ?\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 { 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, MEDIA_BREAKPOINT,\n READ_DELAY, RECEIVED_DELAY } from '../config.js';\nimport { base64ReEncode, makeImageUrl } 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 update_available: {\n id: 'update_available',\n defaultMessage: 'Update available. Reload.',\n description: 'Message shown when an app update is available.'\n },\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});\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.handleAppVisibility = this.handleAppVisibility.bind(this);\n this.handleReadTimer = this.handleReadTimer.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.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.handleSettings = this.handleSettings.bind(this);\n this.handleGlobalSettings = this.handleGlobalSettings.bind(this);\n this.handleShowArchive = this.handleShowArchive.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.initDesktopAlerts = this.initDesktopAlerts.bind(this);\n this.togglePushToken = this.togglePushToken.bind(this);\n this.requestPushToken = this.requestPushToken.bind(this);\n this.handleSidepanelCancel = this.handleSidepanelCancel.bind(this);\n this.handleNewTopic = this.handleNewTopic.bind(this);\n this.handleNewTopicRequest = this.handleNewTopicRequest.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.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\n return {\n connected: false,\n // Connected and subscribed to 'me'\n ready: false,\n transport: settings.transport || null,\n serverAddress: settings.serverAddress || detectServerAddress(),\n serverVersion: \"no connection\",\n // \"On\" is the default, so saving the \"off\" state.\n messageSounds: !settings.messageSoundsOff,\n desktopAlerts: settings.desktopAlerts,\n desktopAlertsEnabled: (isSecureConnection() || isLocalHost()) &&\n (typeof firebase != 'undefined') && (typeof navigator != 'undefined') &&\n (typeof FIREBASE_INIT != 'undefined'),\n firebaseToken: LocalStorageUtil.getObject('firebase-token'),\n\n errorText: '',\n errorLevel: null,\n errorAction: undefined,\n errorActionText: null,\n\n sidePanelSelected: 'login',\n sidePanelTitle: null,\n sidePanelAvatar: null,\n loadSpinnerVisible: false,\n\n login: '',\n password: '',\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 };\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 // 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 this.tinode = TinodeWeb.tnSetup(this.state.serverAddress, this.state.transport);\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\n this.tinode.onAutoreconnectIteration = this.handleAutoreconnectIteration;\n\n // Initialize desktop alerts.\n if (this.state.desktopAlertsEnabled) {\n try {\n this.fbPush = firebase.initializeApp(FIREBASE_INIT, APP_NAME).messaging();\n this.fbPush.usePublicVapidKey(FIREBASE_INIT.messagingVapidKey);\n navigator.serviceWorker.register('/service-worker.js').then((reg) => {\n this.checkForAppUpdate(reg);\n this.fbPush.useServiceWorker(reg);\n this.initDesktopAlerts();\n if (this.state.desktopAlerts) {\n if (!this.state.firebaseToken) {\n this.togglePushToken(true);\n } else {\n this.tinode.setDeviceToken(this.state.firebaseToken, true);\n }\n }\n }).catch((err) => {\n // registration failed :(\n console.log(\"Failed to register service worker:\", err);\n });\n } catch (err) {\n this.handleError(\"Failed to initialize push notifications\", 'err');\n console.log(\"Failed to initialize push notifications\", err);\n this.setState({desktopAlertsEnabled: false});\n }\n }\n\n const token = LocalStorageUtil.getObject('keep-logged-in') ?\n LocalStorageUtil.getObject('auth-token') : undefined;\n\n const parsedNav = HashNavigation.parseUrlHash(window.location.hash);\n if (token) {\n // When reading from storage, date is returned as string.\n token.expires = new Date(token.expires);\n this.tinode.setAuthToken(token);\n this.tinode.connect().catch((err) => {\n // Socket error\n this.handleError(err.message, 'err');\n });\n delete parsedNav.params.info;\n delete parsedNav.params.tab;\n parsedNav.path[0] = '';\n HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsedNav.path, parsedNav.params));\n } else if (!parsedNav.params.token) {\n HashNavigation.navigateTo('');\n }\n\n this.readTimer = null;\n this.readTimerCallback = null;\n\n this.handleHashRoute();\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) {\n let tinode = new Tinode(APP_NAME, serverAddress, API_KEY, transport, isSecureConnection());\n tinode.enableLogging(LOGGING_ENABLED, true);\n return tinode;\n }\n\n handleResize() {\n var 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 const {formatHTMLMessage} = this.props.intl;\n reg.onupdatefound = () => {\n const installingWorker = reg.installing;\n installingWorker.onstatechange = () => {\n if (installingWorker.state == 'installed' && navigator.serviceWorker.controller) {\n this.handleError(formatHTMLMessage(messages.update_available), 'info');\n }\n }\n }\n }\n\n // Handle for hashchange event: display appropriate panels.\n handleHashRoute() {\n var hash = HashNavigation.parseUrlHash(window.location.hash);\n if (hash.path && hash.path.length > 0) {\n // Left-side panel selector.\n if (['register','settings','edit','cred','reset','newtpk','archive','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 if (hash.path.length > 1 && hash.path[1] != this.state.topicSelected) {\n this.setState({\n topicSelected: Tinode.topicType(hash.path[1]) ? hash.path[1] : null\n });\n }\n } else {\n // Empty hashpath\n this.setState({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 var newState = {liveConnection: online};\n if (online) {\n this.handleError('', null);\n } else {\n this.handleError(\"No connection\", 'warn');\n }\n this.setState({liveConnection: online});\n }\n\n // Handling read notifications here to be able to pause\n // them then the window/tab is not visible.\n handleAppVisibility(visible, callback) {\n clearTimeout(this.readTimer);\n this.readTimerCallback = callback;\n if (visible && callback) {\n this.readTimer = setTimeout(callback, READ_DELAY);\n } else {\n this.readTimer = null;\n }\n }\n\n handleReadTimer(callback) {\n this.handleAppVisibility(!document.hidden, callback);\n }\n\n handleVisibilityEvent() {\n this.handleAppVisibility(!document.hidden, this.readTimerCallback);\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 });\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});\n this.handleError(err.message, 'err');\n });\n }\n }\n\n // Connection succeeded.\n handleConnected() {\n var params = this.tinode.getServerInfo();\n this.setState({\n serverVersion: params.ver + ' ' + (params.build ? params.build : 'none') + '; '\n });\n this.doLogin(this.state.login, this.state.password, {meth: this.state.credMethod, resp: this.state.credCode});\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 {formatHTMLMessage} = 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 formatHTMLMessage(messages.reconnect_countdown, {seconds: timeLeft}),\n 'warn',\n () => {\n clearInterval(this.reconnectCountdown);\n this.tinode.reconnect();\n },\n formatHTMLMessage(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 let 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(\"Code does not 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 });\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 var 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('', null);\n\n // Refresh authentication token.\n if (LocalStorageUtil.getObject('keep-logged-in')) {\n LocalStorageUtil.setObject('auth-token', this.tinode.getAuthToken());\n }\n // Logged in fine, subscribe to 'me' attaching callbacks from the contacts view.\n var me = this.tinode.getMeTopic();\n me.onMetaDesc = this.tnMeMetaDesc;\n me.onContactUpdate = this.tnMeContactUpdate;\n me.onSubsUpdated = this.tnMeSubsUpdated;\n this.setState({\n connected: true,\n credMethod: undefined,\n credCode: undefined,\n myUserId: this.tinode.getCurrentUserID()\n });\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 HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, 'contacts'));\n }\n\n tnMeMetaDesc(desc) {\n if (desc && desc.public) {\n this.setState({\n sidePanelTitle: desc.public.fn,\n sidePanelAvatar: makeImageUrl(desc.public.photo)\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 kown contacts for GroupManager to use.\n static prepareSearchableContacts(chatList, foundContacts) {\n let 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.topicType(c.topic) == 'p2p') {\n merged[c.topic] = {\n user: c.topic,\n updated: c.updated,\n public: c.public,\n private: c.private,\n acs: c.acs\n };\n }\n }\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 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) >= 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, unused_index, online, acs) {\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: online,\n topicSelectedAcs: acs\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 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.getType() == 'p2p') {\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();\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 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 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 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 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 HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash,\n this.state.myUserId ? 'edit' : 'settings'));\n }\n\n // User updated global parameters.\n handleGlobalSettings(settings) {\n let serverAddress = settings.serverAddress || this.state.serverAddress;\n let transport = settings.transport || this.state.transport;\n if (this.tinode) {\n this.tinode.onDisconnect = undefined;\n this.tinode.disconnect();\n }\n this.tinode = TinodeWeb.tnSetup(serverAddress, transport);\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\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 // Initialize desktop alerts = push notifications.\n initDesktopAlerts() {\n // Google could not be bothered to mention that\n // onTokenRefresh is never called.\n this.fbPush.onTokenRefresh(() => {\n this.requestPushToken();\n });\n\n this.fbPush.onMessage((payload) => {\n // No need to do anything about it.\n // All the magic happends in the service worker.\n });\n }\n\n togglePushToken(enabled) {\n if (enabled) {\n if (!this.state.firebaseToken) {\n this.fbPush.requestPermission().then(() => {\n this.requestPushToken();\n }).catch((err) => {\n this.handleError(err.message, 'err');\n this.setState({desktopAlerts: false, firebaseToken: null});\n LocalStorageUtil.updateObject('settings', {desktopAlerts: false});\n console.log(\"Failed to get permission to notify.\", err);\n });\n } else {\n this.setState({desktopAlerts: true});\n LocalStorageUtil.updateObject('settings', {desktopAlerts: true});\n }\n } else if (this.state.firebaseToken) {\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 });\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 if (refreshedToken != this.state.firebaseToken) {\n this.tinode.setDeviceToken(refreshedToken, true);\n LocalStorageUtil.setObject('firebase-token', refreshedToken);\n }\n this.setState({firebaseToken: refreshedToken, desktopAlerts: true});\n LocalStorageUtil.updateObject('settings', {desktopAlerts: true});\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 parsed.path[0] = this.state.myUserId ? 'contacts' : '';\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 // User clicked a (+) menu item.\n handleNewTopic() {\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, 'newtpk'));\n }\n\n // Request to start a new topic. New P2P topic requires peer's name.\n handleNewTopicRequest(peerName, pub, priv, tags) {\n\n const topicName = peerName || this.tinode.newGroupTopicName();\n const params = {\n _topicName: topicName,\n };\n if (peerName) {\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 params.desc = {public: pub, private: {comment: priv}};\n params.tags = tags;\n }\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 if (this.tinode) {\n this.tinode.onDisconnect = undefined;\n this.tinode.disconnect();\n }\n this.setState(this.getBlankState());\n this.tinode = TinodeWeb.tnSetup(this.state.serverAddress, this.state.transport);\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\n HashNavigation.navigateTo('');\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 const {formatMessage} = this.props.intl;\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 ? {title: formatMessage({id: 'menu_item_info'}), handler: this.handleShowInfoView} : 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_archive' : null,\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, confirm, onReject, reject) {\n this.setState({\n alertVisible: true,\n alertParams: {\n title: title,\n content: content,\n onConfirm: onConfirm,\n confirm: confirm,\n onReject: onReject,\n reject: reject\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(\"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 document.getElementById('mountPoint')\n);\n"],"sourceRoot":""}
\ No newline at end of file
+{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///external \"React\"","webpack:///external \"ReactIntl\"","webpack:///external \"Tinode\"","webpack:///external \"ReactDOM\"","webpack:///external \"firebase\"","webpack:///external [\"firebase\",\"messaging\"]","webpack:///./src/widgets/alert.jsx","webpack:///./src/version.js","webpack:///./src/config.js","webpack:///./src/lib/navigation.js","webpack:///./src/widgets/context-menu.jsx","webpack:///./src/lib/strformat.js","webpack:///./src/widgets/letter-tile.jsx","webpack:///./src/lib/blob-helpers.js","webpack:///./src/widgets/avatar-upload.jsx","webpack:///./src/widgets/checkbox.jsx","webpack:///./src/widgets/contact-badges.jsx","webpack:///./src/widgets/unread-badge.jsx","webpack:///./src/widgets/contact.jsx","webpack:///./src/widgets/contact-action.jsx","webpack:///./src/widgets/contact-list.jsx","webpack:///./src/widgets/menu-cancel.jsx","webpack:///./src/widgets/error-panel.jsx","webpack:///./src/widgets/chip.jsx","webpack:///./src/widgets/chip-input.jsx","webpack:///./src/widgets/group-manager.jsx","webpack:///./src/widgets/visible-password.jsx","webpack:///./src/widgets/in-place-edit.jsx","webpack:///./src/widgets/more-button.jsx","webpack:///./src/widgets/permissions-editor.jsx","webpack:///./src/lib/utils.js","webpack:///./src/widgets/tag-manager.jsx","webpack:///./src/views/info-view.jsx","webpack:///./src/widgets/file-progress.jsx","webpack:///./src/widgets/attachment.jsx","webpack:///./src/widgets/received-marker.jsx","webpack:///./src/widgets/chat-message.jsx","webpack:///./src/widgets/send-message.jsx","webpack:///./src/widgets/doc-preview.jsx","webpack:///./src/widgets/group-subs.jsx","webpack:///./src/widgets/image-preview.jsx","webpack:///./src/widgets/Invitation.jsx","webpack:///./src/widgets/load-spinner.jsx","webpack:///./src/views/logo-view.jsx","webpack:///./src/views/messages-view.jsx","webpack:///./src/widgets/menu-contacts.jsx","webpack:///./src/widgets/menu-start.jsx","webpack:///./src/widgets/side-navbar.jsx","webpack:///./src/views/contacts-view.jsx","webpack:///./src/lib/local-storage.js","webpack:///./src/views/create-account-view.jsx","webpack:///./src/views/edit-account-view.jsx","webpack:///./src/views/login-view.jsx","webpack:///./src/widgets/new-topic-by-id.jsx","webpack:///./src/widgets/new-topic-group.jsx","webpack:///./src/widgets/search-contacts.jsx","webpack:///./src/views/new-topic-view.jsx","webpack:///./src/views/password-reset-view.jsx","webpack:///./src/widgets/host-selector.jsx","webpack:///./src/views/settings-view.jsx","webpack:///./src/views/validation-view.jsx","webpack:///./src/views/sidepanel-view.jsx","webpack:///./src/lib/host-name.js","webpack:///./src/views/tinode-web.jsx","webpack:///./src/index.js"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","React","ReactIntl","Tinode","ReactDOM","firebase","Alert","PureComponent","this","props","visible","className","title","content","onReject","onClick","reject","id","onConfirm","confirm","APP_NAME","KNOWN_HOSTS","hosted","local","DEFAULT_HOST","DEFAULT_P2P_ACCESS_MODE","MAX_INBAND_ATTACHMENT_SIZE","HashNavigation","hash","parts","split","params","path","substr","forEach","part","item","decodeURIComponent","url","window","location","join","args","push","length","parsed","parseUrlHash","composeUrlHash","sidepanel","topic","info","messages","defineMessages","clear_messages","clear_for_all","delete_for_all","mute","unmute","topic_delete","topic_delete_warning","unblock","block","topic_block_warning","member_delete","archive","ContextMenu","Component","document","addEventListener","handlePageClick","handleEscapeKey","removeEventListener","e","findDOMNode","contains","target","preventDefault","stopPropagation","hide","keyCode","items","currentTarget","dataset","MenuItems","onAction","handler","onError","console","log","all","hard","errorHandler","tinode","getTopic","topicName","cancelSend","seq","delMessagesAll","delMessagesList","catch","err","message","result","updateMode","user","count","menu","map","handleClick","data-id","vSize","position","left","bounds","right","clickAt","x","top","bottom","y","style","formatMessage","intl","onShowAlert","deleteMessages","delete","topicPermissionSetter","then","ctrl","onTopicRemoved","delTopic","delSubscription","injectIntl","shortDateFormat","locale","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","topicType","iconColor","abs","charCodeAt","stringHash","trim","letter","charAt","alt","src","onerror","SUPPORTED_IMAGE_FORMATS","MIME_EXTENSIONS","makeImageUrl","photo","type","data","fitImageSize","width","height","maxWidth","maxHeight","forceSquare","scale","size","dstWidth","dstHeight","srcWidth","srcHeight","xoffset","yoffset","fileNameForMime","fname","mime","idx","indexOf","ext","at","lastIndexOf","substring","imageFileScaledToBase64","file","onSuccess","img","Image","crossOrigin","onload","dim","canvas","createElement","ctx","getContext","imageSmoothingEnabled","drawImage","imageBits","toDataURL","getMimeType","quality","base64DecodedLen","URL","createObjectURL","imageFileToBase64","reader","FileReader","readAsDataURL","fileToBase64","header","exec","AvatarUpload","prevProps","setState","dataUrl","files","base64bits","du","onImageChanged","randId","random","readOnly","state","href","uid","accept","onChange","handleFileUpload","htmlFor","CheckBox","checked","handleChange","icon_mapping","ContactBadges","badges","b","icon","color","UnreadBadge","Contact","onSelected","index","acs","showContextMenu","pageY","pageX","online","slice","icon_badges","showMode","getMode","isMuted","isJoiner","showCheckmark","selected","showOnline","unread","comment","handleContextClick","ContactAction","action","values","badge_you","badge_owner","ContactList","Array","isArray","topicSelected","contactNodes","contactsCount","contacts","filterFunc","filter","private","toLowerCase","public","fn","myUserId","isOwner","showUnread","connected","onTopicSelected","noScroll","dangerouslySetInnerHTML","__html","emptyListMessage","MenuCancel","onCancel","ErrorPanel","level","show","onClearError","warn","text","whiteSpace","actionText","Chip","invalid","noAvatar","required","handleCancel","ChipInput","placeholder","chips","prompt","sortedChips","sortChips","staticMembers","chipIndex","indexChips","keep","normal","includes","concat","prevState","deriveStateFromProps","input","removed","onChipRemoved","removeChipAt","focused","onFocusLost","onEnter","handleChipCancel","undefined","avatarDisabled","handleTextInput","onFocus","handleFocusGained","onBlur","handleFocusLost","onKeyDown","handleKeyDown","autoFocus","no_contacts","contacts_not_found","GroupManager","members","delta","present","keepInitial","requiredMember","stat","sel","userId","status","selectedContacts","splice","val","msg","query","contactFilter","noContactsMessage","instance","added","keys","k","onSubmit","defaultMessage","handleContactFilter","handleMemberRemoved","doContactFiltering","handleContactSelected","handleSubmit","indexMembers","keepInitialMembers","VisiblePassword","onFinished","handleEditingFinished","setTimeout","activeElement","tabIndex","autoComplete","handeTextChange","handleVisibility","InPlaceEdit","newValue","initialValue","active","focus","event","checkValidity","fieldType","spanText","spanClass","handlePasswordFinished","handleStartEditing","MoreButton","open","onToggle","handleToggle","PermissionsEditor","replace","sort","names","joiner","writer","preser","approver","sharer","deleter","owner","skip","compare","userTitle","userAvatar","modeTitle","compareTitle","updateFavicon","oldIcon","getElementById","head","getElementsByTagName","newIcon","rel","removeChild","appendChild","vcard","imageDataUrl","card","dataStart","arrayEqual","a","sanitizeUrl","allowedSchemes","test","schemes","RegExp","TagManager","nextProps","tags","activated","tagInput","last","handleAddTag","tag","onTagsChanged","add_tags_prompt","handleRemoveTag","handleTagInput","defautMessage","description","requested","granted","edit_permissions","other_user","clear_messages_warning","delete_messages","delete_messages_warning","leave_chat","leave_chat_warning","block_contact","block_contact_warning","report_chat","report_chat_warning","InfoView","onMetaDesc","previousMetaDesc","previousSubsUpdated","onSubsUpdated","getType","previousTagsUpdated","onTagsUpdated","resetDesc","resetSubs","newState","contactList","user2","subscriber","modeGiven2","getGiven","modeWant2","getWant","subscribers","sub","defacs","getDefaultAccess","getAccessMode","admin","isAdmin","isSharer","isDeleter","muted","fullName","address","groupTopic","showMemberPanel","access","modeGiven","modeWant","auth","anon","getMeta","startMetaQuery","withTags","build","desc","subs","onTopicDescUpdate","DEL_CHAR","ignored","onChangePermissions","perm","showPermissionEditorFor","userPermissionsEdited","which","toEdit","toCompare","toSkip","titleEdit","titleCompare","userPermissionsTitle","userPermissionsAvatar","editedPermissions","immutablePermissions","editedPermissionsTitle","immutablePermissionsTitle","editedPermissionsSkipped","onInitFind","onMemberUpdateRequest","onDeleteMessages","onLeaveTopic","onBlockTopic","onReportTopic","selectedContact","moreInfoExpanded","onTopicTagsUpdate","menuItems","handleLaunchPermissionsEditor","displayMobile","errorLevel","errorText","searchableContacts","handleHideAddMembers","handleMemberUpdateRequest","handlePermissionsChanged","handleHidePermissionsEditor","handleFullNameUpdate","private_placeholder","handlePrivateUpdate","handleImageChanged","handleMuted","more","handleMoreInfo","handleTagsUpdated","handleDeleteMessages","handleLeave","handleBlock","handleReport","handleShowAddMembers","no_members","handleMemberSelected","handleContextMenu","FileProgress","progress","Attachment","filename","mimetype","downloader","getLargeFileHelper","download","loaded","uploader","onCancelUpload","cancel","helperFunc","downloadUrl","downloadFile","ReceivedMarker","timestamp","received","MESSAGE_STATUS_SENDING","sending","MESSAGE_STATUS_FAILED","failed","marker","MESSAGE_STATUS_SENT","MESSAGE_STATUS_RECEIVED","MESSAGE_STATUS_READ","ChatMessage","onImagePreview","resp","act","ref","onFormResponse","ratio","sideClass","deleted","sequence","response","bubbleClass","fullDisplay","userFrom","attachments","mimeType","Drafty","getContentType","att","getDownloadUrl","isUploading","getEntitySize","handleCancelUpload","format","draftyFormatter","userName","notFound","onProgress","handleProgress","handleImagePreview","handleFormButtonClick","el","tagName","attr","attrValue","viewportWidth","REM_SIZE","sanitizedUrl","sanitizeImageUrl","inner","Children","child","SendMessage","messageEditArea","handlePasteEvent","disabled","onImageSuccess","onAttachmentSuccess","clipboardData","originalEvent","kind","getAsFile","filePasted","bits","onAttachImage","onAttachFile","acceptBlank","noInput","onSendMessage","shiftKey","handleSend","onKeyPress","getTime","keypressTimestamp","messaging_disabled","messagePrompt","type_new_message","attachImage","click","attachFile","handleMessageTyping","handleKeyPress","handleAttachFile","display","handleAttachImage","DocPreview","onClose","mimeToIcon","default","image","video","handleSendDoc","GroupSubs","usersOnline","totalCount","countToShow","some","overflow","ImagePreview","node","getBoundingClientRect","caption","maxlength","max","assignWidth","handleSendImage","Invitation","evt","handleButtonAction","LoadSpinner","LogoView","version","getLibrary","hideSelf","serverVersion","serverAddress","online_now","last_seen","isUnconfirmed","ex","getExcessive","isPeerRestricted","ms","getMissing","MessagesView","nextState","docPreview","imagePreview","imagePostview","typingIndicator","scrollPosition","fetchingMessages","msgs","assign","onlineSubs","peer","p2pPeerDesc","peerMessagingDisabled","isWriter","isReader","readingBlocked","unconformed","unconfirmed","messagesScroller","handleScrollEvent","scrollTop","scrollHeight","viewportHeight","isNewGroupTopicName","leave","readTimerHandler","onData","handleNewMessage","onAllMessagesReceived","handleAllMessagesReceived","onInfo","handleInfoReceipt","handleDescChange","handleSubsUpdated","onPres","isSubscribed","ready","newTopic","newTopicParams","_topicName","getQuery","withLaterDesc","withLaterSub","withLaterData","withLaterDel","setQuery","subscribe","onNewTopicCreated","queuedMessages","pub","_sending","publishMessage","blankState","getDerivedStateFromProps","not_found","oldTopicName","oldTopic","finally","msgHasMoreMessages","getMessagesPage","isNewMessage","msgStatus","from","noteRead","what","clearTimeout","keyPressTimer","KEYPRESS_DELAY","forceUpdate","sendMessage","attachJSON","parse","searchParams","set","search","onNewChat","noteKeyPress","cannot_initiate_upload","uploadCompletionPromise","upload","file_attachment_too_large","limit","insertImage","appendLineBreak","append","init","component","component2","handleClosePreview","sendImageAttachment","sendFileAttachment","messageNodes","previousFrom","chatBoxClass","nextFrom","isReply","deliveryStatus","userDesc","hi","ts","_uploader","handleShowContextMenuMessage","handleImagePostview","handleFormResponse","lastSeen","cont","getMeTopic","getContact","seen","when","onHideMessagesView","handleScrollReference","handleEnablePeer","handleNewChatAcceptance","sendKeyPress","MenuContacts","onNewTopic","onSettings","MenuStart","onSignUp","SideNavbar","archived_contacts_title","ContactsView","unreadThreads","archivedCount","chatList","arch","touched","onShowArchive","handleAction","LocalStorageUtil","localStorage","setItem","JSON","stringify","getItem","oldVal","getObject","setObject","removeItem","CreateAccountView","login","password","email","saveToken","errorCleared","onCreateAccount","submitClasses","login_prompt","handleLoginChange","password_prompt","handlePasswordChange","full_name_prompt","handleFnChange","email_prompt","handleEmailChange","handleToggleSaveToken","stay_logged_in","EditAccountView","me","onCredsUpdated","tnCredsUpdated","tnNewTags","previousOnTags","creds","credentials","onUpdateAccount","pwd","onToggleMessageSounds","onTogglePushNotifications","newCred","addCredInvalid","addCredActive","handleCredEntered","method","onCredAdd","onUpdateTags","cred","meth","done","onCredConfirm","onCredDelete","Fragment","full_name_placeholder","password_unchanged","handlePasswordUpdate","messageSounds","handleCheckboxClick","desktopAlertsEnabled","desktopAlerts","title_tag_manager","handleCredChange","handleCredKeyDown","onLogout","getCredentials","LoginView","onLogin","autoCorrect","autoCapitalize","hostName","invalid_id","NewTopicById","groupId","NewTopicGroup","handlePrivateChange","handleTagsChanged","SearchContacts","edited","onSearchContacts","handleSearch","handleClear","search_placeholder","handleSearchChange","search_for_contacts","search_no_results","NewTopicView","navigateTo","addUrlParam","tabSelected","searchQuery","isNullValue","removeUrlParam","onCreateTopic","priv","no_contacts_placeholder","handleTabClick","handleNewGroupSubmit","handleGroupByID","handleSearchContacts","searchResults","PasswordResetView","token","scheme","onReset","onRequest","reset","HostSelector","changed","onServerAddressChange","hostOptions","list","handleHostNameChange","SettingsView","onUpdate","transport","def","ws","lp","transportOptions","handleTransportSelected","handleServerAddressChange","phone","ValidationView","code","credMethod","numbers_only","credCode","SidepanelView","onLoginRequest","view","errorAction","errorActionText","loadSpinnerVisible","loginDisabled","handleLoginRequested","onGlobalSettings","onUpdateAccountTags","onValidateCredentials","onPasswordResetRequest","onResetPassword","isSecureConnection","protocol","POP_SOUND","Audio","update_available","reconnect_countdown","reconnect_now","TinodeWeb","enableLogging","foundContacts","merged","updated","host","settings","hostname","port","messageSoundsOff","FIREBASE_INIT","firebaseToken","sidePanelSelected","sidePanelTitle","sidePanelAvatar","liveConnection","onLine","topicSelectedOnline","topicSelectedAcs","innerWidth","showInfoPanel","mobilePanel","contextMenuVisible","contextMenuBounds","contextMenuClickAt","contextMenuParams","contextMenuItems","alertVisible","alertParams","handleResize","handleOnline","handleHashRoute","handleVisibilityEvent","documentElement","clientWidth","clientHeight","tnSetup","onConnect","handleConnected","onDisconnect","handleDisconnect","onAutoreconnectIteration","handleAutoreconnectIteration","fbPush","messaging","usePublicVapidKey","messagingVapidKey","serviceWorker","register","reg","checkForAppUpdate","useServiceWorker","initDesktopAlerts","setDeviceToken","togglePushToken","handleError","parsedNav","expires","setAuthToken","connect","tab","readTimer","readTimerCallback","mobile","formatHTMLMessage","onupdatefound","installingWorker","installing","onstatechange","controller","newTopicTabSelected","callback","handleAppVisibility","hidden","isConnected","doLogin","getServerInfo","ver","sec","prom","clearInterval","reconnectCountdown","setInterval","seconds","timeLeft","reconnect","isAuthenticated","credential","promise","getAuthToken","loginBasic","loginToken","handleCredentialsRequest","handleLoginSuccessful","tnMeMetaDesc","onContactUpdate","tnMeContactUpdate","tnMeSubsUpdated","getCurrentUserID","withDesc","withCred","disconnect","setUrlSidePanel","resetContactList","archived","isArchived","play","handleTopicSelected","prepareSearchableContacts","receivedTimer","noteRecv","fnd","getFndTopic","tnFndSubsUpdated","withSub","setMeta","unused_index","setUrlTopic","createMessage","Promise","resolve","publishDraft","am","updateWant","login_","password_","public_","cred_","tags_","createAccountBasic","updateAccountBasic","getCurrentLogin","onTokenRefresh","requestPushToken","onMessage","payload","enabled","updateObject","requestPermission","deleteToken","getToken","refreshedToken","delCredential","peerName","newGroupTopicName","oldName","newName","permissions","updateGiven","getBlankState","publish","TOPIC_SYS","defaultTopicContextMenu","blocked","self_blocked","subscribed","handleShowInfoView","invite","requestResetAuthSecret","newPassword","str","btoa","atob","base64ReEncode","handleHideContextMenu","handleShowAlert","handleContextMenuAction","handleGlobalSettings","handleNewAccount","handleSettings","handleLoginRequest","handleNewAccountRequest","handleUpdateAccountRequest","handleUpdateAccountTagsRequest","handleToggleMessageSounds","handleCredAdd","handleCredDelete","handleCredConfirm","handleNewTopicRequest","handleNewTopic","handleLogout","handleSidepanelCancel","handleValidateCredentialsRequest","handlePasswordResetRequest","handleResetPassword","handleShowArchive","tnInitFind","handleShowContextMenu","handleHideMessagesView","tnData","handleNewTopicCreated","handleReadTimer","handleChangePermissions","handleNewChatInvitation","handleSendMessage","handleTopicUpdateRequest","handleHideInfoView","handleDeleteMessagesRequest","handleLeaveUnsubRequest","handleBlockTopicRequest","handleReportTopic","onAddMember","handleManageGroupMembers","hl","languages","baseLanguage","allMessages","en","render","textComponent"],"mappings":"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","file":"index.prod.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/umd/\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 7);\n","module.exports = React;","module.exports = ReactIntl;","module.exports = Tinode;","module.exports = ReactDOM;","module.exports = firebase;","module.exports = 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.16.3-rc5\";\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.16');\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 shortest allowed tag length. Matches the value on the server.\nexport const MIN_TAG_LENGTH = 4;\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\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 avatar image is uploaded, it's resized to\n// a square of this size.\nexport const AVATAR_SIZE = 128;\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// Maximum in-band (included directly into the message) attachment size which fits into\n// a message of 256K in size, assuming base64 encoding and 1024 bytes of overhead.\n// This is size of an object *before* base64 encoding is applied.\n// Increase this limit to a greater value in production, if desired. Also increase\n// max_message_size in server config.\n// MAX_INBAND_ATTACHMENT_SIZE = base64DecodedLen(max_message_size - overhead);\nexport const MAX_INBAND_ATTACHMENT_SIZE = 195840;\n\n// Absolute maximum attachment size to be used with the server = 8MB. Increase to\n// something like 100MB in production.\nexport const MAX_EXTERN_ATTACHMENT_SIZE = 1 << 23;\n\n// Maximum allowed linear dimension of an inline image in pixels. You may want\n// to adjust it to 1600 or 2400 for production.\nexport const MAX_IMAGE_DIM = 768;\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// Link for \"Contact Us\".\nexport const LINK_CONTACT_US = 'email:info@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","// 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 let parts = hash.split('?', 2);\n let 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 var url = path.join('/');\n var args = [];\n for (var 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 var 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 var 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 var 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 var 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","// 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 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 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});\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({id: 'menu_item_clear_messages'}), // title\n formatMessage({id: '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({id: 'menu_item_clear_messages_for_all'}), // title\n formatMessage({id: '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 '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({id: 'menu_item_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({id: 'menu_item_delete_topic'}), // 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().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 // menu_item_edit_permissions is defined elsewhere.\n 'permissions': {\n id: 'permissions',\n title: formatMessage({id: 'menu_item_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, '-J')\n },\n 'member_unblock': {\n id: 'member_unblock',\n title: formatMessage(messages.unblock),\n handler: this.topicPermissionSetter.bind(this, '+J')\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 // 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 if (then.getMonth() == now.getMonth() && then.getDate() == now.getDate()) {\n\t return then.toLocaleTimeString(locale, {hour12: false, hour: '2-digit', minute: '2-digit'});\n } else {\n\t return then.toLocaleDateString(locale,\n {hour12: false, month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit'});\n }\n }\n return then.toLocaleDateString(locale,\n {hour12: false, year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit'});\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","// File and image helper functions.\nimport { MAX_INBAND_ATTACHMENT_SIZE, MAX_IMAGE_DIM } from '../config.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 makeImageUrl(photo) {\n return (photo && photo.type && photo.data) ?\n 'data:image/' + photo.type + ';base64,' + photo.data : 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 let scale = Math.min(\n Math.min(width, maxWidth) / width,\n Math.min(height, maxHeight) / height\n );\n\n let 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 var idx = SUPPORTED_IMAGE_FORMATS.indexOf(mime);\n var ext = MIME_EXTENSIONS[idx];\n\n var at = fname.lastIndexOf('.');\n if (at >= 0) {\n fname = fname.substring(0, at);\n }\n return fname + '.' + ext;\n}\n\n// Convert uploaded image into a base64-encoded string possibly scaling\n// linear dimensions or constraining to a square.\nexport function imageFileScaledToBase64(file, width, height, forceSquare, onSuccess, onError) {\n var img = new Image();\n img.crossOrigin = 'Anonymous';\n img.onerror = function(err) {\n onError(\"Image format unrecognized\");\n }\n img.onload = function() {\n var dim = fitImageSize(this.width, this.height, width, height, forceSquare);\n if (!dim) {\n onError(\"Invalid image\");\n return;\n }\n var canvas = document.createElement('canvas');\n canvas.width = dim.dstWidth;\n canvas.height = dim.dstHeight;\n var 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 var mime = (this.width != dim.dstWidth ||\n this.height != dim.dstHeight ||\n SUPPORTED_IMAGE_FORMATS.indexOf(file.type) < 0) ? 'image/jpeg' : file.type;\n var imageBits = canvas.toDataURL(mime);\n var parts = imageBits.split(',');\n // Get actual image type: 'data:image/png;base64,'\n mime = getMimeType(parts[0]);\n if (!mime) {\n onError(\"Unsupported image format\");\n return;\n }\n // Ensure the image is not too large\n var quality = 0.78;\n if (base64DecodedLen(imageBits.length) > MAX_INBAND_ATTACHMENT_SIZE) {\n mime = 'image/jpeg';\n }\n if (mime == 'image/jpeg') {\n // Reduce size of the jpeg by reducing image quality\n while (base64DecodedLen(imageBits.length) > MAX_INBAND_ATTACHMENT_SIZE && quality > 0.45) {\n imageBits = canvas.toDataURL(mime, quality);\n quality *= 0.84;\n }\n }\n if (base64DecodedLen(imageBits.length) > MAX_INBAND_ATTACHMENT_SIZE) {\n onError(\"The image size \" + bytesToHumanSize(base64DecodedLen(imageBits.length)) +\n \" exceeds the \" + bytesToHumanSize(MAX_INBAND_ATTACHMENT_SIZE) + \" limit.\", \"err\");\n return;\n }\n canvas = null;\n onSuccess(imageBits.split(',')[1], mime, dim.dstWidth, dim.dstHeight, fileNameForMime(file.name, mime));\n };\n img.src = URL.createObjectURL(file);\n}\n\n// Convert uploaded image file to base64-encoded string without scaling/converting the image\nexport function imageFileToBase64(file, onSuccess, onError) {\n var reader = new FileReader();\n reader.addEventListener('load', function() {\n var parts = reader.result.split(',');\n var mime = getMimeType(parts[0]);\n if (!mime) {\n onError(\"Failed to process image file\");\n return;\n }\n\n // Get image size.\n var img = new Image();\n img.crossOrigin = 'Anonymous';\n img.onload = function() {\n onSuccess(parts[1], mime, this.width, this.height, fileNameForMime(file.name, mime));\n }\n img.onerror = function(err) {\n onError(\"Image format not recognized\");\n }\n img.src = URL.createObjectURL(file);\n }, false);\n reader.readAsDataURL(file);\n}\n\nexport function fileToBase64(file, onSuccess, onError) {\n var 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// 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';\n\nimport { AVATAR_SIZE } from '../config.js';\nimport { imageFileScaledToBase64, makeImageUrl } 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 };\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 imageFileScaledToBase64(e.target.files[0], AVATAR_SIZE, AVATAR_SIZE, true,\n // Success\n (base64bits, mime) => {\n var du = makeImageUrl({data: base64bits, type: mime});\n this.setState({dataUrl: du});\n this.props.onImageChanged(du);\n },\n // Failure\n (err) => {\n this.props.onError(err, 'err');\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 var randId = \"file-input-avatar-\" + (Math.random() + '').substr(2);\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","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'};\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(function(b) {\n if (b.icon) {\n badges.push({icon_mapping[b.icon]});\n } else {\n const style = 'badge' + (b.color ? ' ' + b.color : '');\n badges.push({b.name});\n }\n });\n return <>{badges}>;\n }\n return null;\n }\n};\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 { FormattedMessage } from 'react-intl';\n\nimport LetterTile from './letter-tile.jsx';\nimport ContactBadges from './contact-badges.jsx';\nimport UnreadBadge from './unread-badge.jsx';\n\nexport default class 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 this.props.onSelected(this.props.item, this.props.index, this.props.now, this.props.acs);\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.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 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 \n
\n {this.props.comment ?
{this.props.comment}
: null}\n
\n
\n {this.props.showContextMenu ?\n \n \n expand_more\n \n : null}\n \n );\n }\n};\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 Contact from './contact.jsx';\nimport ContactAction from './contact-action.jsx';\n\nimport { makeImageUrl } from '../lib/blob-helpers.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 = c.topic ? 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 let content = [key];\n if (c.private && c.private.comment) {\n content.push(('' + c.private.comment).toLowerCase());\n }\n if (c.public && c.public.fn) {\n content.push(('' + c.public.fn).toLowerCase());\n }\n if (!this.props.filterFunc(this.props.filter, content)) {\n return;\n }\n }\n\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 const comment = Array.isArray(c.private) ?\n c.private.join(',') : (c.private ? c.private.comment : null);\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();}}>close\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 { makeImageUrl } 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: {\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, {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 { makeImageUrl } 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","// Odds and ends\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 VCard which represents topic 'public' info\nexport function vcard(fn, imageDataUrl) {\n let card = null;\n\n if ((fn && fn.trim()) || imageDataUrl) {\n card = {};\n if (fn) {\n card.fn = fn.trim();\n }\n if (imageDataUrl) {\n const dataStart = imageDataUrl.indexOf(',');\n card.photo = dataStart >= 0 ? {\n data: imageDataUrl.substring(dataStart+1),\n type: 'jpg'\n } : imageDataUrl;\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 !/^\\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.\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 if (!/^([a-z][a-z0-9+.-]*:|\\/\\/)/i.test(url)) {\n return url;\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:, or data: scheme. In case of data: scheme, the URL must start with\n// 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","// TagManager: edit topic or user tags.\nimport React from 'react';\nimport { FormattedMessage } from 'react-intl';\n\nimport ChipInput from './chip-input.jsx';\n\nimport { 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 if (tag.length > 0) {\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 let tags = [];\n if (this.state.activated) {\n this.state.tags.map((tag) => {\n tags.push({user: tag, invalid: (tag.length < MIN_TAG_LENGTH)});\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 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 { makeImageUrl } from '../lib/blob-helpers.js';\nimport { arrayEqual, vcard } 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 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 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 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: makeImageUrl(topic.public ? topic.public.photo : null),\n private: topic.private ? topic.private.comment : null,\n address: topic.name,\n groupTopic: (topic.getType() == 'grp'),\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, vcard(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, vcard(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 toSkip = this.state.groupTopic ? 'O' : 'ASDO';\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 let topic = this.props.tinode.getTopic(this.props.topic);\n if (!topic) {\n return;\n }\n var 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 (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 ?\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 var downloader = this.props.tinode.getLargeFileHelper();\n this.setState({downloader: downloader});\n downloader.download(url, filename, mimetype, (loaded) => {\n this.setState({progress: loaded / this.props.size});\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.uploader) {\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.uploader && !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) || 'about:blank';\n helperFunc = null;\n }\n return (\n \n
insert_drive_file
\n
\n
{filename} {size}
\n {this.props.uploader || this.state.downloader ?\n
\n :\n
\n }\n
\n
\n );\n }\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';\n\nconst messages = defineMessages({\n 'sending': {\n 'id': 'message_sending',\n 'defaultMessage': 'sending...',\n 'description': 'Message being sent, in place of time stamp'\n },\n '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.sending);\n } else if (this.props.received == Tinode.MESSAGE_STATUS_FAILED) {\n timestamp = formatMessage(messages.failed);\n } else {\n timestamp = shortDateFormat(this.props.timestamp, this.props.intl.locale);\n }\n\n let marker = null;\n if (this.props.received <= Tinode.MESSAGE_STATUS_SENDING) {\n marker = (access_time); // watch face\n } else if (this.props.received == Tinode.MESSAGE_STATUS_FAILED) {\n marker = (warning); // yellow icon /!\\\n } else if (this.props.received == Tinode.MESSAGE_STATUS_SENT) {\n marker = (done); // checkmark\n } else if (this.props.received == Tinode.MESSAGE_STATUS_RECEIVED) {\n marker = (done_all); // double checkmark\n } else if (this.props.received == Tinode.MESSAGE_STATUS_READ) {\n marker = (done_all); // blue double checkmark\n }\n\n return (\n \n {timestamp}{'\\u00a0'}{marker}\n \n );\n }\n};\n\nexport default injectIntl(ReceivedMarker);\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 { 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 this.props.showContextMenu({ seq: this.props.seq, y: e.pageY, x: e.pageX });\n }\n\n handleProgress(ratio) {\n this.setState({progress: ratio});\n }\n\n handleCancelUpload() {\n this.props.uploader.cancel();\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 &&\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()) {\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('span', 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 = <>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 {content}\n {attachments}\n {this.props.timestamp ?\n \n : null}\n
\n {this.props.deleted ?\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\nfunction draftyFormatter(style, data, values, key) {\n let el = Drafty.tagName(style);\n if (el) {\n let attr = Drafty.attrValue(style, data) || {};\n attr.key = key;\n switch (style) {\n case 'IM':\n // Additional processing for images\n if (data) {\n attr.className = 'inline-image';\n let dim = fitImageSize(data.width, data.height,\n Math.min(this.props.viewportWidth - REM_SIZE * 4, REM_SIZE * 36), REM_SIZE * 24, false);\n dim = dim || {dstWidth: BROKEN_IMAGE_SIZE, dstHeight: BROKEN_IMAGE_SIZE};\n attr.style = { width: dim.dstWidth + 'px', height: dim.dstHeight + 'px' };\n attr.src = sanitizeImageUrl(attr.src);\n if (attr.src) {\n attr.onClick = this.handleImagePreview;\n attr.className += ' image-clickable';\n } else {\n attr.src = 'img/broken_image.png';\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 'FE':\n // Form element formatting is dependent on element content.\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 { makeImageUrl } 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.type, this.props.content.bits,\n this.props.content.width, this.props.content.height, this.props.content.filename);\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 // Averate 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","import React from 'react';\n\nexport default class LoadSpinner extends React.PureComponent {\n render() {\n return (this.props.show ?\n : null);\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 var 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","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 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, KEYPRESS_DELAY, MESSAGES_PAGE, MAX_EXTERN_ATTACHMENT_SIZE,\n MAX_IMAGE_DIM, MAX_INBAND_ATTACHMENT_SIZE } from '../config.js';\nimport { SUPPORTED_IMAGE_FORMATS, filePasted, fileToBase64, imageFileToBase64,\n imageFileScaledToBase64, makeImageUrl } from '../lib/blob-helpers.js';\nimport { bytesToHumanSize, shortDateFormat } from '../lib/strformat.js';\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});\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.propsChange = this.propsChange.bind(this);\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 }\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\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 this.messagesScroller.scrollTop = this.messagesScroller.scrollHeight - this.state.scrollPosition;\n } else if (prevProps.viewportHeight > this.props.viewportHeight) {\n this.messagesScroller.scrollTop += prevProps.viewportHeight - this.props.viewportHeight;\n }\n }\n\n const topic = this.props.tinode.getTopic(this.state.topic);\n if (this.state.topic != prevState.topic) {\n if (prevState.topic && !Tinode.isNewGroupTopicName(prevState.topic)) {\n this.leave(prevState.topic);\n this.props.readTimerHandler(null);\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 (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 // If 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 (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 docPreview: null,\n imagePreview: null,\n imagePostview: null,\n typingIndicator: false,\n scrollPosition: 0,\n fetchingMessages: false,\n peerMessagingDisabled: 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: makeImageUrl(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 } else {\n // Invalid topic.\n Object.assign(nextState, {\n messages: [],\n onlineSubs: [],\n title: '',\n avatar: null,\n peerMessagingDisabled: 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 } 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 }\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) {\n return;\n }\n let 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 }\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: makeImageUrl(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 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);\n if (status >= Tinode.MESSAGE_STATUS_SENT && msg.from != this.props.myUserId) {\n this.props.readTimerHandler(() => {\n topic.noteRead(msg.seq);\n });\n }\n this.props.onData(msg);\n }\n this.setState(newState);\n }\n\n handleAllMessagesReceived(count) {\n this.setState({fetchingMessages: false});\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 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(function(key) {\n if (data[key]) {\n params.set(key, data[key]);\n }\n });\n params.set('uid', this.props.myUserId);\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) {\n params.topicName = this.state.topic;\n const menuItems = ['message_delete'];\n const topic = this.props.tinode.getTopic(params.topicName);\n if (topic) {\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 sendFileAttachment(file) {\n if (file.size > MAX_INBAND_ATTACHMENT_SIZE) {\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(formatMessage(messages.cannot_initiate_upload));\n return;\n }\n const uploadCompletionPromise = uploader.upload(file);\n const msg = Drafty.attachFile(null, file.type, null, file.name, file.size, uploadCompletionPromise);\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, bits, fname));\n },\n this.props.onError\n );\n }\n }\n\n handleAttachFile(file) {\n if (file.size > MAX_EXTERN_ATTACHMENT_SIZE) {\n // Too large.\n this.props.onError(formatMessage(messages.file_attachment_too_large,\n {size: bytesToHumanSize(file.size), limit: bytesToHumanSize(MAX_EXTERN_ATTACHMENT_SIZE)}), '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(caption, mime, bits, width, height, fname) {\n let msg = Drafty.insertImage(null, 0, mime, bits, width, height, fname);\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 handleAttachImage(file) {\n // Check if the uploaded file is indeed an image and if it isn't too large.\n if (file.size > MAX_INBAND_ATTACHMENT_SIZE || SUPPORTED_IMAGE_FORMATS.indexOf(file.type) < 0) {\n // Convert image for size or format.\n imageFileScaledToBase64(file, MAX_IMAGE_DIM, MAX_IMAGE_DIM, false,\n // Success\n (bits, mime, width, height, fname) => {\n this.setState({imagePreview: {\n url: URL.createObjectURL(file),\n bits: bits,\n filename: fname,\n width: width,\n height: height,\n size: bits.length,\n type: mime\n }});\n },\n // Failure\n (err) => {\n this.props.onError(err, 'err');\n });\n } else {\n // Image can be uploaded as is. No conversion is needed.\n imageFileToBase64(file,\n // Success\n (bits, mime, width, height, fname) => {\n this.setState({imagePreview: {\n url: URL.createObjectURL(file),\n bits: bits,\n filename: fname,\n width: width,\n height: height,\n size: bits.length,\n type: mime\n }});\n },\n // Failure\n (err) => {\n this.props.onError(err, 'err');\n }\n );\n }\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 groupTopic = topic.getType() == 'grp';\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 const cont = this.props.tinode.getMeTopic().getContact(this.state.topic);\n if (cont && Tinode.topicType(cont.topic) == 'p2p') {\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 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","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\nimport LetterTile from './letter-tile.jsx';\nimport MenuCancel from './menu-cancel.jsx';\nimport MenuContacts from './menu-contacts.jsx';\nimport MenuStart from './menu-start.jsx';\n\nexport default class SideNavbar extends React.PureComponent {\n render() {\n return (\n \n
\n \n
\n
{this.props.title}
\n {this.props.state === 'login' ?\n
:\n this.props.state === 'contacts' ?\n
:\n null}\n {this.props.onCancel ?\n
: null}\n
\n );\n }\n};\n","/* ContactsView holds all contacts-related stuff */\nimport React from 'react';\nimport { FormattedHTMLMessage, 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 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 contacts.sort(function(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 || prevProps.archive != this.props.archive) {\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 { vcard } 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 vcard(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 = 'blue';\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 Tinode from 'tinode-sdk';\n\nimport AvatarUpload from '../widgets/avatar-upload.jsx';\nimport CheckBox from '../widgets/checkbox.jsx';\nimport InPlaceEdit from '../widgets/in-place-edit.jsx';\nimport PermissionsEditor from '../widgets/permissions-editor.jsx';\nimport TagManager from '../widgets/tag-manager.jsx';\n\nimport { LINK_CONTACT_US, LINK_PRIVACY_POLICY, LINK_TERMS_OF_SERVICE, MAX_TITLE_LENGTH } from '../config.js';\nimport { makeImageUrl } from '../lib/blob-helpers.js';\nimport { arrayEqual, asEmail, asPhone, vcard } from '../lib/utils.js';\n\nexport default class EditAccountView extends React.Component {\n constructor(props) {\n super(props);\n\n const me = this.props.tinode.getMeTopic();\n const defacs = me.getDefaultAccess();\n this.state = {\n fullName: me.public ? me.public.fn : undefined,\n avatar: makeImageUrl(me.public ? me.public.photo : null),\n auth: defacs ? defacs.auth : null,\n anon: defacs ? defacs.anon : null,\n tags: me.tags(),\n credentials: me.getCredentials() || [],\n addCredActive: false,\n addCredInvalid: false,\n newCred: '',\n showPermissionEditorFor: undefined,\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.handlePasswordUpdate = this.handlePasswordUpdate.bind(this);\n this.handleImageChanged = this.handleImageChanged.bind(this);\n this.handleCheckboxClick = this.handleCheckboxClick.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.handleLaunchPermissionsEditor = this.handleLaunchPermissionsEditor.bind(this);\n this.handleHidePermissionsEditor = this.handleHidePermissionsEditor.bind(this);\n this.handlePermissionsChanged = this.handlePermissionsChanged.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, vcard(fn, null));\n }\n }\n\n handlePasswordUpdate(pwd) {\n this.setState({password: pwd});\n this.props.onUpdateAccount(pwd);\n }\n\n handleImageChanged(img) {\n this.setState({avatar: img});\n this.props.onUpdateAccount(undefined, vcard(null, img || Tinode.DEL_CHAR));\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 }\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 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 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 let credentials = [];\n this.state.credentials.map((cred) => {\n credentials.push();\n });\n\n return (\n {this.state.showPermissionEditorFor ?\n \n :\n \n
\n
\n
\n
{\n (full_name_placeholder) => \n }
\n
\n
\n {\n (password_unchanged) => \n }\n
\n
\n
\n
\n
\n
\n \n \n
\n
\n \n \n
\n
\n
{\n (title_tag_manager) => \n }\n
\n
\n
\n \n
\n
\n {credentials}\n {this.state.addCredActive ?\n
\n : null}\n
\n
\n
\n
\n
\n
\n \n {this.props.myUserId}\n
\n
\n \n
\n
\n
Auth: {this.state.auth}
\n
Anon: {this.state.anon}
\n
\n
\n
\n
\n
\n
\n
\n \n
\n
\n
\n
\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: LocalStorageUtil.getObject('keep-logged-in')\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 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.props.onLogin(this.state.login.trim(), this.state.password.trim());\n }\n\n render() {\n var submitClasses = 'blue';\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 var name = this.state.groupId.trim();\n if (name.length > 3 && (name.substr(0, 3) == 'usr' || name.substr(0, 3) == 'grp')) {\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 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 };\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.handleTagsChanged = this.handleTagsChanged.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 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);\n }\n }\n\n render() {\n var submitClasses = 'blue';\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 { vcard } 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, undefined);\n }\n }\n\n handleNewGroupSubmit(name, dataUrl, priv, tags) {\n HashNavigation.navigateTo(HashNavigation.removeUrlParam(window.location.hash, 'tab'));\n this.props.onCreateTopic(undefined, vcard(name, dataUrl), priv, tags);\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 var transportOptions = [];\n var instance = this;\n ['def', 'ws', 'lp'].map(function(item) {\n var id = 'transport-' + item;\n var 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 ContactsView from './contacts-view.jsx';\nimport CreateAccountView from './create-account-view.jsx';\nimport EditAccountView from './edit-account-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_edit_account',\n description: 'Sidepanel title for EditAccountView.',\n defaultMessage: 'Edit Account'\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 '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});\n\nclass SidepanelView extends React.Component {\n constructor(props) {\n super(props);\n\n this.handleLoginRequested = this.handleLoginRequested.bind(this);\n }\n\n handleLoginRequested(login, password) {\n this.props.onLoginRequest(login, password);\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;\n if (view == 'contacts') {\n title = this.props.title;\n avatar = this.props.avatar ? this.props.avatar : true;\n } else {\n title = formatMessage(messages[view]);\n avatar = false;\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 === 'contacts' || view == 'archive') ?\n
:\n\n view === 'newtpk' ?\n
:\n\n view === 'cred' ?\n
:\n\n view === 'reset' ?\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 { 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, MEDIA_BREAKPOINT,\n READ_DELAY, RECEIVED_DELAY } from '../config.js';\nimport { base64ReEncode, makeImageUrl } 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 update_available: {\n id: 'update_available',\n defaultMessage: 'Update available. Reload.',\n description: 'Message shown when an app update is available.'\n },\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});\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.handleAppVisibility = this.handleAppVisibility.bind(this);\n this.handleReadTimer = this.handleReadTimer.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.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.handleSettings = this.handleSettings.bind(this);\n this.handleGlobalSettings = this.handleGlobalSettings.bind(this);\n this.handleShowArchive = this.handleShowArchive.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.initDesktopAlerts = this.initDesktopAlerts.bind(this);\n this.togglePushToken = this.togglePushToken.bind(this);\n this.requestPushToken = this.requestPushToken.bind(this);\n this.handleSidepanelCancel = this.handleSidepanelCancel.bind(this);\n this.handleNewTopic = this.handleNewTopic.bind(this);\n this.handleNewTopicRequest = this.handleNewTopicRequest.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.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\n return {\n connected: false,\n // Connected and subscribed to 'me'\n ready: false,\n transport: settings.transport || null,\n serverAddress: settings.serverAddress || detectServerAddress(),\n serverVersion: \"no connection\",\n // \"On\" is the default, so saving the \"off\" state.\n messageSounds: !settings.messageSoundsOff,\n desktopAlerts: settings.desktopAlerts,\n desktopAlertsEnabled: (isSecureConnection() || isLocalHost()) &&\n (typeof firebase != 'undefined') && (typeof navigator != 'undefined') &&\n (typeof FIREBASE_INIT != 'undefined'),\n firebaseToken: LocalStorageUtil.getObject('firebase-token'),\n\n errorText: '',\n errorLevel: null,\n errorAction: undefined,\n errorActionText: null,\n\n sidePanelSelected: 'login',\n sidePanelTitle: null,\n sidePanelAvatar: null,\n loadSpinnerVisible: false,\n\n login: '',\n password: '',\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 };\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 // 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 this.tinode = TinodeWeb.tnSetup(this.state.serverAddress, this.state.transport);\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\n this.tinode.onAutoreconnectIteration = this.handleAutoreconnectIteration;\n\n // Initialize desktop alerts.\n if (this.state.desktopAlertsEnabled) {\n try {\n this.fbPush = firebase.initializeApp(FIREBASE_INIT, APP_NAME).messaging();\n this.fbPush.usePublicVapidKey(FIREBASE_INIT.messagingVapidKey);\n navigator.serviceWorker.register('/service-worker.js').then((reg) => {\n this.checkForAppUpdate(reg);\n this.fbPush.useServiceWorker(reg);\n this.initDesktopAlerts();\n if (this.state.desktopAlerts) {\n if (!this.state.firebaseToken) {\n this.togglePushToken(true);\n } else {\n this.tinode.setDeviceToken(this.state.firebaseToken, true);\n }\n }\n }).catch((err) => {\n // registration failed :(\n console.log(\"Failed to register service worker:\", err);\n });\n } catch (err) {\n this.handleError(\"Failed to initialize push notifications\", 'err');\n console.log(\"Failed to initialize push notifications\", err);\n this.setState({desktopAlertsEnabled: false});\n }\n }\n\n const token = LocalStorageUtil.getObject('keep-logged-in') ?\n LocalStorageUtil.getObject('auth-token') : undefined;\n\n const parsedNav = HashNavigation.parseUrlHash(window.location.hash);\n if (token) {\n // When reading from storage, date is returned as string.\n token.expires = new Date(token.expires);\n this.tinode.setAuthToken(token);\n this.tinode.connect().catch((err) => {\n // Socket error\n this.handleError(err.message, 'err');\n });\n delete parsedNav.params.info;\n delete parsedNav.params.tab;\n parsedNav.path[0] = '';\n HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsedNav.path, parsedNav.params));\n } else if (!parsedNav.params.token) {\n HashNavigation.navigateTo('');\n }\n\n this.readTimer = null;\n this.readTimerCallback = null;\n\n this.handleHashRoute();\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) {\n let tinode = new Tinode(APP_NAME, serverAddress, API_KEY, transport, isSecureConnection());\n tinode.enableLogging(LOGGING_ENABLED, true);\n return tinode;\n }\n\n handleResize() {\n var 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 const {formatHTMLMessage} = this.props.intl;\n reg.onupdatefound = () => {\n const installingWorker = reg.installing;\n installingWorker.onstatechange = () => {\n if (installingWorker.state == 'installed' && navigator.serviceWorker.controller) {\n this.handleError(formatHTMLMessage(messages.update_available), 'info');\n }\n }\n }\n }\n\n // Handle for hashchange event: display appropriate panels.\n handleHashRoute() {\n var hash = HashNavigation.parseUrlHash(window.location.hash);\n if (hash.path && hash.path.length > 0) {\n // Left-side panel selector.\n if (['register','settings','edit','cred','reset','newtpk','archive','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 if (hash.path.length > 1 && hash.path[1] != this.state.topicSelected) {\n this.setState({\n topicSelected: Tinode.topicType(hash.path[1]) ? hash.path[1] : null\n });\n }\n } else {\n // Empty hashpath\n this.setState({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 var newState = {liveConnection: online};\n if (online) {\n this.handleError('', null);\n } else {\n this.handleError(\"No connection\", 'warn');\n }\n this.setState({liveConnection: online});\n }\n\n // Handling read notifications here to be able to pause\n // them then the window/tab is not visible.\n handleAppVisibility(visible, callback) {\n clearTimeout(this.readTimer);\n this.readTimerCallback = callback;\n if (visible && callback) {\n this.readTimer = setTimeout(callback, READ_DELAY);\n } else {\n this.readTimer = null;\n }\n }\n\n handleReadTimer(callback) {\n this.handleAppVisibility(!document.hidden, callback);\n }\n\n handleVisibilityEvent() {\n this.handleAppVisibility(!document.hidden, this.readTimerCallback);\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 });\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});\n this.handleError(err.message, 'err');\n });\n }\n }\n\n // Connection succeeded.\n handleConnected() {\n var params = this.tinode.getServerInfo();\n this.setState({\n serverVersion: params.ver + ' ' + (params.build ? params.build : 'none') + '; '\n });\n this.doLogin(this.state.login, this.state.password, {meth: this.state.credMethod, resp: this.state.credCode});\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 {formatHTMLMessage} = 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 formatHTMLMessage(messages.reconnect_countdown, {seconds: timeLeft}),\n 'warn',\n () => {\n clearInterval(this.reconnectCountdown);\n this.tinode.reconnect();\n },\n formatHTMLMessage(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 let 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(\"Code does not 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 });\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 var 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('', null);\n\n // Refresh authentication token.\n if (LocalStorageUtil.getObject('keep-logged-in')) {\n LocalStorageUtil.setObject('auth-token', this.tinode.getAuthToken());\n }\n // Logged in fine, subscribe to 'me' attaching callbacks from the contacts view.\n var me = this.tinode.getMeTopic();\n me.onMetaDesc = this.tnMeMetaDesc;\n me.onContactUpdate = this.tnMeContactUpdate;\n me.onSubsUpdated = this.tnMeSubsUpdated;\n this.setState({\n connected: true,\n credMethod: undefined,\n credCode: undefined,\n myUserId: this.tinode.getCurrentUserID()\n });\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 HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, 'contacts'));\n }\n\n tnMeMetaDesc(desc) {\n if (desc && desc.public) {\n this.setState({\n sidePanelTitle: desc.public.fn,\n sidePanelAvatar: makeImageUrl(desc.public.photo)\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 kown contacts for GroupManager to use.\n static prepareSearchableContacts(chatList, foundContacts) {\n let 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.topicType(c.topic) == 'p2p') {\n merged[c.topic] = {\n user: c.topic,\n updated: c.updated,\n public: c.public,\n private: c.private,\n acs: c.acs\n };\n }\n }\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 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) >= 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, unused_index, online, acs) {\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: online,\n topicSelectedAcs: acs\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 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.getType() == 'p2p') {\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();\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 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 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 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 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 HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash,\n this.state.myUserId ? 'edit' : 'settings'));\n }\n\n // User updated global parameters.\n handleGlobalSettings(settings) {\n let serverAddress = settings.serverAddress || this.state.serverAddress;\n let transport = settings.transport || this.state.transport;\n if (this.tinode) {\n this.tinode.onDisconnect = undefined;\n this.tinode.disconnect();\n }\n this.tinode = TinodeWeb.tnSetup(serverAddress, transport);\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\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 // Initialize desktop alerts = push notifications.\n initDesktopAlerts() {\n // Google could not be bothered to mention that\n // onTokenRefresh is never called.\n this.fbPush.onTokenRefresh(() => {\n this.requestPushToken();\n });\n\n this.fbPush.onMessage((payload) => {\n // No need to do anything about it.\n // All the magic happends in the service worker.\n });\n }\n\n togglePushToken(enabled) {\n if (enabled) {\n if (!this.state.firebaseToken) {\n this.fbPush.requestPermission().then(() => {\n this.requestPushToken();\n }).catch((err) => {\n this.handleError(err.message, 'err');\n this.setState({desktopAlerts: false, firebaseToken: null});\n LocalStorageUtil.updateObject('settings', {desktopAlerts: false});\n console.log(\"Failed to get permission to notify.\", err);\n });\n } else {\n this.setState({desktopAlerts: true});\n LocalStorageUtil.updateObject('settings', {desktopAlerts: true});\n }\n } else if (this.state.firebaseToken) {\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 });\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 if (refreshedToken != this.state.firebaseToken) {\n this.tinode.setDeviceToken(refreshedToken, true);\n LocalStorageUtil.setObject('firebase-token', refreshedToken);\n }\n this.setState({firebaseToken: refreshedToken, desktopAlerts: true});\n LocalStorageUtil.updateObject('settings', {desktopAlerts: true});\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 parsed.path[0] = this.state.myUserId ? 'contacts' : '';\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 // User clicked a (+) menu item.\n handleNewTopic() {\n HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, 'newtpk'));\n }\n\n // Request to start a new topic. New P2P topic requires peer's name.\n handleNewTopicRequest(peerName, pub, priv, tags) {\n\n const topicName = peerName || this.tinode.newGroupTopicName();\n const params = {\n _topicName: topicName,\n };\n if (peerName) {\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 params.desc = {public: pub, private: {comment: priv}};\n params.tags = tags;\n }\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 if (this.tinode) {\n this.tinode.onDisconnect = undefined;\n this.tinode.disconnect();\n }\n this.setState(this.getBlankState());\n this.tinode = TinodeWeb.tnSetup(this.state.serverAddress, this.state.transport);\n this.tinode.onConnect = this.handleConnected;\n this.tinode.onDisconnect = this.handleDisconnect;\n HashNavigation.navigateTo('');\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 const {formatMessage} = this.props.intl;\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 ? {title: formatMessage({id: 'menu_item_info'}), handler: this.handleShowInfoView} : 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_archive' : null,\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, confirm, onReject, reject) {\n this.setState({\n alertVisible: true,\n alertParams: {\n title: title,\n content: content,\n onConfirm: onConfirm,\n confirm: confirm,\n onReject: onReject,\n reject: reject\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(\"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 document.getElementById('mountPoint')\n);\n"],"sourceRoot":""}
\ No newline at end of file