Compare commits
27
Commits
0c91e4c615
...
c94b397222
| 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]
|
- TinodeWeb Version [e.g. 0.16.6]
|
||||||
|
|
||||||
**Console log**
|
**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**
|
**Additional context**
|
||||||
Add any other context about the problem here.
|
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
|
### Mobile screenshots
|
||||||
|
|
||||||
<p align="center">
|
<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>
|
</p>
|
||||||
|
|||||||
Binary file not shown.
+5
-3
@@ -838,6 +838,10 @@ table.permission-editor td.checkbox {
|
|||||||
padding-left: 0.5rem;
|
padding-left: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input#host-name {
|
||||||
|
width: 15rem;
|
||||||
|
}
|
||||||
|
|
||||||
/* Input with a search icon on the left */
|
/* Input with a search icon on the left */
|
||||||
input.search {
|
input.search {
|
||||||
text-indent: 1.4rem;
|
text-indent: 1.4rem;
|
||||||
@@ -1667,7 +1671,7 @@ video {
|
|||||||
|
|
||||||
.caller-card.pulse img.avatar,
|
.caller-card.pulse img.avatar,
|
||||||
.caller-card.pulse .lettertile {
|
.caller-card.pulse .lettertile {
|
||||||
animation: active-call 1.5s linear infinite;;
|
animation: active-call 1.5s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.caller-card .avatar-box .material-icons {
|
.caller-card .avatar-box .material-icons {
|
||||||
@@ -1753,14 +1757,12 @@ video {
|
|||||||
@keyframes active-call {
|
@keyframes active-call {
|
||||||
0% {
|
0% {
|
||||||
box-shadow:
|
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.1rem rgba(33, 150, 243, 0.3),
|
||||||
0 0 0 0.3rem 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);
|
0 0 0 0.5rem rgba(33, 150, 243, 0.3);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
box-shadow:
|
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 0.4rem rgba(33, 150, 243, 0.3),
|
||||||
0 0 0 2rem rgba(33, 150, 243, 0),
|
0 0 0 2rem rgba(33, 150, 243, 0),
|
||||||
0 0 0 3rem 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:url" content="https://web.tinode.co/">
|
||||||
<meta name="og:title" content="Tinode Web Development">
|
<meta name="og:title" content="Tinode Web Development">
|
||||||
<meta name="og:image" content="img/og-logo.jpeg">
|
<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="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<link rel="manifest" href="manifest.json" />
|
<link rel="manifest" href="manifest.json" />
|
||||||
|
|||||||
+2
-1
@@ -5,7 +5,7 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="application-name" content="Tinode Web" />
|
<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="application-url" content="https://web.tinode.co/">
|
||||||
<meta name="theme-color" content="#3949AB" />
|
<meta name="theme-color" content="#3949AB" />
|
||||||
<meta name="google" value="notranslate" />
|
<meta name="google" value="notranslate" />
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
<meta name="og:url" content="https://web.tinode.co/">
|
<meta name="og:url" content="https://web.tinode.co/">
|
||||||
<meta name="og:title" content="Tinode Web">
|
<meta name="og:title" content="Tinode Web">
|
||||||
<meta name="og:image" content="img/og-logo.jpeg">
|
<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="apple-mobile-web-app-capable" content="yes">
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<link rel="manifest" href="manifest.json" />
|
<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",
|
"name": "tinode-webapp",
|
||||||
"description": "Tinode messenger for the web",
|
"description": "Tinode messenger for the web",
|
||||||
"version": "0.20.0-beta1",
|
"version": "0.20.0-beta2",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/tinode/example-react-js.git"
|
"url": "https://github.com/tinode/example-react-js.git"
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-intl": "^5.20.2",
|
"react-intl": "^5.20.2",
|
||||||
"tinode-sdk": "^0.20.0-beta1",
|
"tinode-sdk": "^0.20.0-beta2",
|
||||||
"webrtc-adapter": "^8.1.1"
|
"webrtc-adapter": "^8.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
"browserslist": "^4.16.5",
|
"browserslist": "^4.16.5",
|
||||||
"copy-webpack-plugin": "^10.0.0",
|
"copy-webpack-plugin": "^10.0.0",
|
||||||
"cssnano": "^5.0.6",
|
"cssnano": "^5.0.6",
|
||||||
"glob": "^7.1.7",
|
"glob": "^8.0.3",
|
||||||
"mkdirp": "^1.0.4",
|
"mkdirp": "^1.0.4",
|
||||||
"postcss": "^8.3.2",
|
"postcss": "^8.3.2",
|
||||||
"postcss-cli": "^9.0.2",
|
"postcss-cli": "^9.0.2",
|
||||||
|
|||||||
+1
-3
@@ -17,10 +17,8 @@ export const LOGGING_ENABLED = true;
|
|||||||
|
|
||||||
// Minimum time between two keypress notifications, milliseconds.
|
// Minimum time between two keypress notifications, milliseconds.
|
||||||
export const KEYPRESS_DELAY = 3_000;
|
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.
|
// 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.
|
// The default shortest allowed tag length. Matches the value on the server.
|
||||||
export const MIN_TAG_LENGTH = 2;
|
export const MIN_TAG_LENGTH = 2;
|
||||||
|
|||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
// This is a generated file. Don't edit.
|
// This is a generated file. Don't edit.
|
||||||
|
|
||||||
export const PACKAGE_VERSION = "0.20.0-beta1";
|
export const PACKAGE_VERSION = "0.20.0-beta2";
|
||||||
|
|||||||
@@ -134,6 +134,8 @@ class MessagesView extends React.Component {
|
|||||||
|
|
||||||
this.readNotificationQueue = [];
|
this.readNotificationQueue = [];
|
||||||
this.readNotificationTimer = null;
|
this.readNotificationTimer = null;
|
||||||
|
|
||||||
|
this.keyPressTimer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getOrCreateMessageRef(seqId) {
|
getOrCreateMessageRef(seqId) {
|
||||||
@@ -515,7 +517,7 @@ class MessagesView extends React.Component {
|
|||||||
|
|
||||||
// Set up the timer if it's not running already.
|
// Set up the timer if it's not running already.
|
||||||
if (!this.readNotificationTimer) {
|
if (!this.readNotificationTimer) {
|
||||||
this.readNotificationTimer = setInterval(() => {
|
this.readNotificationTimer = setInterval(_ => {
|
||||||
if (this.readNotificationQueue.length == 0) {
|
if (this.readNotificationQueue.length == 0) {
|
||||||
// Shut down the timer if the queue is empty.
|
// Shut down the timer if the queue is empty.
|
||||||
clearInterval(this.readNotificationTimer);
|
clearInterval(this.readNotificationTimer);
|
||||||
@@ -604,17 +606,18 @@ class MessagesView extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
clearTimeout(this.keyPressTimer)
|
clearTimeout(this.keyPressTimer)
|
||||||
this.setState({messageCount: topic.messageCount(), typingIndicator: false});
|
this.setState({messageCount: topic.messageCount(), typingIndicator: false}, _ => {
|
||||||
|
|
||||||
// Scroll to the bottom if the message is added to the end of the message
|
// 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.
|
// 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 (topic.isNewMessage(msg.seq)) {
|
||||||
if (this.state.scrollPosition > SHOW_GO_TO_LAST_DIST) {
|
if (this.state.scrollPosition > SHOW_GO_TO_LAST_DIST) {
|
||||||
this.setState({showGoToLastButton: true});
|
this.setState({showGoToLastButton: true});
|
||||||
} else {
|
} else {
|
||||||
this.setState({scrollPosition: 0});
|
this.goToLatestMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Aknowledge messages except own messages. They are
|
// Aknowledge messages except own messages. They are
|
||||||
// automatically assumed to be read and recived.
|
// 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 SidepanelView from './sidepanel-view.jsx';
|
||||||
|
|
||||||
import { API_KEY, APP_NAME, DEFAULT_P2P_ACCESS_MODE, FORWARDED_PREVIEW_LENGTH, LOGGING_ENABLED,
|
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,
|
import { CALL_STATE_NONE, CALL_STATE_OUTGOING_INITATED,
|
||||||
CALL_STATE_INCOMING_RECEIVED, CALL_STATE_IN_PROGRESS,
|
CALL_STATE_INCOMING_RECEIVED, CALL_STATE_IN_PROGRESS,
|
||||||
CALL_HEAD_STARTED } from '../constants.js';
|
CALL_HEAD_STARTED } from '../constants.js';
|
||||||
@@ -156,6 +156,7 @@ class TinodeWeb extends React.Component {
|
|||||||
|
|
||||||
this.handleStartVideoCall = this.handleStartVideoCall.bind(this);
|
this.handleStartVideoCall = this.handleStartVideoCall.bind(this);
|
||||||
this.handleInfoMessage = this.handleInfoMessage.bind(this);
|
this.handleInfoMessage = this.handleInfoMessage.bind(this);
|
||||||
|
this.handleDataMessage = this.handleDataMessage.bind(this);
|
||||||
this.handleCallClose = this.handleCallClose.bind(this);
|
this.handleCallClose = this.handleCallClose.bind(this);
|
||||||
|
|
||||||
this.handleCallInvite = this.handleCallInvite.bind(this);
|
this.handleCallInvite = this.handleCallInvite.bind(this);
|
||||||
@@ -168,6 +169,9 @@ class TinodeWeb extends React.Component {
|
|||||||
this.handleCallAcceptCall = this.handleCallAcceptCall.bind(this);
|
this.handleCallAcceptCall = this.handleCallAcceptCall.bind(this);
|
||||||
|
|
||||||
this.sendMessageToTopic = this.sendMessageToTopic.bind(this);
|
this.sendMessageToTopic = this.sendMessageToTopic.bind(this);
|
||||||
|
|
||||||
|
// FIXME: this is unused.
|
||||||
|
this.callTimeoutTimer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getBlankState() {
|
getBlankState() {
|
||||||
@@ -284,6 +288,7 @@ class TinodeWeb extends React.Component {
|
|||||||
this.tinode.onDisconnect = this.handleDisconnect;
|
this.tinode.onDisconnect = this.handleDisconnect;
|
||||||
this.tinode.onAutoreconnectIteration = this.handleAutoreconnectIteration;
|
this.tinode.onAutoreconnectIteration = this.handleAutoreconnectIteration;
|
||||||
this.tinode.onInfoMessage = this.handleInfoMessage;
|
this.tinode.onInfoMessage = this.handleInfoMessage;
|
||||||
|
this.tinode.onDataMessage = this.handleDataMessage;
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
// Initialize desktop alerts.
|
// Initialize desktop alerts.
|
||||||
if (this.state.desktopAlertsEnabled) {
|
if (this.state.desktopAlertsEnabled) {
|
||||||
@@ -783,7 +788,9 @@ class TinodeWeb extends React.Component {
|
|||||||
if (cont.unread > 0 && this.state.messageSounds && !archived) {
|
if (cont.unread > 0 && this.state.messageSounds && !archived) {
|
||||||
// Skip update if the topic is currently open, otherwise the badge will annoyingly flash.
|
// Skip update if the topic is currently open, otherwise the badge will annoyingly flash.
|
||||||
if (document.hidden || this.state.topicSelected != cont.topic) {
|
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'.
|
// Reorder contact list to use possibly updated 'touched'.
|
||||||
@@ -806,12 +813,12 @@ class TinodeWeb extends React.Component {
|
|||||||
}
|
}
|
||||||
} else if (what == 'del') {
|
} else if (what == 'del') {
|
||||||
// TODO: messages deleted (hard or soft) -- update pill counter.
|
// TODO: messages deleted (hard or soft) -- update pill counter.
|
||||||
} else if (what == 'upd') {
|
} else if (what == 'upd' || what == 'call') {
|
||||||
// upd - handled by the SDK. Explicitly ignoring here.
|
// upd, call - handled by the SDK. Explicitly ignoring here.
|
||||||
} else {
|
} else {
|
||||||
// TODO(gene): handle other types of notifications:
|
// TODO(gene): handle other types of notifications:
|
||||||
// * ua -- user agent changes (maybe display a pictogram for mobile/desktop).
|
// * 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);
|
this.setState(newState);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sending "received" notifications
|
|
||||||
tnData(data) {
|
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 */
|
/* Fnd topic: find contacts by tokens */
|
||||||
@@ -1331,7 +1329,9 @@ class TinodeWeb extends React.Component {
|
|||||||
handleUnarchive(topicName) {
|
handleUnarchive(topicName) {
|
||||||
const topic = this.tinode.getTopic(topicName);
|
const topic = this.tinode.getTopic(topicName);
|
||||||
if (topic) {
|
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() {
|
handleCallClose() {
|
||||||
if (this.state.callTimeout) {
|
if (this.callTimeoutTimer) {
|
||||||
console.log('closing call timeout ', this.state.callTimeout);
|
clearTimeout(this.callTimeoutTimer);
|
||||||
clearTimeout(this.state.callTimeout);
|
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
callTopic: undefined,
|
callTopic: undefined,
|
||||||
@@ -1786,18 +1785,7 @@ class TinodeWeb extends React.Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (info.event) {
|
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':
|
case 'accept':
|
||||||
console.log('TinodeWeb: received accept msg ', info);
|
|
||||||
// If another my session has accepted the call.
|
// If another my session has accepted the call.
|
||||||
if (Tinode.isMeTopicName(info.topic) && this.tinode.isMe(info.from)) {
|
if (Tinode.isMeTopicName(info.topic) && this.tinode.isMe(info.from)) {
|
||||||
this.setState({
|
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() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div id="app-container" ref={this.selfRef}>
|
<div id="app-container" ref={this.selfRef}>
|
||||||
|
|||||||
@@ -39,8 +39,9 @@ export default class CallIncoming extends React.Component {
|
|||||||
|
|
||||||
this.resetDesc(topic, this.props);
|
this.resetDesc(topic, this.props);
|
||||||
if (this.props.callState == CALL_STATE_INCOMING_RECEIVED) {
|
if (this.props.callState == CALL_STATE_INCOMING_RECEIVED) {
|
||||||
RING_SOUND.play();
|
// play() throws if the user did not click the app first: https://goo.gl/xX8pDD.
|
||||||
this.ringTimer = setInterval(() => RING_SOUND.play(), 2000);
|
RING_SOUND.play().catch(_ => {});
|
||||||
|
this.ringTimer = setInterval(_ => {RING_SOUND.play().catch(_ => {})}, 2000);
|
||||||
this.props.onRinging(this.props.topic, this.props.seq);
|
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;
|
RING_SOUND.loop = true;
|
||||||
const CALL_ENDED_SOUND = new Audio('audio/call-end.m4a');
|
const CALL_ENDED_SOUND = new Audio('audio/call-end.m4a');
|
||||||
CALL_ENDED_SOUND.loop = true;
|
CALL_ENDED_SOUND.loop = true;
|
||||||
|
const DIALING_SOUND = new Audio('audio/dialing.m4a');
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
already_in_call: {
|
already_in_call: {
|
||||||
@@ -107,9 +108,6 @@ class CallPanel extends React.PureComponent {
|
|||||||
case 'accept':
|
case 'accept':
|
||||||
this.handleVideoCallAccepted(info);
|
this.handleVideoCallAccepted(info);
|
||||||
break;
|
break;
|
||||||
case 'offer':
|
|
||||||
this.handleVideoOfferMsg(info);
|
|
||||||
break;
|
|
||||||
case 'answer':
|
case 'answer':
|
||||||
this.handleVideoAnswerMsg(info);
|
this.handleVideoAnswerMsg(info);
|
||||||
break;
|
break;
|
||||||
@@ -119,6 +117,16 @@ class CallPanel extends React.PureComponent {
|
|||||||
case 'hang-up':
|
case 'hang-up':
|
||||||
this.handleRemoteHangup(info);
|
this.handleRemoteHangup(info);
|
||||||
break;
|
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.setState({localStream: stream, waitingForPeer: true});
|
||||||
this.localRef.current.srcObject = stream;
|
this.localRef.current.srcObject = stream;
|
||||||
|
|
||||||
RING_SOUND.play();
|
DIALING_SOUND.play();
|
||||||
|
|
||||||
// Send call invitation.
|
// Send call invitation.
|
||||||
this.props.onInvite(this.props.topic, this.props.seq, this.props.callState);
|
this.props.onInvite(this.props.topic, this.props.seq, this.props.callState);
|
||||||
@@ -256,7 +264,7 @@ class CallPanel extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleICEGatheringStateChangeEvent(event) {
|
handleICEGatheringStateChangeEvent(event) {
|
||||||
console.log('ICE gathering change state: ', event);
|
// console.log('ICE gathering change state: ', event);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTrackEvent(event) {
|
handleTrackEvent(event) {
|
||||||
@@ -278,7 +286,7 @@ class CallPanel extends React.PureComponent {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this.reportError(e.message);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,7 +337,7 @@ class CallPanel extends React.PureComponent {
|
|||||||
RING_SOUND.pause();
|
RING_SOUND.pause();
|
||||||
RING_SOUND.currentTime = 0;
|
RING_SOUND.currentTime = 0;
|
||||||
CALL_ENDED_SOUND.loop = true;
|
CALL_ENDED_SOUND.loop = true;
|
||||||
CALL_ENDED_SOUND.play();
|
CALL_ENDED_SOUND.play().catch(_ => {});
|
||||||
setTimeout(_ => {
|
setTimeout(_ => {
|
||||||
this.handleCloseClick();
|
this.handleCloseClick();
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ export default class InPlaceEdit extends React.Component {
|
|||||||
attr.type = this.props.type || 'text';
|
attr.type = this.props.type || 'text';
|
||||||
}
|
}
|
||||||
attr.value = this.state.value;
|
attr.value = this.state.value;
|
||||||
|
attr.ref = this.selfRef;
|
||||||
attr.onChange = this.handeTextChange;
|
attr.onChange = this.handeTextChange;
|
||||||
attr.onKeyDown = this.handleKeyDown;
|
attr.onKeyDown = this.handleKeyDown;
|
||||||
attr.onBlur = this.handleEditingFinished;
|
attr.onBlur = this.handleEditingFinished;
|
||||||
@@ -118,7 +119,6 @@ export default class InPlaceEdit extends React.Component {
|
|||||||
attr.required = this.props.required ? 'required' : '';
|
attr.required = this.props.required ? 'required' : '';
|
||||||
attr.autoComplete = this.props.autoComplete;
|
attr.autoComplete = this.props.autoComplete;
|
||||||
attr.autoFocus = true;
|
attr.autoFocus = true;
|
||||||
attr.ref = this.selfRef;
|
|
||||||
|
|
||||||
return React.createElement(element, attr, null);
|
return React.createElement(element, attr, null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class SendMessage extends React.PureComponent {
|
|||||||
quote: null,
|
quote: null,
|
||||||
message: '',
|
message: '',
|
||||||
audioRec: false,
|
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.
|
// Make initial keypress time as if it happened 5001 milliseconds in the past.
|
||||||
keypressTimestamp: new Date().getTime() - KEYPRESS_DELAY - 1
|
keypressTimestamp: new Date().getTime() - KEYPRESS_DELAY - 1
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ export default class VisiblePassword extends React.PureComponent {
|
|||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
this.inputRef = React.createRef();
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
value: this.props.value,
|
value: this.props.value,
|
||||||
visible: false
|
visible: false
|
||||||
@@ -16,6 +18,10 @@ export default class VisiblePassword extends React.PureComponent {
|
|||||||
this.handleEditingFinished = this.handleEditingFinished.bind(this);
|
this.handleEditingFinished = this.handleEditingFinished.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.inputRef.current.focus();
|
||||||
|
}
|
||||||
|
|
||||||
handeTextChange(e) {
|
handeTextChange(e) {
|
||||||
this.setState({value: e.target.value});
|
this.setState({value: e.target.value});
|
||||||
if (this.props.onChange) {
|
if (this.props.onChange) {
|
||||||
@@ -58,8 +64,7 @@ export default class VisiblePassword extends React.PureComponent {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div tabIndex="-1" className="group-focus"
|
<div tabIndex="-1" className="group-focus" onBlur={this.handleEditingFinished}>
|
||||||
onBlur={this.handleEditingFinished}>
|
|
||||||
<input className="with-visibility"
|
<input className="with-visibility"
|
||||||
type={this.state.visible ? "text" : "password"}
|
type={this.state.visible ? "text" : "password"}
|
||||||
value={this.state.value}
|
value={this.state.value}
|
||||||
@@ -68,7 +73,8 @@ export default class VisiblePassword extends React.PureComponent {
|
|||||||
autoFocus={this.props.autoFocus ? 'autoFocus' : ''}
|
autoFocus={this.props.autoFocus ? 'autoFocus' : ''}
|
||||||
autoComplete={this.props.autoComplete}
|
autoComplete={this.props.autoComplete}
|
||||||
onChange={this.handeTextChange}
|
onChange={this.handeTextChange}
|
||||||
onKeyDown={this.handleKeyDown} />
|
onKeyDown={this.handleKeyDown}
|
||||||
|
ref={this.inputRef} />
|
||||||
<span onClick={this.handleVisibility}>
|
<span onClick={this.handleVisibility}>
|
||||||
<i className="material-icons clickable light-gray">
|
<i className="material-icons clickable light-gray">
|
||||||
{this.state.visible ? 'visibility' : 'visibility_off'}
|
{this.state.visible ? 'visibility' : 'visibility_off'}
|
||||||
|
|||||||
+447
-257
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
+230
-24
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.
|
// 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