UNPKG

frc-ui

Version:

React Web UI

420 lines (419 loc) 18.1 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import React from 'react'; import classNames from 'classnames'; import { v1 } from 'uuid'; import { Consumer } from '../common/Hoc'; import Inset from '../common/ChatInsetContent'; import { FsType, LocalStorageKey, MessageType, PermissionEnum, TrendType } from '../interfaces'; import Button from './Button'; import { getLocalStorageItem } from '../common'; let Send = class Send extends React.PureComponent { constructor(props) { super(props); this.urlReg = /^((https|http|ftp|rtsp|mms)?:\/\/)[^\s]+/; this.textMaxFreq = 10; this.textTimeWindow = 10; this.textSendRecord = []; this.saveRef = (name) => (ref) => { this[name] = ref; }; this.getMessage = () => { if (!this.chatMessage) return []; const childNodes = this.chatMessage.childNodes; const length = childNodes.length; let message = []; for (let i = 0; i < length; i++) { const node = childNodes[i]; if (node.nodeName === '#text') { message = this.analysisText(node['textContent'], message); } else { message.push({ type: node['eleType'], content: node['content'] }); } } this.setState({ textLength: 0 }); return message; }; this.analysisText = (text, message = []) => { if (!text) return message; const arr = (text || '').split(' '); arr && arr.map((t) => { message.push({ type: this.urlReg.test(t) ? MessageType.LINK : MessageType.TEXT, content: t }); message.push({ type: MessageType.TEXT, content: ' ' }); }); return message; }; this.initColorIndex = (permission) => { const localStorage = window.localStorage; let danmakuColor = '#ffffff'; if (permission && localStorage) { const colorValue = localStorage.getItem('danmakuColor'); if (!colorValue && colorValue !== '') { return colorValue; } } return danmakuColor; }; this.send = (message = []) => { if (!message || message.length === 0) return; const { Socket, user, permission, channel, isAnonymous, fontSize, anonymousName, locale, trendType, isGag, setName } = this.props; const { often } = this.state; const has = (permission || []).some((p) => p === PermissionEnum.CHAT_ROOM); if (isGag || often || !has) { return; } const per = permission || []; const fontSizePermission = per.some((p) => p === PermissionEnum.DANMAKU_FONT); const colorPermission = per.some((p) => p === PermissionEnum.DANMAKU_COLOR); const chatMessage = { id: v1(), username: user && user.username, isAnonymous, channel, userId: user && user.userId, loginName: user && user.username, showName: user && user.companyName + '-' + user.name, message, style: { color: this.initColorIndex(colorPermission), fontSize: (fontSizePermission ? fontSize : 20) + 'px' } }; if (anonymousName && isAnonymous) { if (typeof setName === 'function') { chatMessage.showName = setName(Object.assign(Object.assign({}, user), { trendType, isAnonymous, anonymousName })); } else { chatMessage.showName = ((locale && locale.trendType[trendType || TrendType.NONE]) || '') + anonymousName; } } if (Socket) Socket.send('chat', chatMessage); }; this.moveCursor = () => { if (!this.chatMessage) return; this.chatMessage.focus(); if (window.getSelection && this.chatMessage.lastChild) { let sel = window.getSelection(); let tempRange = document.createRange(); const n = this.chatMessage.lastChild || 0; tempRange.setStart(n, 0); if (sel) { sel.removeAllRanges(); sel.addRange(tempRange); } } }; this.insetContent = (type, content) => { const { isGag, permission } = this.props; const { often } = this.state; const has = (permission || []).some((p) => p === PermissionEnum.CHAT_ROOM); if (isGag || often || !has) { return; } if (!this.chatMessage) return; this.currentType = type; Inset({ type, content }, this.chatMessage); this.moveCursor(); }; this.handlePaste = (event) => { event.preventDefault(); const { emit } = this.props; const textMaxLength = this.props.textMaxLength || 0; const { textLength } = this.state; let cbd = event.clipboardData; for (let i = 0; i < cbd.items.length; i++) { let item = cbd.items[i]; if (item.kind === 'file' || item.type.indexOf('image') > -1) { let blob = item.getAsFile(); if (!blob || blob.size === 0) { return; } const formData = new FormData(); formData.append('file', blob, ''); formData.append('ttl', '1d'); emit && emit('chatFile', { type: FsType.PASTE_IMAGE, file: formData }); // blobToDataURL(blob, (url) => this.insetContent(Type.IMAGE, url)); } else if (item.kind === 'string' && item.type === 'text/plain') { item.getAsString((str) => { let regStr = str; regStr = regStr.replace(/\s+/g, ''); regStr = regStr.replace(/<\/?.+?>/g, ''); regStr = regStr.replace(/[\r\n]/g, ''); if (textLength < textMaxLength) { let length = regStr.length; if (textLength + length > textMaxLength) { const remindLength = textMaxLength - textLength; let count = 0; for (let ti = 0; ti < str.length; ti++) { if (!/\s+/g.test(str[ti]) && !/<\/?.+?>/g.test(str[ti]) && !/[\r\n]/g.test(str[ti])) { count += 1; } if (remindLength <= count) { str = str.substring(0, ti); break; } } length = count; } this.setState({ textLength: this.state.textLength + length }); str = str.replace(/[\n]/g, ' <br/> '); const arr = (str || '').split(' '); (arr || []).map((t) => { if (t === '<br/>') { this.insetContent(MessageType.ELEMENT, 'br'); } else { this.insetContent(this.urlReg.test(str) ? MessageType.LINK : MessageType.TEXT, t); } }); } }); } else if (item.kind === 'string' && item.type === 'text/html') { item.getAsString((str) => { console.log(str); }); } } }; this.handleKeyDown = (event) => { const shortcutKey = getLocalStorageItem(LocalStorageKey.shortcutKey, 'EnterSend'); if ((event.ctrlKey || event.metaKey) && event.keyCode === 13) { if (shortcutKey === 'EnterSend') { this.handleAddBlank(event); } else if (shortcutKey === 'CtrlEnterSend') { this.handleSend(); } } else if (event.keyCode === 13) { if (shortcutKey === 'EnterSend') { this.handleSend(); } else if (shortcutKey === 'CtrlEnterSend') { this.handleAddBlank(event); } } else { this.currentType = MessageType.TEXT; if (this.state.textLength >= (this.props.textMaxLength || 0) && event.keyCode !== 8 && event.keyCode !== 37 && event.keyCode !== 38 && event.keyCode !== 39 && event.keyCode !== 40) { event.preventDefault(); } } }; this.handleKeyUp = (event) => { if (!this.chatMessage) return; const { textMaxLength } = this.props; const childNodes = this.chatMessage.childNodes; let length = 0; if (!childNodes) return; for (let i = 0; i < childNodes.length; i++) { const node = childNodes[i]; if (!node) continue; if (node.nodeName === '#text') { let tempLength = length + (node.textContent ? node.textContent.length : 0); if (textMaxLength && tempLength > textMaxLength) { node.textContent = node.textContent ? node.textContent.substring(0, textMaxLength - length) : node.textContent; length = textMaxLength; } else { length = tempLength; } } else if (node['eleType'] === MessageType.TEXT) { let tempLength = length + (node.textContent ? node.textContent.length : 0); if (textMaxLength && tempLength > textMaxLength) { node.textContent = node.textContent ? node.textContent.substring(0, textMaxLength - length) : node.textContent; node['content'] = node.textContent; length = textMaxLength; } else { length = tempLength; } } } if (textMaxLength && length > textMaxLength) { event.preventDefault(); } this.setState({ textLength: length }); }; this.handleAddBlank = (event) => { event && event.preventDefault(); if (this.currentType === MessageType.TEXT || !this.currentType) { this.insetContent(MessageType.ELEMENT, 'br'); } this.insetContent(MessageType.ELEMENT, 'br'); }; this.handleSend = (event) => { if (event) event.preventDefault(); const message = this.getMessage(); if (this.checkOften()) { this.chatMessage && (this.chatMessage.innerHTML = ''); return; } setTimeout(() => { this.chatMessage && (this.chatMessage.innerHTML = ''); }, 16); this.send(message); }; this.handleOnClick = () => { this.closeHistory(); if (this.state.often) { this.setState({ often: false, textLength: 0 }); } }; this.handleInput = (event) => { this.handleKeyUp(event); }; this.handleFocus = () => { this.closeHistory(); }; this.closeHistory = () => { const { emit, success, history } = this.props; if (emit && history) { emit('chat', { history: false }); success && success('chatMessage.histories', { payload: 'clear' }); } }; this.renderCover = (has) => { const { locale, isGag, prefix } = this.props; const { often } = this.state; const cls = `${prefix}-send-cover`; if (!has) return React.createElement("div", { className: cls }, locale && locale.noChatPermissionText); if (isGag) return React.createElement("div", { className: cls }, locale && locale.gagText); if (often) return React.createElement("div", { className: cls }, locale && locale.oftenText); return null; }; this.checkOften = (timestamp = new Date().getTime()) => { const { often } = this.state; if (this.textSendRecord.length < this.textMaxFreq) { this.textSendRecord.push(timestamp); often && this.setState({ often: false }); return false; } if (this.textSendRecord.length > 0) { const first = this.textSendRecord[0]; if ((timestamp - first) / 1000 > this.textTimeWindow) { this.textSendRecord.push(timestamp); this.textSendRecord.shift(); often && this.setState({ often: false }); return false; } } !often && this.setState({ often: true }); return true; }; this.renderInput = (has) => { const { prefix } = this.props; const props = { ref: this.saveRef('chatMessage'), className: `${prefix}-send-input`, // contentEditable: has && !isGag && !often, contentEditable: false, onKeyDown: this.handleKeyDown, onKeyUp: this.handleKeyUp, onPaste: this.handlePaste, onClick: this.handleOnClick, onInput: this.handleInput, onFocus: this.handleFocus }; return React.createElement("div", Object.assign({}, props, { style: { cursor: 'not-allowed' } }), this.renderCover(has)); }; this.renderTip = (has) => { const { textMaxLength } = this.props; const { textLength, often } = this.state; const { isGag, prefix, locale } = this.props; if (has && !isGag && !often) { return (React.createElement("div", { className: `${prefix}-send-text-length` }, locale && locale.inputTip(Math.max((textMaxLength || 0) - textLength, 0)))); } return null; }; this.state = { textLength: 0, often: false, textSendRecord: [] }; } componentWillReceiveProps(nextProps) { const { emo, textMaxLength, uploadImage } = nextProps; if (emo && emo !== this.props.emo) { if (emo.type === 'emo') { if (textMaxLength && textMaxLength > this.state.textLength) { this.insetContent(MessageType.EMO, emo.emo); this.setState({ textLength: this.state.textLength + 1 }); } } else if (emo.type === 'collectEmo') { let message = [{ type: MessageType.GIF, content: emo.emo }]; this.send(message); } } if (uploadImage && uploadImage !== this.props.uploadImage && uploadImage.url) { this.insetContent(MessageType.IMAGE, uploadImage.url); } } componentDidUpdate(preProps, preState) { if (preState.often !== this.state.often) this.moveCursor(); } render() { const { prefix, className, permission, isGag } = this.props; const { often } = this.state; const has = (permission || []).some((p) => p === PermissionEnum.CHAT_ROOM); const cls = classNames(`${prefix}-send`, className); return (React.createElement("div", { className: cls }, this.renderInput(has), React.createElement("div", { className: `${prefix}-send-bottom` }, this.renderTip(has), " ", has && !isGag && !often && React.createElement(Button, { onClick: this.handleSend })))); } }; Send = __decorate([ Consumer([ 'textMaxLength', 'isGag', 'isAnonymous', 'permission', 'fontSize', 'trendType', 'emo', 'uploadImage', 'history', 'anonymousName' ]) ], Send); export default Send;