Compare commits
27
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c94b397222 | ||
|
|
c013885bec | ||
|
|
4a2667015f | ||
|
|
e209ef2483 | ||
|
|
2a00791a1b | ||
|
|
dcf9e1d47e | ||
|
|
6c0612dc8f | ||
|
|
315631f67c | ||
|
|
c31c0c2c07 | ||
|
|
c7c2cffbbf | ||
|
|
e77e8441cf | ||
|
|
d95c53ed24 | ||
|
|
300b4f8d64 | ||
|
|
36977b429b | ||
|
|
0d7643ee42 | ||
|
|
145ac78de8 | ||
|
|
245603e8b9 | ||
|
|
3eab7ec09b | ||
|
|
a50261ab42 | ||
|
|
8cd8ffb659 | ||
|
|
932ab0a047 | ||
|
|
b5686f45b3 | ||
|
|
88c4cb42cd | ||
|
|
cd6a87037d | ||
|
|
deaea078ea | ||
|
|
bdfe163ecf | ||
|
|
1fb3a233a2 |
@@ -32,7 +32,7 @@ If applicable, add screenshots to help explain your problem.
|
||||
- TinodeWeb Version [e.g. 0.16.6]
|
||||
|
||||
**Console log**
|
||||
Please attach or insert console log which illustrates the problem.
|
||||
Please attach or insert console log which illustrates the problem. Please use the log produced by `index-dev.html`, not `index.html`; `index.html` uses minified javascript which produces unusable logs.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
@@ -68,5 +68,5 @@ More translations are welcome. See [instructions](https://github.com/tinode/chat
|
||||
### Mobile screenshots
|
||||
|
||||
<p align="center">
|
||||
<kbd><img src="web-mob-contacts.png" alt="Mobile web: contacts" width=250 /></kbd> <kbd><img src="web-mob-chat.png" alt="Mobile web: chat" width=250 /></kbd> <kbd><img src="web-mob-info.png" alt="Mobile web: topic info" width=250 /></kbd> <kbd><img src="web-mob-forward.png" alt="Mobile web: forward message" width=250 /></kbd> <kbd><img src="web-mob-acc-settings.png" alt="Mobile web: account settings" width=250 /></kbd> <kbd><img src="web-mob-avatar.png" alt="Mobile web: change avatar" width=250 /></kbd>
|
||||
<kbd><img src="web-mob-contacts.png" alt="Mobile web: contacts" width=250 /></kbd> <kbd><img src="web-mob-chat.png" alt="Mobile web: chat" width=250 /></kbd> <kbd><img src="web-mob-info.png" alt="Mobile web: topic info" width=250 /></kbd> <kbd><img src="web-mob-forward.png" alt="Mobile web: forward message" width=250 /></kbd> <kbd><img src="web-mob-video-call.png" alt="Mobile web: video call" width=250 /></kbd> <kbd><img src="web-mob-avatar.png" alt="Mobile web: change avatar" width=250 /></kbd>
|
||||
</p>
|
||||
|
||||
Binary file not shown.
+5
-3
@@ -838,6 +838,10 @@ table.permission-editor td.checkbox {
|
||||
padding-left: 0.5rem;
|
||||
}
|
||||
|
||||
input#host-name {
|
||||
width: 15rem;
|
||||
}
|
||||
|
||||
/* Input with a search icon on the left */
|
||||
input.search {
|
||||
text-indent: 1.4rem;
|
||||
@@ -1667,7 +1671,7 @@ video {
|
||||
|
||||
.caller-card.pulse img.avatar,
|
||||
.caller-card.pulse .lettertile {
|
||||
animation: active-call 1.5s linear infinite;;
|
||||
animation: active-call 1.5s linear infinite;
|
||||
}
|
||||
|
||||
.caller-card .avatar-box .material-icons {
|
||||
@@ -1753,14 +1757,12 @@ video {
|
||||
@keyframes active-call {
|
||||
0% {
|
||||
box-shadow:
|
||||
0 0 0 0 rgba(33, 150, 243, 0.3),
|
||||
0 0 0 0.1rem rgba(33, 150, 243, 0.3),
|
||||
0 0 0 0.3rem rgba(33, 150, 243, 0.3),
|
||||
0 0 0 0.5rem rgba(33, 150, 243, 0.3);
|
||||
}
|
||||
100% {
|
||||
box-shadow:
|
||||
0 0 0 0 rgba(33, 150, 243, 0.3),
|
||||
0 0 0 0.4rem rgba(33, 150, 243, 0.3),
|
||||
0 0 0 2rem rgba(33, 150, 243, 0),
|
||||
0 0 0 3rem rgba(33, 150, 243, 0);
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -16,6 +16,7 @@
|
||||
<meta name="og:url" content="https://web.tinode.co/">
|
||||
<meta name="og:title" content="Tinode Web Development">
|
||||
<meta name="og:image" content="img/og-logo.jpeg">
|
||||
<meta name="og:description" content="Tinode instant messenger, development version" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
|
||||
+2
-1
@@ -5,7 +5,7 @@
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="application-name" content="Tinode Web" />
|
||||
<meta name="description" content="Tinode instant messaging in a browser" />
|
||||
<meta name="description" content="Tinode instant messenger in a browser" />
|
||||
<meta name="application-url" content="https://web.tinode.co/">
|
||||
<meta name="theme-color" content="#3949AB" />
|
||||
<meta name="google" value="notranslate" />
|
||||
@@ -13,6 +13,7 @@
|
||||
<meta name="og:url" content="https://web.tinode.co/">
|
||||
<meta name="og:title" content="Tinode Web">
|
||||
<meta name="og:image" content="img/og-logo.jpeg">
|
||||
<meta name="og:description" content="Tinode instant messenger in a browser" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
|
||||
Generated
+2219
-2055
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "tinode-webapp",
|
||||
"description": "Tinode messenger for the web",
|
||||
"version": "0.20.0-beta1",
|
||||
"version": "0.20.0-beta2",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tinode/example-react-js.git"
|
||||
@@ -71,7 +71,7 @@
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-intl": "^5.20.2",
|
||||
"tinode-sdk": "^0.20.0-beta1",
|
||||
"tinode-sdk": "^0.20.0-beta2",
|
||||
"webrtc-adapter": "^8.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -84,7 +84,7 @@
|
||||
"browserslist": "^4.16.5",
|
||||
"copy-webpack-plugin": "^10.0.0",
|
||||
"cssnano": "^5.0.6",
|
||||
"glob": "^7.1.7",
|
||||
"glob": "^8.0.3",
|
||||
"mkdirp": "^1.0.4",
|
||||
"postcss": "^8.3.2",
|
||||
"postcss-cli": "^9.0.2",
|
||||
|
||||
+1
-3
@@ -17,10 +17,8 @@ export const LOGGING_ENABLED = true;
|
||||
|
||||
// Minimum time between two keypress notifications, milliseconds.
|
||||
export const KEYPRESS_DELAY = 3_000;
|
||||
// Delay before sending a {note} for reciving a message, milliseconds.
|
||||
export const RECEIVED_DELAY = 500;
|
||||
// Delay before sending a read notification, milliseconds.
|
||||
export const READ_DELAY = 1000;
|
||||
export const READ_DELAY = 1_500;
|
||||
|
||||
// The default shortest allowed tag length. Matches the value on the server.
|
||||
export const MIN_TAG_LENGTH = 2;
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
// This is a generated file. Don't edit.
|
||||
|
||||
export const PACKAGE_VERSION = "0.20.0-beta1";
|
||||
export const PACKAGE_VERSION = "0.20.0-beta2";
|
||||
|
||||
+14
-11
@@ -134,6 +134,8 @@ class MessagesView extends React.Component {
|
||||
|
||||
this.readNotificationQueue = [];
|
||||
this.readNotificationTimer = null;
|
||||
|
||||
this.keyPressTimer = null;
|
||||
}
|
||||
|
||||
getOrCreateMessageRef(seqId) {
|
||||
@@ -515,7 +517,7 @@ class MessagesView extends React.Component {
|
||||
|
||||
// Set up the timer if it's not running already.
|
||||
if (!this.readNotificationTimer) {
|
||||
this.readNotificationTimer = setInterval(() => {
|
||||
this.readNotificationTimer = setInterval(_ => {
|
||||
if (this.readNotificationQueue.length == 0) {
|
||||
// Shut down the timer if the queue is empty.
|
||||
clearInterval(this.readNotificationTimer);
|
||||
@@ -604,17 +606,18 @@ class MessagesView extends React.Component {
|
||||
}
|
||||
|
||||
clearTimeout(this.keyPressTimer)
|
||||
this.setState({messageCount: topic.messageCount(), typingIndicator: false});
|
||||
|
||||
// Scroll to the bottom if the message is added to the end of the message
|
||||
// list if already at the bottom, otherwise show [go to latest] button.
|
||||
if (topic.isNewMessage(msg.seq)) {
|
||||
if (this.state.scrollPosition > SHOW_GO_TO_LAST_DIST) {
|
||||
this.setState({showGoToLastButton: true});
|
||||
} else {
|
||||
this.setState({scrollPosition: 0});
|
||||
this.setState({messageCount: topic.messageCount(), typingIndicator: false}, _ => {
|
||||
// Scroll to the bottom if the message is added to the end of the message
|
||||
// list if already at the bottom, otherwise show [go to latest] button.
|
||||
// Implemented as a callback to be sure the scroll height has been updated.
|
||||
if (topic.isNewMessage(msg.seq)) {
|
||||
if (this.state.scrollPosition > SHOW_GO_TO_LAST_DIST) {
|
||||
this.setState({showGoToLastButton: true});
|
||||
} else {
|
||||
this.goToLatestMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Aknowledge messages except own messages. They are
|
||||
// automatically assumed to be read and recived.
|
||||
|
||||
+49
-29
@@ -18,7 +18,7 @@ import MessagesView from './messages-view.jsx';
|
||||
import SidepanelView from './sidepanel-view.jsx';
|
||||
|
||||
import { API_KEY, APP_NAME, DEFAULT_P2P_ACCESS_MODE, FORWARDED_PREVIEW_LENGTH, LOGGING_ENABLED,
|
||||
MEDIA_BREAKPOINT, RECEIVED_DELAY } from '../config.js';
|
||||
MEDIA_BREAKPOINT } from '../config.js';
|
||||
import { CALL_STATE_NONE, CALL_STATE_OUTGOING_INITATED,
|
||||
CALL_STATE_INCOMING_RECEIVED, CALL_STATE_IN_PROGRESS,
|
||||
CALL_HEAD_STARTED } from '../constants.js';
|
||||
@@ -156,6 +156,7 @@ class TinodeWeb extends React.Component {
|
||||
|
||||
this.handleStartVideoCall = this.handleStartVideoCall.bind(this);
|
||||
this.handleInfoMessage = this.handleInfoMessage.bind(this);
|
||||
this.handleDataMessage = this.handleDataMessage.bind(this);
|
||||
this.handleCallClose = this.handleCallClose.bind(this);
|
||||
|
||||
this.handleCallInvite = this.handleCallInvite.bind(this);
|
||||
@@ -168,6 +169,9 @@ class TinodeWeb extends React.Component {
|
||||
this.handleCallAcceptCall = this.handleCallAcceptCall.bind(this);
|
||||
|
||||
this.sendMessageToTopic = this.sendMessageToTopic.bind(this);
|
||||
|
||||
// FIXME: this is unused.
|
||||
this.callTimeoutTimer = null;
|
||||
}
|
||||
|
||||
getBlankState() {
|
||||
@@ -284,6 +288,7 @@ class TinodeWeb extends React.Component {
|
||||
this.tinode.onDisconnect = this.handleDisconnect;
|
||||
this.tinode.onAutoreconnectIteration = this.handleAutoreconnectIteration;
|
||||
this.tinode.onInfoMessage = this.handleInfoMessage;
|
||||
this.tinode.onDataMessage = this.handleDataMessage;
|
||||
}).then(() => {
|
||||
// Initialize desktop alerts.
|
||||
if (this.state.desktopAlertsEnabled) {
|
||||
@@ -783,7 +788,9 @@ class TinodeWeb extends React.Component {
|
||||
if (cont.unread > 0 && this.state.messageSounds && !archived) {
|
||||
// Skip update if the topic is currently open, otherwise the badge will annoyingly flash.
|
||||
if (document.hidden || this.state.topicSelected != cont.topic) {
|
||||
POP_SOUND.play();
|
||||
POP_SOUND.play().catch(_ => {
|
||||
// play() throws if the user did not click the app first: https://goo.gl/xX8pDD.
|
||||
});
|
||||
}
|
||||
}
|
||||
// Reorder contact list to use possibly updated 'touched'.
|
||||
@@ -806,12 +813,12 @@ class TinodeWeb extends React.Component {
|
||||
}
|
||||
} else if (what == 'del') {
|
||||
// TODO: messages deleted (hard or soft) -- update pill counter.
|
||||
} else if (what == 'upd') {
|
||||
// upd - handled by the SDK. Explicitly ignoring here.
|
||||
} else if (what == 'upd' || what == 'call') {
|
||||
// upd, call - handled by the SDK. Explicitly ignoring here.
|
||||
} else {
|
||||
// TODO(gene): handle other types of notifications:
|
||||
// * ua -- user agent changes (maybe display a pictogram for mobile/desktop).
|
||||
console.info("Unsupported (yet) presence update:" + what + " in: " + cont.topic);
|
||||
console.info("Unsupported (yet) presence update:", what, "in", cont.topic);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -880,16 +887,7 @@ class TinodeWeb extends React.Component {
|
||||
this.setState(newState);
|
||||
}
|
||||
|
||||
// Sending "received" notifications
|
||||
tnData(data) {
|
||||
const topic = this.tinode.getTopic(data.topic);
|
||||
if (topic.msgStatus(data, true) >= Tinode.MESSAGE_STATUS_SENT && data.from != this.state.myUserId) {
|
||||
clearTimeout(this.receivedTimer);
|
||||
this.receivedTimer = setTimeout(() => {
|
||||
this.receivedTimer = undefined;
|
||||
topic.noteRecv(data.seq);
|
||||
}, RECEIVED_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
/* Fnd topic: find contacts by tokens */
|
||||
@@ -1331,7 +1329,9 @@ class TinodeWeb extends React.Component {
|
||||
handleUnarchive(topicName) {
|
||||
const topic = this.tinode.getTopic(topicName);
|
||||
if (topic) {
|
||||
topic.archive(false);
|
||||
topic.archive(false).catch((err) => {
|
||||
this.handleError(err.message, 'err');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1753,9 +1753,8 @@ class TinodeWeb extends React.Component {
|
||||
}
|
||||
|
||||
handleCallClose() {
|
||||
if (this.state.callTimeout) {
|
||||
console.log('closing call timeout ', this.state.callTimeout);
|
||||
clearTimeout(this.state.callTimeout);
|
||||
if (this.callTimeoutTimer) {
|
||||
clearTimeout(this.callTimeoutTimer);
|
||||
}
|
||||
this.setState({
|
||||
callTopic: undefined,
|
||||
@@ -1786,18 +1785,7 @@ class TinodeWeb extends React.Component {
|
||||
return;
|
||||
}
|
||||
switch (info.event) {
|
||||
case 'invite':
|
||||
if (info.from != this.state.myUserId) {
|
||||
// Incoming call.
|
||||
this.setState({
|
||||
callTopic: info.src,
|
||||
callState: CALL_STATE_INCOMING_RECEIVED,
|
||||
callSeq: info.seq
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'accept':
|
||||
console.log('TinodeWeb: received accept msg ', info);
|
||||
// If another my session has accepted the call.
|
||||
if (Tinode.isMeTopicName(info.topic) && this.tinode.isMe(info.from)) {
|
||||
this.setState({
|
||||
@@ -1819,6 +1807,38 @@ class TinodeWeb extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleDataMessage(data) {
|
||||
if (data.head && data.head.webrtc && data.head.webrtc == CALL_HEAD_STARTED) {
|
||||
// If it's a video call invite message.
|
||||
// See if we need to display incoming call UI.
|
||||
const topic = this.tinode.getTopic(data.topic);
|
||||
if (topic) {
|
||||
// Check if a later version of the message exists (which means the call
|
||||
// has been either accepted or finished already).
|
||||
const msg = topic.latestMsgVersion(data.seq) || data;
|
||||
if (msg.head && msg.head.webrtc && msg.head.webrtc == CALL_HEAD_STARTED) {
|
||||
// This is a legit new call.
|
||||
if (data.from != this.state.myUserId) {
|
||||
if (this.state.callState == CALL_STATE_NONE) {
|
||||
// Incoming call.
|
||||
this.setState({
|
||||
callTopic: data.topic,
|
||||
callState: CALL_STATE_INCOMING_RECEIVED,
|
||||
callSeq: data.seq
|
||||
});
|
||||
} else {
|
||||
// Another call is either in progress or being established.
|
||||
// Reject the incoming call.
|
||||
this.handleCallHangup(data.topic, data.seq);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.warn("Received vc data message from unknown topic", data.topic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div id="app-container" ref={this.selfRef}>
|
||||
|
||||
@@ -39,8 +39,9 @@ export default class CallIncoming extends React.Component {
|
||||
|
||||
this.resetDesc(topic, this.props);
|
||||
if (this.props.callState == CALL_STATE_INCOMING_RECEIVED) {
|
||||
RING_SOUND.play();
|
||||
this.ringTimer = setInterval(() => RING_SOUND.play(), 2000);
|
||||
// play() throws if the user did not click the app first: https://goo.gl/xX8pDD.
|
||||
RING_SOUND.play().catch(_ => {});
|
||||
this.ringTimer = setInterval(_ => {RING_SOUND.play().catch(_ => {})}, 2000);
|
||||
this.props.onRinging(this.props.topic, this.props.seq);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ const RING_SOUND = new Audio('audio/call-out.m4a');
|
||||
RING_SOUND.loop = true;
|
||||
const CALL_ENDED_SOUND = new Audio('audio/call-end.m4a');
|
||||
CALL_ENDED_SOUND.loop = true;
|
||||
const DIALING_SOUND = new Audio('audio/dialing.m4a');
|
||||
|
||||
const messages = defineMessages({
|
||||
already_in_call: {
|
||||
@@ -107,9 +108,6 @@ class CallPanel extends React.PureComponent {
|
||||
case 'accept':
|
||||
this.handleVideoCallAccepted(info);
|
||||
break;
|
||||
case 'offer':
|
||||
this.handleVideoOfferMsg(info);
|
||||
break;
|
||||
case 'answer':
|
||||
this.handleVideoAnswerMsg(info);
|
||||
break;
|
||||
@@ -119,6 +117,16 @@ class CallPanel extends React.PureComponent {
|
||||
case 'hang-up':
|
||||
this.handleRemoteHangup(info);
|
||||
break;
|
||||
case 'offer':
|
||||
this.handleVideoOfferMsg(info);
|
||||
break;
|
||||
case 'ringing':
|
||||
// play() throws if the user did not click the app first: https://goo.gl/xX8pDD.
|
||||
RING_SOUND.play().catch(_ => {});
|
||||
break;
|
||||
default:
|
||||
console.warn("Unknown call event", info.event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +149,7 @@ class CallPanel extends React.PureComponent {
|
||||
this.setState({localStream: stream, waitingForPeer: true});
|
||||
this.localRef.current.srcObject = stream;
|
||||
|
||||
RING_SOUND.play();
|
||||
DIALING_SOUND.play();
|
||||
|
||||
// Send call invitation.
|
||||
this.props.onInvite(this.props.topic, this.props.seq, this.props.callState);
|
||||
@@ -256,7 +264,7 @@ class CallPanel extends React.PureComponent {
|
||||
}
|
||||
|
||||
handleICEGatheringStateChangeEvent(event) {
|
||||
console.log('ICE gathering change state: ', event);
|
||||
// console.log('ICE gathering change state: ', event);
|
||||
}
|
||||
|
||||
handleTrackEvent(event) {
|
||||
@@ -278,7 +286,7 @@ class CallPanel extends React.PureComponent {
|
||||
break;
|
||||
default:
|
||||
this.reportError(e.message);
|
||||
console.error('Error opening your camera and/or microphone: ' + e.message);
|
||||
console.error("Error opening your camera and/or microphone:", e.message);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -329,7 +337,7 @@ class CallPanel extends React.PureComponent {
|
||||
RING_SOUND.pause();
|
||||
RING_SOUND.currentTime = 0;
|
||||
CALL_ENDED_SOUND.loop = true;
|
||||
CALL_ENDED_SOUND.play();
|
||||
CALL_ENDED_SOUND.play().catch(_ => {});
|
||||
setTimeout(_ => {
|
||||
this.handleCloseClick();
|
||||
}, 2000);
|
||||
|
||||
@@ -110,6 +110,7 @@ export default class InPlaceEdit extends React.Component {
|
||||
attr.type = this.props.type || 'text';
|
||||
}
|
||||
attr.value = this.state.value;
|
||||
attr.ref = this.selfRef;
|
||||
attr.onChange = this.handeTextChange;
|
||||
attr.onKeyDown = this.handleKeyDown;
|
||||
attr.onBlur = this.handleEditingFinished;
|
||||
@@ -118,7 +119,6 @@ export default class InPlaceEdit extends React.Component {
|
||||
attr.required = this.props.required ? 'required' : '';
|
||||
attr.autoComplete = this.props.autoComplete;
|
||||
attr.autoFocus = true;
|
||||
attr.ref = this.selfRef;
|
||||
|
||||
return React.createElement(element, attr, null);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class SendMessage extends React.PureComponent {
|
||||
quote: null,
|
||||
message: '',
|
||||
audioRec: false,
|
||||
audioAvailable: !!navigator.mediaDevices.getUserMedia,
|
||||
audioAvailable: !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia),
|
||||
// Make initial keypress time as if it happened 5001 milliseconds in the past.
|
||||
keypressTimestamp: new Date().getTime() - KEYPRESS_DELAY - 1
|
||||
};
|
||||
|
||||
@@ -5,6 +5,8 @@ export default class VisiblePassword extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.inputRef = React.createRef();
|
||||
|
||||
this.state = {
|
||||
value: this.props.value,
|
||||
visible: false
|
||||
@@ -16,6 +18,10 @@ export default class VisiblePassword extends React.PureComponent {
|
||||
this.handleEditingFinished = this.handleEditingFinished.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.inputRef.current.focus();
|
||||
}
|
||||
|
||||
handeTextChange(e) {
|
||||
this.setState({value: e.target.value});
|
||||
if (this.props.onChange) {
|
||||
@@ -58,8 +64,7 @@ export default class VisiblePassword extends React.PureComponent {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div tabIndex="-1" className="group-focus"
|
||||
onBlur={this.handleEditingFinished}>
|
||||
<div tabIndex="-1" className="group-focus" onBlur={this.handleEditingFinished}>
|
||||
<input className="with-visibility"
|
||||
type={this.state.visible ? "text" : "password"}
|
||||
value={this.state.value}
|
||||
@@ -68,7 +73,8 @@ export default class VisiblePassword extends React.PureComponent {
|
||||
autoFocus={this.props.autoFocus ? 'autoFocus' : ''}
|
||||
autoComplete={this.props.autoComplete}
|
||||
onChange={this.handeTextChange}
|
||||
onKeyDown={this.handleKeyDown} />
|
||||
onKeyDown={this.handleKeyDown}
|
||||
ref={this.inputRef} />
|
||||
<span onClick={this.handleVisibility}>
|
||||
<i className="material-icons clickable light-gray">
|
||||
{this.state.visible ? 'visibility' : 'visibility_off'}
|
||||
|
||||
+535
-345
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+231
-25
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,3 +1,3 @@
|
||||
// This is a generated file. Don't edit.
|
||||
|
||||
const PACKAGE_VERSION = "0.20.0-beta1";
|
||||
const PACKAGE_VERSION = "0.20.0-beta2";
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 101 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 813 KiB |
Reference in New Issue
Block a user