Merge branch 'devel' into repackage
This commit is contained in:
@@ -6,14 +6,18 @@ and [icons](https://google.github.io/material-design-icons/#icon-font-for-the-we
|
|||||||
|
|
||||||
Although the app is generally usable, keep in mind that this is work in progress. Some bugs probably exist. The app was tested in the latest Chrome & Firefox only. NPM [is available](https://www.npmjs.com/package/tinode-webapp).
|
Although the app is generally usable, keep in mind that this is work in progress. Some bugs probably exist. The app was tested in the latest Chrome & Firefox only. NPM [is available](https://www.npmjs.com/package/tinode-webapp).
|
||||||
|
|
||||||
Try possibly newer or older version live at https://api.tinode.co/. Login as one of `alice`, `bob`, `carol`, `dave`, `frank`. Password is `<login>123`, e.g. password for `alice` is `alice123`. You can discover other users by email or phone by prefixing them with `email:` or `tel:` respectively. Emails are `<login>@example.com`, e.g. `alice@example.com`, phones are `17025550001` through `17025550009`. The demo is reset every night at 2:15am Pacific time.
|
Try possibly newer or older version live at https://api.tinode.co/. For login and other instructions [see here](https://github.com/tinode/chat/#demo).
|
||||||
|
|
||||||
License: [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
|
||||||
|
|
||||||
Sample avatar photos from https://www.pexels.com/ under [CC0 license](https://www.pexels.com/photo-license/).
|
Sample avatar photos from https://www.pexels.com/ under [CC0 license](https://www.pexels.com/photo-license/).
|
||||||
|
|
||||||
Background pattern from http://subtlepatterns.com/, commercial and non-commercial use allowed with attribution.
|
Background pattern from http://subtlepatterns.com/, commercial and non-commercial use allowed with attribution.
|
||||||
|
|
||||||
|
## Getting support
|
||||||
|
|
||||||
|
* Read [client-side](http://tinode.github.io/js-api/) and [server-side](https://github.com/tinode/chat/blob/master/docs/API.md) API documentation.
|
||||||
|
* For support, general questions, discussions post to [https://groups.google.com/d/forum/tinode](https://groups.google.com/d/forum/tinode).
|
||||||
|
* For bugs and feature requests [open an issue](https://github.com/tinode/webchat/issues/new).
|
||||||
|
|
||||||
## Missing functionality
|
## Missing functionality
|
||||||
|
|
||||||
* Previews not generated for videos, audio, links or docs.
|
* Previews not generated for videos, audio, links or docs.
|
||||||
|
|||||||
+4
-4
@@ -1339,14 +1339,14 @@ ul.tabbar > li {
|
|||||||
ul.tabbar > li > a {
|
ul.tabbar > li > a {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-top: 0.75rem;
|
padding-top: 0.75rem;
|
||||||
text-decoration: none;
|
|
||||||
text-transform: uppercase;
|
|
||||||
display: block;
|
display: block;
|
||||||
min-width: 6rem;
|
min-width: 6.5rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 2rem;
|
line-height: 2rem;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 1.1rem;
|
font-size: 1.25rem;
|
||||||
|
font-variant: small-caps;
|
||||||
|
text-decoration: none;
|
||||||
color: rgba(33,150,243,0.6);
|
color: rgba(33,150,243,0.6);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: inset 0 -2px 2.5px -1px rgba(0,0,0,0.25);
|
box-shadow: inset 0 -2px 2.5px -1px rgba(0,0,0,0.25);
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "tinode-webapp",
|
"name": "tinode-webapp",
|
||||||
"description": "Tinode Web Client",
|
"description": "Tinode Web Client",
|
||||||
"version": "0.15.5-rc2",
|
"version": "0.15.5",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-cli": "^6.26.0",
|
"babel-cli": "^6.26.0",
|
||||||
"babel-minify": "^0.4.3",
|
"babel-minify": "^0.4.3",
|
||||||
|
|||||||
+209
-123
@@ -384,15 +384,25 @@ function setUrlTopic(hash, topic) {
|
|||||||
|
|
||||||
// Detect server address from the URL
|
// Detect server address from the URL
|
||||||
function detectServerAddress() {
|
function detectServerAddress() {
|
||||||
var host = DEFAULT_HOST;
|
let host = DEFAULT_HOST;
|
||||||
if (window.location.protocol === 'file:' || window.location.hostname === 'localhost') {
|
if (typeof window.location == 'object') {
|
||||||
host = KNOWN_HOSTS.local;
|
if (window.location.protocol == 'file:' || window.location.hostname == 'localhost') {
|
||||||
} else if (window.location.hostname) {
|
host = KNOWN_HOSTS.local;
|
||||||
host = window.location.hostname + (window.location.port ? ':' + window.location.port : '');
|
} else if (window.location.hostname) {
|
||||||
|
host = window.location.hostname + (window.location.port ? ':' + window.location.port : '');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return host;
|
return host;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detect if the page is served over HTTPS.
|
||||||
|
function isSecureConnection() {
|
||||||
|
if (typeof window.location == 'object') {
|
||||||
|
return window.location.protocol == 'https:';
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Create VCard which represents topic 'public' info
|
// Create VCard which represents topic 'public' info
|
||||||
function vcard(fn, imageDataUrl) {
|
function vcard(fn, imageDataUrl) {
|
||||||
var card = null;
|
var card = null;
|
||||||
@@ -475,7 +485,7 @@ function deleteMessages(all, hard, params, errorHandler) {
|
|||||||
var promise = all ?
|
var promise = all ?
|
||||||
topic.delMessagesAll(hard) :
|
topic.delMessagesAll(hard) :
|
||||||
topic.delMessagesList([params.seq], hard);
|
topic.delMessagesList([params.seq], hard);
|
||||||
promise.catch(function(err) {
|
promise.catch((err) => {
|
||||||
if (errorHandler) {
|
if (errorHandler) {
|
||||||
errorHandler(err.message, "err");
|
errorHandler(err.message, "err");
|
||||||
}
|
}
|
||||||
@@ -548,7 +558,7 @@ class ContextMenu extends React.Component {
|
|||||||
this.toggle(false);
|
this.toggle(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||||
this.toggle(nextProps.visible);
|
this.toggle(nextProps.visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -581,7 +591,7 @@ class ContextMenu extends React.Component {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.props.hide();
|
this.props.hide();
|
||||||
var item = this.props.items[e.currentTarget.dataset.id];
|
let item = this.props.items[e.currentTarget.dataset.id];
|
||||||
item.handler(this.props.params, this.props.onError);
|
item.handler(this.props.params, this.props.onError);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -590,16 +600,15 @@ class ContextMenu extends React.Component {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var count = 0;
|
let count = 0;
|
||||||
var instance = this;
|
let menu = [];
|
||||||
var menu = [];
|
this.props.items.map((item) => {
|
||||||
this.props.items.map(function(item) {
|
|
||||||
if (item && item.title) {
|
if (item && item.title) {
|
||||||
menu.push(
|
menu.push(
|
||||||
item.title === "-" ?
|
item.title === "-" ?
|
||||||
<li className="separator" key={count} />
|
<li className="separator" key={count} />
|
||||||
:
|
:
|
||||||
<li onClick={instance.handleClick} data-id={count} key={count}>{item.title}</li>
|
<li onClick={this.handleClick} data-id={count} key={count}>{item.title}</li>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
count++;
|
count++;
|
||||||
@@ -661,7 +670,7 @@ class MoreButton extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleToggle() {
|
handleToggle() {
|
||||||
var open = !this.state.open;
|
let open = !this.state.open;
|
||||||
this.setState({open: open});
|
this.setState({open: open});
|
||||||
if (this.props.onToggle) {
|
if (this.props.onToggle) {
|
||||||
this.props.onToggle(open);
|
this.props.onToggle(open);
|
||||||
@@ -679,11 +688,11 @@ class MoreButton extends React.PureComponent {
|
|||||||
/* BEGIN Lettertile: Avatar box: either a bitmap or a letter tile or a stock icon. */
|
/* BEGIN Lettertile: Avatar box: either a bitmap or a letter tile or a stock icon. */
|
||||||
class LetterTile extends React.PureComponent {
|
class LetterTile extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
var avatar;
|
let avatar;
|
||||||
if (this.props.avatar === true) {
|
if (this.props.avatar === true) {
|
||||||
if (this.props.topic && this.props.title && this.props.title.trim()) {
|
if (this.props.topic && this.props.title && this.props.title.trim()) {
|
||||||
var letter = this.props.title.trim().charAt(0);
|
let letter = this.props.title.trim().charAt(0);
|
||||||
var color = "lettertile dark-color" + (Math.abs(stringHash(this.props.topic)) % 16);
|
let color = "lettertile dark-color" + (Math.abs(stringHash(this.props.topic)) % 16);
|
||||||
avatar = (<div className={color}><div>{letter}</div></div>)
|
avatar = (<div className={color}><div>{letter}</div></div>)
|
||||||
} else {
|
} else {
|
||||||
avatar = (Tinode.topicType(this.props.topic) === "grp") ?
|
avatar = (Tinode.topicType(this.props.topic) === "grp") ?
|
||||||
@@ -708,6 +717,7 @@ class InPlaceEdit extends React.Component {
|
|||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
active: props.state,
|
active: props.state,
|
||||||
|
initialText: props.text || "",
|
||||||
text: props.text || ""
|
text: props.text || ""
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -717,12 +727,16 @@ class InPlaceEdit extends React.Component {
|
|||||||
this.handleEditingFinished = this.handleEditingFinished.bind(this);
|
this.handleEditingFinished = this.handleEditingFinished.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
static getDerivedStateFromProps(nextProps, prevState) {
|
||||||
// If text has changed while in read mode, update text and discard changes.
|
// If text has changed while in read mode, update text and discard changes.
|
||||||
// Ignore update if in edit mode.
|
// Ignore update if in edit mode.
|
||||||
if (this.props.text != nextProps.text && !this.state.active) {
|
if (prevState.initialText != nextProps.text && !prevState.active) {
|
||||||
this.setState({text: nextProps.text || ""});
|
return {
|
||||||
|
initialText: nextProps.text || "",
|
||||||
|
text: nextProps.text || ""
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
handeTextChange(e) {
|
handeTextChange(e) {
|
||||||
@@ -875,8 +889,8 @@ class PermissionsEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleChange(val) {
|
handleChange(val) {
|
||||||
var mode = this.state.mode;
|
let mode = this.state.mode;
|
||||||
var idx = mode.indexOf(val);
|
let idx = mode.indexOf(val);
|
||||||
if (idx == -1) {
|
if (idx == -1) {
|
||||||
mode += val;
|
mode += val;
|
||||||
} else {
|
} else {
|
||||||
@@ -901,8 +915,8 @@ class PermissionsEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
var all = 'JRWPASDO';
|
const all = 'JRWPASDO';
|
||||||
var names = {
|
const names = {
|
||||||
'J': 'Join (J)',
|
'J': 'Join (J)',
|
||||||
'R': 'Read (R)',
|
'R': 'Read (R)',
|
||||||
'W': 'Write (W)',
|
'W': 'Write (W)',
|
||||||
@@ -913,12 +927,12 @@ class PermissionsEditor extends React.Component {
|
|||||||
'O': 'Owner (O)'
|
'O': 'Owner (O)'
|
||||||
};
|
};
|
||||||
|
|
||||||
var skip = this.props.skip || "";
|
let skip = this.props.skip || "";
|
||||||
var mode = this.state.mode;
|
let mode = this.state.mode;
|
||||||
var compare = (this.props.compare || "").replace("N", "");
|
let compare = (this.props.compare || "").replace("N", "");
|
||||||
var items = [];
|
let items = [];
|
||||||
for (var i=0; i<all.length; i++) {
|
for (let i=0; i<all.length; i++) {
|
||||||
var c = all.charAt(i);
|
let c = all.charAt(i);
|
||||||
if (skip.indexOf(c) >= 0 && mode.indexOf(c) < 0) {
|
if (skip.indexOf(c) >= 0 && mode.indexOf(c) < 0) {
|
||||||
// Permission is marked as inactive: hide unchecked permissions, disable checked permissions
|
// Permission is marked as inactive: hide unchecked permissions, disable checked permissions
|
||||||
continue;
|
continue;
|
||||||
@@ -987,7 +1001,7 @@ class ChipInput extends React.Component {
|
|||||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||||
this.setState({
|
this.setState({
|
||||||
sortedChips: ChipInput.sortChips(nextProps.chips, nextProps.required),
|
sortedChips: ChipInput.sortChips(nextProps.chips, nextProps.required),
|
||||||
chipIndex: ChipInput.indexChips(nextProps.chips)
|
chipIndex: ChipInput.indexChips(nextProps.chips)
|
||||||
@@ -1149,8 +1163,10 @@ class GroupSubs extends React.Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
static getDerivedStateFromProps(nextProps, prevState) {
|
||||||
this.setState({onlineSubs: nextProps.subscribers ? nextProps.subscribers : []});
|
return {
|
||||||
|
onlineSubs: nextProps.subscribers ? nextProps.subscribers : []
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -1373,10 +1389,13 @@ class AvatarUpload extends React.Component {
|
|||||||
this.handleFileUpload = this.handleFileUpload.bind(this);
|
this.handleFileUpload = this.handleFileUpload.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
static getDerivedStateFromProps(nextProps, prevState) {
|
||||||
if (this.props.avatar != nextProps.avatar) {
|
if (prevState.dataUrl != nextProps.avatar) {
|
||||||
this.setState({dataUrl: nextProps.avatar});
|
return {
|
||||||
|
dataUrl: nextProps.avatar
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleFileUpload(e) {
|
handleFileUpload(e) {
|
||||||
@@ -1429,16 +1448,31 @@ class SettingsView extends React.PureComponent {
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
transport: "def"
|
transport: props.transport || "def",
|
||||||
|
messageSounds: props.messageSounds,
|
||||||
|
desktopAlerts: props.desktopAlerts && props.desktopAlertsEnabled
|
||||||
};
|
};
|
||||||
|
|
||||||
this.handleSubmit = this.handleSubmit.bind(this);
|
this.handleSubmit = this.handleSubmit.bind(this);
|
||||||
|
this.handleCheckboxClick = this.handleCheckboxClick.bind(this);
|
||||||
this.handleTransportSelected = this.handleTransportSelected.bind(this);
|
this.handleTransportSelected = this.handleTransportSelected.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit(e) {
|
handleSubmit(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.props.onUpdate(null, this.state.transport);
|
this.props.onUpdate({
|
||||||
|
transport: this.state.transport,
|
||||||
|
messageSounds: this.state.messageSounds,
|
||||||
|
desktopAlerts: this.state.desktopAlerts
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCheckboxClick(what, checked) {
|
||||||
|
if (what == "sound") {
|
||||||
|
this.setState({messageSounds: checked});
|
||||||
|
} else if (what == "alert") {
|
||||||
|
this.setState({desktopAlerts: checked});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTransportSelected(e) {
|
handleTransportSelected(e) {
|
||||||
@@ -1463,10 +1497,26 @@ class SettingsView extends React.PureComponent {
|
|||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<form id="settings-form" onSubmit={this.handleSubmit}>
|
<form id="settings-form" onSubmit={this.handleSubmit}>
|
||||||
<label className="small">Wire transport:</label>
|
<div className="panel-form-row">
|
||||||
<ul className="quoted">
|
<label forHtml="message-sound">Message sound:</label>
|
||||||
{transportOptions}
|
<CheckBox name="sound" id="message-sound"
|
||||||
</ul>
|
checked={this.state.messageSounds}
|
||||||
|
onChange={this.handleCheckboxClick} />
|
||||||
|
</div>
|
||||||
|
<div className="panel-form-row">
|
||||||
|
<label forHtml="desktop-alerts">Notification alerts{!this.props.desktopAlertsEnabled ? ' (requires HTTPS)' : null}:</label>
|
||||||
|
<CheckBox name="alert" id="desktop-alerts"
|
||||||
|
checked={this.state.desktopAlerts}
|
||||||
|
onChange={this.props.desktopAlertsEnabled ? this.handleCheckboxClick : null} />
|
||||||
|
</div>
|
||||||
|
<div className="panel-form-row">
|
||||||
|
<label className="small">Wire transport:</label>
|
||||||
|
</div>
|
||||||
|
<div className="panel-form-row">
|
||||||
|
<ul className="quoted">
|
||||||
|
{transportOptions}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
<div className="dialog-buttons">
|
<div className="dialog-buttons">
|
||||||
<button type="submit" className="blue">Update</button>
|
<button type="submit" className="blue">Update</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -1587,7 +1637,7 @@ class SidepanelView extends React.Component {
|
|||||||
disabled={this.props.loginDisabled}
|
disabled={this.props.loginDisabled}
|
||||||
serverAddress={this.props.serverAddress}
|
serverAddress={this.props.serverAddress}
|
||||||
onLogin={this.handleLoginRequested}
|
onLogin={this.handleLoginRequested}
|
||||||
onServerAddressChange={this.props.onConnectionSettings} /> :
|
onServerAddressChange={this.props.onGlobalSettings} /> :
|
||||||
|
|
||||||
view === 'register' ?
|
view === 'register' ?
|
||||||
<CreateAccountView
|
<CreateAccountView
|
||||||
@@ -1596,8 +1646,13 @@ class SidepanelView extends React.Component {
|
|||||||
onError={this.props.onError} /> :
|
onError={this.props.onError} /> :
|
||||||
|
|
||||||
view === 'settings' ?
|
view === 'settings' ?
|
||||||
<SettingsView onCancel={this.props.onCancel}
|
<SettingsView
|
||||||
onUpdate={this.props.onConnectionSettings}
|
transport={this.props.transport}
|
||||||
|
messageSounds={this.props.messageSounds}
|
||||||
|
desktopAlerts={this.props.desktopAlerts}
|
||||||
|
desktopAlertsEnabled={this.props.desktopAlertsEnabled}
|
||||||
|
onCancel={this.props.onCancel}
|
||||||
|
onUpdate={this.props.onGlobalSettings}
|
||||||
/> :
|
/> :
|
||||||
|
|
||||||
view === 'edit' ?
|
view === 'edit' ?
|
||||||
@@ -1614,6 +1669,7 @@ class SidepanelView extends React.Component {
|
|||||||
connected={this.props.connected}
|
connected={this.props.connected}
|
||||||
topicSelected={this.props.topicSelected}
|
topicSelected={this.props.topicSelected}
|
||||||
showContextMenu={this.props.showContextMenu}
|
showContextMenu={this.props.showContextMenu}
|
||||||
|
messageSounds={this.props.messageSounds}
|
||||||
onTopicSelected={this.props.onTopicSelected}
|
onTopicSelected={this.props.onTopicSelected}
|
||||||
onAcsChange={this.props.onAcsChange}
|
onAcsChange={this.props.onAcsChange}
|
||||||
onOnlineChange={this.props.onOnlineChange} /> :
|
onOnlineChange={this.props.onOnlineChange} /> :
|
||||||
@@ -1652,8 +1708,10 @@ class ErrorPanel extends React.PureComponent {
|
|||||||
this.hide = this.hide.bind(this);
|
this.hide = this.hide.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
static getDerivedStateFromProps(nextProps, prevState) {
|
||||||
this.setState({show: !(!nextProps.level)});
|
return {
|
||||||
|
show: !(!nextProps.level)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
@@ -1832,8 +1890,8 @@ class TagManager extends React.Component {
|
|||||||
this.handleCancel = this.handleCancel.bind(this);
|
this.handleCancel = this.handleCancel.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
static getDerivedStateFromProps(nextProps, prevState) {
|
||||||
this.setState({tags: nextProps.tags});
|
return {tags: nextProps.tags};
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowTagManager() {
|
handleShowTagManager() {
|
||||||
@@ -2037,16 +2095,15 @@ class UnreadBadge extends React.PureComponent {
|
|||||||
|
|
||||||
/* Contact's labels: [you], [muted], [blocked], etc */
|
/* Contact's labels: [you], [muted], [blocked], etc */
|
||||||
// FIXME: this class is unused.
|
// FIXME: this class is unused.
|
||||||
class ContactBadges_UNUSED_REMOVE extends React.Component {
|
class ContactBadges_UNUSED_REMOVE extends React.PureComponent {
|
||||||
render() {
|
render() {
|
||||||
var badges = null;
|
let badges = null;
|
||||||
if (this.props.badges && this.props.badges.length > 0) {
|
if (this.props.badges && this.props.badges.length > 0) {
|
||||||
var count = 0;
|
|
||||||
badges = [];
|
badges = [];
|
||||||
this.props.badges.map(function(b) {
|
this.props.badges.map(function(b) {
|
||||||
var style = "badge" + (b.color ? " " + b.color : "");
|
var style = "badge" + (b.color ? " " + b.color : "");
|
||||||
badges.push(<span className={style} key={count}>{b.name}</span>);
|
// Badge names are expected to be unique, so using the name as the key.
|
||||||
count ++;
|
badges.push(<span className={style} key={b.name}>{b.name}</span>);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return badges;
|
return badges;
|
||||||
@@ -2079,7 +2136,7 @@ class ContactsView extends React.Component {
|
|||||||
me.onSubsUpdated = undefined;
|
me.onSubsUpdated = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||||
if (this.props.topicSelected != nextProps.topicSelected) {
|
if (this.props.topicSelected != nextProps.topicSelected) {
|
||||||
// If topicSelecetd is changed externally, update the
|
// If topicSelecetd is changed externally, update the
|
||||||
// topic with online status and access mode.
|
// topic with online status and access mode.
|
||||||
@@ -2107,12 +2164,12 @@ class ContactsView extends React.Component {
|
|||||||
// New message received
|
// New message received
|
||||||
// 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 (this.props.topicSelected !== cont.topic) {
|
if (this.props.topicSelected !== cont.topic) {
|
||||||
// Disable sound for now. Need to add a config option.
|
if (this.props.messageSounds) {
|
||||||
// POP_SOUND.play();
|
POP_SOUND.play();
|
||||||
|
}
|
||||||
this.resetContactList();
|
this.resetContactList();
|
||||||
} else if (document.hidden) {
|
} else if (document.hidden && this.props.messageSounds) {
|
||||||
// Disable sound for now.
|
POP_SOUND.play();
|
||||||
// POP_SOUND.play();
|
|
||||||
}
|
}
|
||||||
} else if (what === "recv") {
|
} else if (what === "recv") {
|
||||||
// Explicitly ignoring "recv" -- it causes no visible updates to contact list.
|
// Explicitly ignoring "recv" -- it causes no visible updates to contact list.
|
||||||
@@ -2434,8 +2491,7 @@ class NewTopicView extends React.Component {
|
|||||||
this.state = {
|
this.state = {
|
||||||
tabSelected: "p2p",
|
tabSelected: "p2p",
|
||||||
searchQuery: props.contactsSearchQuery,
|
searchQuery: props.contactsSearchQuery,
|
||||||
contactList: props.foundContacts,
|
contactList: props.foundContacts
|
||||||
contactSelected: null
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.handleTabClick = this.handleTabClick.bind(this);
|
this.handleTabClick = this.handleTabClick.bind(this);
|
||||||
@@ -2448,17 +2504,17 @@ class NewTopicView extends React.Component {
|
|||||||
this.props.onInitFind();
|
this.props.onInitFind();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
static getDerivedStateFromProps(nextProps, prevState) {
|
||||||
this.setState({
|
return {
|
||||||
searchQuery: nextProps.contactsSearchQuery,
|
searchQuery: nextProps.contactsSearchQuery,
|
||||||
contactList: nextProps.foundContacts
|
contactList: nextProps.foundContacts
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTabClick(e) {
|
handleTabClick(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
window.location.hash = addUrlParam(window.location.hash, 'tab', e.currentTarget.dataset.id);
|
window.location.hash = addUrlParam(window.location.hash, 'tab', e.currentTarget.dataset.id);
|
||||||
this.setState({tabSelected: e.currentTarget.dataset.id, contactSelected: null});
|
this.setState({tabSelected: e.currentTarget.dataset.id});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleContactSelected(sel) {
|
handleContactSelected(sel) {
|
||||||
@@ -2483,10 +2539,10 @@ class NewTopicView extends React.Component {
|
|||||||
<div className="flex-column">
|
<div className="flex-column">
|
||||||
<ul className="tabbar">
|
<ul className="tabbar">
|
||||||
<li className={this.state.tabSelected === "p2p" ? "active" : null}>
|
<li className={this.state.tabSelected === "p2p" ? "active" : null}>
|
||||||
<a href="javascript:;" data-id="p2p" onClick={this.handleTabClick}>1:1</a>
|
<a href="javascript:;" data-id="p2p" onClick={this.handleTabClick}>find</a>
|
||||||
</li>
|
</li>
|
||||||
<li className={this.state.tabSelected === "grp" ? "active" : null}>
|
<li className={this.state.tabSelected === "grp" ? "active" : null}>
|
||||||
<a href="javascript:;" data-id="grp" onClick={this.handleTabClick}>group</a>
|
<a href="javascript:;" data-id="grp" onClick={this.handleTabClick}>new group</a>
|
||||||
</li>
|
</li>
|
||||||
<li className={this.state.tabSelected === "byid" ? "active" : null}>
|
<li className={this.state.tabSelected === "byid" ? "active" : null}>
|
||||||
<a href="javascript:;" data-id="byid" onClick={this.handleTabClick}>by id</a>
|
<a href="javascript:;" data-id="byid" onClick={this.handleTabClick}>by id</a>
|
||||||
@@ -2606,7 +2662,7 @@ class SearchContacts extends React.Component {
|
|||||||
this.handleSearchChange = this.handleSearchChange.bind(this);
|
this.handleSearchChange = this.handleSearchChange.bind(this);
|
||||||
this.handleSearch = this.handleSearch.bind(this);
|
this.handleSearch = this.handleSearch.bind(this);
|
||||||
this.handleClear = this.handleClear.bind(this);
|
this.handleClear = this.handleClear.bind(this);
|
||||||
this.handleKeyPress = this.handleKeyPress.bind(this);
|
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
@@ -2626,6 +2682,8 @@ class SearchContacts extends React.Component {
|
|||||||
if (query.length > 0) {
|
if (query.length > 0) {
|
||||||
this.setState({edited: true});
|
this.setState({edited: true});
|
||||||
this.props.onSearchContacts(query);
|
this.props.onSearchContacts(query);
|
||||||
|
} else if (this.props.searchQuery) {
|
||||||
|
this.props.onSearchContacts(DEL_CHAR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2636,9 +2694,11 @@ class SearchContacts extends React.Component {
|
|||||||
this.setState({search: '', edited: false});
|
this.setState({search: '', edited: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleKeyPress(e) {
|
handleKeyDown(e) {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
this.handleSearch(e);
|
this.handleSearch(e);
|
||||||
|
} else if (e.key === 'Escape') {
|
||||||
|
this.handleClear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2650,7 +2710,7 @@ class SearchContacts extends React.Component {
|
|||||||
<input className="search" type="text"
|
<input className="search" type="text"
|
||||||
placeholder="List like email:alice@example.com, tel:17025550003..."
|
placeholder="List like email:alice@example.com, tel:17025550003..."
|
||||||
value={this.state.search} onChange={this.handleSearchChange}
|
value={this.state.search} onChange={this.handleSearchChange}
|
||||||
onKeyPress={this.handleKeyPress} required autoFocus />
|
onKeyDown={this.handleKeyDown} required autoFocus />
|
||||||
<a href="javascript:;" onClick={this.handleClear}>
|
<a href="javascript:;" onClick={this.handleClear}>
|
||||||
<i className="material-icons">close</i>
|
<i className="material-icons">close</i>
|
||||||
</a>
|
</a>
|
||||||
@@ -2726,8 +2786,8 @@ class ValidationView extends React.PureComponent {
|
|||||||
this.handleSubmit = this.handleSubmit.bind(this);
|
this.handleSubmit = this.handleSubmit.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
static getDerivedStateFromProps(nextProps, prevState) {
|
||||||
this.setState({code: nextProps.credCode});
|
return {code: nextProps.credCode};
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange(e) {
|
handleChange(e) {
|
||||||
@@ -2758,7 +2818,7 @@ class ValidationView extends React.PureComponent {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="panel-form-row">
|
<div className="panel-form-row">
|
||||||
<input type="text" id="enter-confirmation-code" placeholder="The code is 123456"
|
<input type="text" id="enter-confirmation-code" placeholder="Numbers only"
|
||||||
value={this.state.code} onChange={this.handleChange}
|
value={this.state.code} onChange={this.handleChange}
|
||||||
onKeyPress={this.handleKeyPress} required />
|
onKeyPress={this.handleKeyPress} required />
|
||||||
</div>
|
</div>
|
||||||
@@ -2828,7 +2888,7 @@ class InfoView extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// No need to separately handle component mount.
|
// No need to separately handle component mount.
|
||||||
componentWillReceiveProps(props) {
|
UNSAFE_componentWillReceiveProps(props) {
|
||||||
var topic = Tinode.getTopic(props.topic);
|
var topic = Tinode.getTopic(props.topic);
|
||||||
if (!topic) {
|
if (!topic) {
|
||||||
return;
|
return;
|
||||||
@@ -3191,7 +3251,7 @@ class InfoView extends React.Component {
|
|||||||
<div className="panel-form-row">
|
<div className="panel-form-row">
|
||||||
<label>Muted:</label>
|
<label>Muted:</label>
|
||||||
<CheckBox name="P" checked={this.state.muted}
|
<CheckBox name="P" checked={this.state.muted}
|
||||||
onChange={this.handleMuted} />
|
onChange={this.handleMuted} />
|
||||||
</div>
|
</div>
|
||||||
<MoreButton
|
<MoreButton
|
||||||
title="More"
|
title="More"
|
||||||
@@ -3579,6 +3639,7 @@ class MessagesView extends React.Component {
|
|||||||
this.handleDescChange = this.handleDescChange.bind(this);
|
this.handleDescChange = this.handleDescChange.bind(this);
|
||||||
this.handleSubsUpdated = this.handleSubsUpdated.bind(this);
|
this.handleSubsUpdated = this.handleSubsUpdated.bind(this);
|
||||||
this.handleNewMessage = this.handleNewMessage.bind(this);
|
this.handleNewMessage = this.handleNewMessage.bind(this);
|
||||||
|
this.handleAllMessagesReceived = this.handleAllMessagesReceived.bind(this);
|
||||||
this.handleInfoReceipt = this.handleInfoReceipt.bind(this);
|
this.handleInfoReceipt = this.handleInfoReceipt.bind(this);
|
||||||
this.handleImagePreview = this.handleImagePreview.bind(this);
|
this.handleImagePreview = this.handleImagePreview.bind(this);
|
||||||
this.handleCloseImagePreview = this.handleCloseImagePreview.bind(this);
|
this.handleCloseImagePreview = this.handleCloseImagePreview.bind(this);
|
||||||
@@ -3610,7 +3671,7 @@ class MessagesView extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||||
this.propsChange(nextProps);
|
this.propsChange(nextProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3636,6 +3697,7 @@ class MessagesView extends React.Component {
|
|||||||
|
|
||||||
// Bind the new topic to component.
|
// Bind the new topic to component.
|
||||||
topic.onData = this.handleNewMessage;
|
topic.onData = this.handleNewMessage;
|
||||||
|
topic.onAllMessagesReceived = this.handleAllMessagesReceived;
|
||||||
topic.onInfo = this.handleInfoReceipt;
|
topic.onInfo = this.handleInfoReceipt;
|
||||||
topic.onMetaDesc = this.handleDescChange;
|
topic.onMetaDesc = this.handleDescChange;
|
||||||
topic.onSubsUpdated = this.handleSubsUpdated;
|
topic.onSubsUpdated = this.handleSubsUpdated;
|
||||||
@@ -3688,10 +3750,6 @@ class MessagesView extends React.Component {
|
|||||||
// Show "loading" spinner.
|
// Show "loading" spinner.
|
||||||
this.setState({ fetchingMessages: true });
|
this.setState({ fetchingMessages: true });
|
||||||
topic.subscribe(getQuery.build())
|
topic.subscribe(getQuery.build())
|
||||||
.then(() => {
|
|
||||||
// Hide spinner.
|
|
||||||
this.setState({ fetchingMessages: false });
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
this.props.onError(err.message, "err");
|
this.props.onError(err.message, "err");
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -3716,6 +3774,7 @@ class MessagesView extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
oldTopic.onData = undefined;
|
oldTopic.onData = undefined;
|
||||||
|
oldTopic.onAllMessagesReceived = undefined;
|
||||||
oldTopic.onInfo = undefined;
|
oldTopic.onInfo = undefined;
|
||||||
oldTopic.onMetaDesc = undefined;
|
oldTopic.onMetaDesc = undefined;
|
||||||
oldTopic.onSubsUpdated = undefined;
|
oldTopic.onSubsUpdated = undefined;
|
||||||
@@ -3733,20 +3792,16 @@ class MessagesView extends React.Component {
|
|||||||
|
|
||||||
// Get older messages
|
// Get older messages
|
||||||
fetchMoreMessages(event) {
|
fetchMoreMessages(event) {
|
||||||
var instance = this;
|
|
||||||
if (event.target.scrollTop <= 0) {
|
if (event.target.scrollTop <= 0) {
|
||||||
var started = false;
|
let newState = {scrollPosition: event.target.scrollHeight - event.target.scrollTop};
|
||||||
var newState = {scrollPosition: event.target.scrollHeight - event.target.scrollTop};
|
this.setState((prevState, props) => {
|
||||||
this.setState(function(prevState, props) {
|
|
||||||
if (!prevState.fetchingMessages) {
|
if (!prevState.fetchingMessages) {
|
||||||
var topic = Tinode.getTopic(instance.state.topic);
|
var topic = Tinode.getTopic(this.state.topic);
|
||||||
if (topic && topic.isSubscribed() && topic.msgHasMoreMessages()) {
|
if (topic && topic.isSubscribed() && topic.msgHasMoreMessages()) {
|
||||||
newState.fetchingMessages = true;
|
newState.fetchingMessages = true;
|
||||||
topic.getMessagesPage(MESSAGES_PAGE).then(function() {
|
topic.getMessagesPage(MESSAGES_PAGE).catch((err) => {
|
||||||
instance.setState({fetchingMessages: false});
|
this.setState({fetchingMessages: false});
|
||||||
}).catch(function(err) {
|
this.props.onError(err.message, "err");
|
||||||
instance.setState({fetchingMessages: false});
|
|
||||||
instance.props.onError(err.message, "err");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3815,6 +3870,10 @@ class MessagesView extends React.Component {
|
|||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleAllMessagesReceived(count) {
|
||||||
|
this.setState({fetchingMessages: false});
|
||||||
|
}
|
||||||
|
|
||||||
handleInfoReceipt(info) {
|
handleInfoReceipt(info) {
|
||||||
switch (info.what) {
|
switch (info.what) {
|
||||||
case "kp": {
|
case "kp": {
|
||||||
@@ -4301,7 +4360,7 @@ class TinodeWeb extends React.Component {
|
|||||||
this.handleUpdateAccountRequest = this.handleUpdateAccountRequest.bind(this);
|
this.handleUpdateAccountRequest = this.handleUpdateAccountRequest.bind(this);
|
||||||
this.handleUpdateAccountTagsRequest = this.handleUpdateAccountTagsRequest.bind(this);
|
this.handleUpdateAccountTagsRequest = this.handleUpdateAccountTagsRequest.bind(this);
|
||||||
this.handleSettings = this.handleSettings.bind(this);
|
this.handleSettings = this.handleSettings.bind(this);
|
||||||
this.handleConnectionSettings = this.handleConnectionSettings.bind(this);
|
this.handleGlobalSettings = this.handleGlobalSettings.bind(this);
|
||||||
this.handleSidepanelCancel = this.handleSidepanelCancel.bind(this);
|
this.handleSidepanelCancel = this.handleSidepanelCancel.bind(this);
|
||||||
this.handleNewTopic = this.handleNewTopic.bind(this);
|
this.handleNewTopic = this.handleNewTopic.bind(this);
|
||||||
this.handleNewTopicRequest = this.handleNewTopicRequest.bind(this);
|
this.handleNewTopicRequest = this.handleNewTopicRequest.bind(this);
|
||||||
@@ -4323,10 +4382,17 @@ class TinodeWeb extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getBlankState() {
|
getBlankState() {
|
||||||
|
let settings = localStorage.getObject("settings") || {};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
connected: false,
|
connected: false,
|
||||||
transport: null,
|
transport: settings.transport || null,
|
||||||
serverAddress: detectServerAddress(),
|
serverAddress: settings.serverAddress || detectServerAddress(),
|
||||||
|
// "On" is the default, so saving the "off" state.
|
||||||
|
messageSounds: !settings.messageSoundsOff,
|
||||||
|
desktopAlerts: settings.desktopAlerts,
|
||||||
|
desktopAlertsEnabled: isSecureConnection(),
|
||||||
|
|
||||||
sidePanelSelected: 'login',
|
sidePanelSelected: 'login',
|
||||||
sidePanelTitle: null,
|
sidePanelTitle: null,
|
||||||
sidePanelAvatar: null,
|
sidePanelAvatar: null,
|
||||||
@@ -4351,7 +4417,7 @@ class TinodeWeb extends React.Component {
|
|||||||
contextMenuParams: null,
|
contextMenuParams: null,
|
||||||
contextMenuItems: [],
|
contextMenuItems: [],
|
||||||
serverVersion: 'no connection',
|
serverVersion: 'no connection',
|
||||||
contactsSearchQuery: '',
|
contactsSearchQuery: undefined,
|
||||||
foundContacts: [],
|
foundContacts: [],
|
||||||
credMethod: undefined,
|
credMethod: undefined,
|
||||||
credCode: undefined
|
credCode: undefined
|
||||||
@@ -4445,11 +4511,11 @@ class TinodeWeb extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save validation credentials, if available.
|
// Save validation credentials, if available.
|
||||||
if (hash.params.code && hash.params.method) {
|
if (hash.params.method) {
|
||||||
this.setState({
|
this.setState({ credMethod: hash.params.method });
|
||||||
credCode: hash.params.code,
|
}
|
||||||
credMethod: hash.params.method
|
if (hash.params.code) {
|
||||||
});
|
this.setState({ credCode: hash.params.code });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Additional parameters of panels.
|
// Additional parameters of panels.
|
||||||
@@ -4495,17 +4561,16 @@ class TinodeWeb extends React.Component {
|
|||||||
|
|
||||||
// User clicked Login button in the side panel.
|
// User clicked Login button in the side panel.
|
||||||
handleLoginRequest(login, password) {
|
handleLoginRequest(login, password) {
|
||||||
var instance = this;
|
|
||||||
this.setState({loginDisabled: true, login: login, password: password});
|
this.setState({loginDisabled: true, login: login, password: password});
|
||||||
this.handleError("", null);
|
this.handleError("", null);
|
||||||
|
|
||||||
if (Tinode.isConnected()) {
|
if (Tinode.isConnected()) {
|
||||||
this.doLogin(login, password, {meth: this.state.credMethod, resp: this.state.credCode});
|
this.doLogin(login, password, {meth: this.state.credMethod, resp: this.state.credCode});
|
||||||
} else {
|
} else {
|
||||||
Tinode.connect().catch(function(err) {
|
Tinode.connect().catch((err) => {
|
||||||
// Socket error
|
// Socket error
|
||||||
instance.setState({loginDisabled: false});
|
this.setState({loginDisabled: false});
|
||||||
instance.handleError(err.message, "err");
|
this.handleError(err.message, "err");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4542,6 +4607,9 @@ class TinodeWeb extends React.Component {
|
|||||||
if (promise) {
|
if (promise) {
|
||||||
promise.then((ctrl) => {
|
promise.then((ctrl) => {
|
||||||
if (ctrl.code >= 300 && ctrl.text === "validate credentials") {
|
if (ctrl.code >= 300 && ctrl.text === "validate credentials") {
|
||||||
|
if (cred) {
|
||||||
|
this.handleError("Code does not match", "warn");
|
||||||
|
}
|
||||||
this.handleCredentialsRequest(ctrl.params);
|
this.handleCredentialsRequest(ctrl.params);
|
||||||
} else {
|
} else {
|
||||||
this.handleLoginSuccessful(this);
|
this.handleLoginSuccessful(this);
|
||||||
@@ -4640,9 +4708,8 @@ class TinodeWeb extends React.Component {
|
|||||||
this.tnFndSubsUpdated();
|
this.tnFndSubsUpdated();
|
||||||
} else {
|
} else {
|
||||||
fnd.onSubsUpdated = this.tnFndSubsUpdated;
|
fnd.onSubsUpdated = this.tnFndSubsUpdated;
|
||||||
var instance = this;
|
fnd.subscribe(fnd.startMetaQuery().withSub().withTags().build()).catch((err) => {
|
||||||
fnd.subscribe(fnd.startMetaQuery().withSub().withTags().build()).catch(function(err){
|
this.handleError(err.message, "err");
|
||||||
instance.handleError(err.message, "err");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4655,16 +4722,17 @@ class TinodeWeb extends React.Component {
|
|||||||
});
|
});
|
||||||
this.setState({foundContacts: contacts});
|
this.setState({foundContacts: contacts});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Called when the user enters a contact into the contact search field in the NewAccount panel
|
/** Called when the user enters a contact into the contact search field in the NewAccount panel
|
||||||
@param query {Array} is an array of contacts to search for
|
@param query {Array} is an array of contacts to search for
|
||||||
*/
|
*/
|
||||||
handleSearchContacts(query) {
|
handleSearchContacts(query) {
|
||||||
var fnd = Tinode.getFndTopic();
|
var fnd = Tinode.getFndTopic();
|
||||||
var instance = this;
|
this.setState({contactsSearchQuery: query == DEL_CHAR ? '' : query});
|
||||||
fnd.setMeta({desc: {public: query}}).then(function(ctrl) {
|
fnd.setMeta({desc: {public: query}}).then((ctrl) => {
|
||||||
return fnd.getMeta(fnd.startMetaQuery().withSub().build());
|
return fnd.getMeta(fnd.startMetaQuery().withSub().build());
|
||||||
}).catch(function(err){
|
}).catch((err) => {
|
||||||
instance.handleError(err.message, "err");
|
this.handleError(err.message, "err");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4751,19 +4819,18 @@ class TinodeWeb extends React.Component {
|
|||||||
|
|
||||||
// Actual registration of a new account.
|
// Actual registration of a new account.
|
||||||
handleNewAccountRequest(login_, password_, public_, cred_, tags_) {
|
handleNewAccountRequest(login_, password_, public_, cred_, tags_) {
|
||||||
var instance = this;
|
|
||||||
Tinode.connect(this.state.serverAddress)
|
Tinode.connect(this.state.serverAddress)
|
||||||
.then(function() {
|
.then(function() {
|
||||||
var params = Tinode.addCredential({public: public_, tags: tags_}, cred_);
|
var params = Tinode.addCredential({public: public_, tags: tags_}, cred_);
|
||||||
return Tinode.createAccountBasic(login_, password_, params);
|
return Tinode.createAccountBasic(login_, password_, params);
|
||||||
}).then(function(ctrl) {
|
}).then((ctrl) => {
|
||||||
if (ctrl.code >= 300 && ctrl.text === "validate credentials") {
|
if (ctrl.code >= 300 && ctrl.text === "validate credentials") {
|
||||||
instance.handleCredentialsRequest(ctrl.params);
|
this.handleCredentialsRequest(ctrl.params);
|
||||||
} else {
|
} else {
|
||||||
instance.handleLoginSuccessful(instance);
|
this.handleLoginSuccessful(this);
|
||||||
}
|
}
|
||||||
}).catch(function(err) {
|
}).catch((err) => {
|
||||||
instance.handleError(err.message, "err");
|
this.handleError(err.message, "err");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4794,11 +4861,26 @@ class TinodeWeb extends React.Component {
|
|||||||
window.location.hash = setUrlSidePanel(window.location.hash, this.state.myUserId ? 'edit' : 'settings');
|
window.location.hash = setUrlSidePanel(window.location.hash, this.state.myUserId ? 'edit' : 'settings');
|
||||||
}
|
}
|
||||||
|
|
||||||
// User updated connection parameters - transport and server address
|
// User updated global parameters.
|
||||||
handleConnectionSettings(serverAddress, transport) {
|
handleGlobalSettings(settings) {
|
||||||
transport = transport || this.state.transport;
|
let serverAddress = settings.serverAddress || this.state.serverAddress;
|
||||||
serverAddress = serverAddress || this.state.serverAddress;
|
let transport = settings.transport || this.state.transport;
|
||||||
this.setState({serverAddress: serverAddress, transport: transport, sidePanelSelected: 'login'});
|
let messageSounds = (typeof settings.messageSounds == 'boolean') ? settings.messageSounds : this.state.messageSounds;
|
||||||
|
let desktopAlerts = settings.desktopAlerts || this.state.desktopAlerts;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
serverAddress: serverAddress,
|
||||||
|
transport: transport,
|
||||||
|
messageSounds: messageSounds,
|
||||||
|
desktopAlerts: desktopAlerts,
|
||||||
|
});
|
||||||
|
localStorage.setObject({
|
||||||
|
serverAddress: serverAddress,
|
||||||
|
messageSoundsOff: !messageSounds,
|
||||||
|
transport: this.state.transport,
|
||||||
|
desktopAlerts: desktopAlerts
|
||||||
|
});
|
||||||
|
window.location.hash = setUrlSidePanel(window.location.hash, '');
|
||||||
TinodeWeb.tnSetup(serverAddress, transport);
|
TinodeWeb.tnSetup(serverAddress, transport);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5028,8 +5110,12 @@ class TinodeWeb extends React.Component {
|
|||||||
credMethod={this.state.credMethod}
|
credMethod={this.state.credMethod}
|
||||||
credCode={this.state.credCode}
|
credCode={this.state.credCode}
|
||||||
|
|
||||||
|
transport={this.state.transport}
|
||||||
|
messageSounds={this.state.messageSounds}
|
||||||
|
desktopAlerts={this.state.desktopAlerts}
|
||||||
|
desktopAlertsEnabled={this.state.desktopAlertsEnabled}
|
||||||
serverAddress={this.state.serverAddress}
|
serverAddress={this.state.serverAddress}
|
||||||
onConnectionSettings={this.handleConnectionSettings}
|
onGlobalSettings={this.handleGlobalSettings}
|
||||||
|
|
||||||
onSignUp={this.handleNewAccount}
|
onSignUp={this.handleNewAccount}
|
||||||
onSettings={this.handleSettings}
|
onSettings={this.handleSettings}
|
||||||
|
|||||||
Reference in New Issue
Block a user