merge next

This commit is contained in:
or-else
2023-06-10 12:14:59 -07:00
81 changed files with 28106 additions and 24984 deletions
+2444 -1655
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -1,7 +1,7 @@
{ {
"name": "tinode-webapp", "name": "tinode-webapp",
"description": "Tinode messenger for the web", "description": "Tinode messenger for the web",
"version": "0.23.0-rc1", "version": "0.23.0-rc2",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/tinode/webapp.git" "url": "https://github.com/tinode/webapp.git"
@@ -84,8 +84,8 @@
"babel-plugin-formatjs": "^10.4.0", "babel-plugin-formatjs": "^10.4.0",
"browserslist": "^4.21.5", "browserslist": "^4.21.5",
"copy-webpack-plugin": "^11.0.0", "copy-webpack-plugin": "^11.0.0",
"cssnano": "^5.1.15", "cssnano": "^6.0.1",
"glob": "^9.3.0", "glob": "^10.2.7",
"postcss": "^8.4.21", "postcss": "^8.4.21",
"postcss-cli": "^10.1.0", "postcss-cli": "^10.1.0",
"webpack": "^5.76.1", "webpack": "^5.76.1",
+1 -1
View File
@@ -15,7 +15,7 @@ const OUTPUT_PATTERN = './src/i18n.min/%s.json';
const args = process.argv.slice(2); const args = process.argv.slice(2);
function getLocaleFromFileName(filename) { function getLocaleFromFileName(filename) {
const parts = filename.split('/'); const parts = filename.split(path.sep);
return parts[parts.length - 1].split('.json')[0]; return parts[parts.length - 1].split('.json')[0];
} }
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+8 -1
View File
@@ -1420,7 +1420,7 @@
"cred_confirmed_successfully": { "cred_confirmed_successfully": {
"translation": "Erfolgreich bestätigt", "translation": "Erfolgreich bestätigt",
"defaultMessage": "Confirmed successfully", "defaultMessage": "Confirmed successfully",
"description": "Message explaining that the credential was successfully validated.", "description": "Notification message that the credential was successfully validated.",
"missing": false, "missing": false,
"obsolete": false "obsolete": false
}, },
@@ -1654,5 +1654,12 @@
"description": "Conference call label", "description": "Conference call label",
"missing": true, "missing": true,
"obsolete": false "obsolete": false
},
"password_reset_success": {
"translation": "Passwort erfolgreich zurückgesetzt",
"defaultMessage": "Password reset successfully",
"description": "Notification message that the password was successfully reset.",
"missing": false,
"obsolete": false
} }
} }
+8 -1
View File
@@ -1420,7 +1420,7 @@
"cred_confirmed_successfully": { "cred_confirmed_successfully": {
"translation": "Confirmed successfully", "translation": "Confirmed successfully",
"defaultMessage": "Confirmed successfully", "defaultMessage": "Confirmed successfully",
"description": "Message explaining that the credential was successfully validated.", "description": "Notification message that the credential was successfully validated.",
"missing": false, "missing": false,
"obsolete": false "obsolete": false
}, },
@@ -1654,5 +1654,12 @@
"description": "Conference call label", "description": "Conference call label",
"missing": false, "missing": false,
"obsolete": false "obsolete": false
},
"password_reset_success": {
"translation": "Password reset successfully",
"defaultMessage": "Password reset successfully",
"description": "Notification message that the password was successfully reset.",
"missing": false,
"obsolete": false
} }
} }
+8 -1
View File
@@ -1420,7 +1420,7 @@
"cred_confirmed_successfully": { "cred_confirmed_successfully": {
"translation": "Confirmado con éxito", "translation": "Confirmado con éxito",
"defaultMessage": "Confirmed successfully", "defaultMessage": "Confirmed successfully",
"description": "Message explaining that the credential was successfully validated.", "description": "Notification message that the credential was successfully validated.",
"missing": false, "missing": false,
"obsolete": false "obsolete": false
}, },
@@ -1654,5 +1654,12 @@
"description": "Conference call label", "description": "Conference call label",
"missing": true, "missing": true,
"obsolete": false "obsolete": false
},
"password_reset_success": {
"translation": "Restablecimiento de contraseña con éxito",
"defaultMessage": "Password reset successfully",
"description": "Notification message that the password was successfully reset.",
"missing": false,
"obsolete": false
} }
} }
+5 -1
View File
@@ -213,7 +213,7 @@
}, },
"cred_confirmed_successfully": { "cred_confirmed_successfully": {
"defaultMessage": "Confirmed successfully", "defaultMessage": "Confirmed successfully",
"description": "Message explaining that the credential was successfully validated." "description": "Notification message that the credential was successfully validated."
}, },
"credential_email_prompt": { "credential_email_prompt": {
"defaultMessage": "Your registration email", "defaultMessage": "Your registration email",
@@ -708,6 +708,10 @@
"defaultMessage": "A text message with security code has been sent.", "defaultMessage": "A text message with security code has been sent.",
"description": "Notification that the SMS with password reset instructions has been sent" "description": "Notification that the SMS with password reset instructions has been sent"
}, },
"password_reset_success": {
"defaultMessage": "Password reset successfully",
"description": "Notification message that the password was successfully reset."
},
"password_unchanged_prompt": { "password_unchanged_prompt": {
"defaultMessage": "Unchanged", "defaultMessage": "Unchanged",
"description": "Message in editor while password is unchanged" "description": "Message in editor while password is unchanged"
+8 -1
View File
@@ -1420,7 +1420,7 @@
"cred_confirmed_successfully": { "cred_confirmed_successfully": {
"translation": "Confirmé avec succès", "translation": "Confirmé avec succès",
"defaultMessage": "Confirmed successfully", "defaultMessage": "Confirmed successfully",
"description": "Message explaining that the credential was successfully validated.", "description": "Notification message that the credential was successfully validated.",
"missing": false, "missing": false,
"obsolete": false "obsolete": false
}, },
@@ -1654,5 +1654,12 @@
"description": "Conference call label", "description": "Conference call label",
"missing": true, "missing": true,
"obsolete": false "obsolete": false
},
"password_reset_success": {
"translation": "Mot de passe réinitialisé avec succès",
"defaultMessage": "Password reset successfully",
"description": "Notification message that the password was successfully reset.",
"missing": false,
"obsolete": false
} }
} }
+8 -1
View File
@@ -1420,7 +1420,7 @@
"cred_confirmed_successfully": { "cred_confirmed_successfully": {
"translation": "성공적으로 확인됨", "translation": "성공적으로 확인됨",
"defaultMessage": "Confirmed successfully", "defaultMessage": "Confirmed successfully",
"description": "Message explaining that the credential was successfully validated.", "description": "Notification message that the credential was successfully validated.",
"missing": false, "missing": false,
"obsolete": false "obsolete": false
}, },
@@ -1654,5 +1654,12 @@
"description": "Conference call label", "description": "Conference call label",
"missing": true, "missing": true,
"obsolete": false "obsolete": false
},
"password_reset_success": {
"translation": "비밀번호 재설정 성공",
"defaultMessage": "Password reset successfully",
"description": "Notification message that the password was successfully reset.",
"missing": false,
"obsolete": false
} }
} }
+8 -1
View File
@@ -1420,7 +1420,7 @@
"cred_confirmed_successfully": { "cred_confirmed_successfully": {
"translation": "Confirmat cu succes", "translation": "Confirmat cu succes",
"defaultMessage": "Confirmed successfully", "defaultMessage": "Confirmed successfully",
"description": "Message explaining that the credential was successfully validated.", "description": "Notification message that the credential was successfully validated.",
"missing": false, "missing": false,
"obsolete": false "obsolete": false
}, },
@@ -1654,5 +1654,12 @@
"description": "Conference call label", "description": "Conference call label",
"missing": true, "missing": true,
"obsolete": false "obsolete": false
},
"password_reset_success": {
"translation": "Resetarea parolei cu succes",
"defaultMessage": "Password reset successfully",
"description": "Notification message that the password was successfully reset.",
"missing": false,
"obsolete": false
} }
} }
+8 -1
View File
@@ -1420,7 +1420,7 @@
"cred_confirmed_successfully": { "cred_confirmed_successfully": {
"translation": "Подтверждено успешно", "translation": "Подтверждено успешно",
"defaultMessage": "Confirmed successfully", "defaultMessage": "Confirmed successfully",
"description": "Message explaining that the credential was successfully validated.", "description": "Notification message that the credential was successfully validated.",
"missing": false, "missing": false,
"obsolete": false "obsolete": false
}, },
@@ -1654,5 +1654,12 @@
"description": "Conference call label", "description": "Conference call label",
"missing": true, "missing": true,
"obsolete": false "obsolete": false
},
"password_reset_success": {
"translation": "Пароль успешно изменен",
"defaultMessage": "Password reset successfully",
"description": "Notification message that the password was successfully reset.",
"missing": false,
"obsolete": false
} }
} }
+8 -1
View File
@@ -1420,7 +1420,7 @@
"cred_confirmed_successfully": { "cred_confirmed_successfully": {
"translation": "確認成功", "translation": "確認成功",
"defaultMessage": "Confirmed successfully", "defaultMessage": "Confirmed successfully",
"description": "Message explaining that the credential was successfully validated.", "description": "Notification message that the credential was successfully validated.",
"missing": false, "missing": false,
"obsolete": false "obsolete": false
}, },
@@ -1654,5 +1654,12 @@
"description": "Conference call label", "description": "Conference call label",
"missing": true, "missing": true,
"obsolete": false "obsolete": false
},
"password_reset_success": {
"translation": "密碼重置成功",
"defaultMessage": "Password reset successfully",
"description": "Notification message that the password was successfully reset.",
"missing": false,
"obsolete": false
} }
} }
+8 -1
View File
@@ -1420,7 +1420,7 @@
"cred_confirmed_successfully": { "cred_confirmed_successfully": {
"translation": "确认成功", "translation": "确认成功",
"defaultMessage": "Confirmed successfully", "defaultMessage": "Confirmed successfully",
"description": "Message explaining that the credential was successfully validated.", "description": "Notification message that the credential was successfully validated.",
"missing": false, "missing": false,
"obsolete": false "obsolete": false
}, },
@@ -1654,5 +1654,12 @@
"description": "Conference call label", "description": "Conference call label",
"missing": true, "missing": true,
"obsolete": false "obsolete": false
},
"password_reset_success": {
"translation": "密码重置成功",
"defaultMessage": "Password reset successfully",
"description": "Notification message that the password was successfully reset.",
"missing": false,
"obsolete": false
} }
} }
+2 -2
View File
@@ -210,7 +210,7 @@ function handleImageData(el, data, attr) {
return el; return el;
} }
// Additional processing of image data. // Additional processing of attached video data.
function handleVideoData(el, data, attr) { function handleVideoData(el, data, attr) {
if (!data) { if (!data) {
attr.src = 'img/broken_video.png'; attr.src = 'img/broken_video.png';
@@ -241,7 +241,7 @@ function handleVideoData(el, data, attr) {
} }
el = InlineVideo; el = InlineVideo;
} else { } else {
// Use custom element instead of <img>. // Use custom element instead of <img> or <video>.
el = UploadingImage; el = UploadingImage;
} }
+4 -2
View File
@@ -4,7 +4,7 @@ export default class HashNavigation {
// Parse hash as in http://www.example.com/path#hash as if it were // Parse hash as in http://www.example.com/path#hash as if it were
// path and arguments. // path and arguments.
static parseUrlHash(hash) { static parseUrlHash(hash) {
// Split path from args, path -> parts[0], args->path[1] // Split 'path/?args' into 'path/' and 'args', path -> parts[0], args->path[1].
const parts = hash.split('?', 2); const parts = hash.split('?', 2);
const params = {}; const params = {};
let path = []; let path = [];
@@ -32,7 +32,9 @@ export default class HashNavigation {
const args = []; const args = [];
for (const key in params) { for (const key in params) {
if (params.hasOwnProperty(key)) { if (params.hasOwnProperty(key)) {
args.push(key + '=' + encodeURIComponent(params[key])); if (params[key] !== undefined) {
args.push(key + '=' + encodeURIComponent(params[key]));
}
} }
} }
if (args.length > 0) { if (args.length > 0) {
+1 -1
View File
@@ -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.23.0-rc1"; export const PACKAGE_VERSION = "0.23.0-rc2";
+15 -7
View File
@@ -46,12 +46,21 @@ export default class AccountSettingsView extends React.Component {
val={this.state.credEdit.val} val={this.state.credEdit.val}
done={this.state.credEdit.done} done={this.state.credEdit.done}
onShowCountrySelector={this.props.onShowCountrySelector} onShowCountrySelector={this.props.onShowCountrySelector}
onSubmit={this.props.onCredAdd} onCredAdd={this.props.onCredAdd}
onCredConfirm={this.props.onCredConfirm}
onCancel={_ => this.setState({credEdit: undefined})} onCancel={_ => this.setState({credEdit: undefined})}
onError={this.props.onError} /> onError={this.props.onError} />
); );
} }
// Count how many times each method is validated.
const validated = {};
this.state.credentials.forEach(cred => {
if (cred.done) {
validated[cred.meth] = (validated[cred.meth] || 0) + 1;
}
});
const credentials = []; const credentials = [];
this.state.credentials.forEach((cred, idx) => { this.state.credentials.forEach((cred, idx) => {
if (!['email', 'tel'].includes(cred.meth)) { if (!['email', 'tel'].includes(cred.meth)) {
@@ -68,15 +77,14 @@ export default class AccountSettingsView extends React.Component {
credentials.push( credentials.push(
<div className="group quoted" key={idx}> <div className="group quoted" key={idx}>
<tt className="clickable" onClick={e => {e.preventDefault(); this.setState({credEdit: cred});}}>{val}</tt> <tt className="clickable" onClick={e => {e.preventDefault(); this.setState({credEdit: cred});}}>{val}</tt>
<span> { <span> {cred.done ? null : <i className="material-icons">pending</i>}&nbsp;
cred.done ? null : {!cred.done || validated[cred.meth] > 1 || this.props.reqCredMethod != cred.meth ?
<>
<i className="material-icons">pending</i>&nbsp;
<a href="#" onClick={e => {e.preventDefault(); this.props.onCredDelete(cred.meth, cred.val);}}> <a href="#" onClick={e => {e.preventDefault(); this.props.onCredDelete(cred.meth, cred.val);}}>
<i className="material-icons">delete</i> <i className="material-icons">delete</i>
</a> </a>
</> :
}</span> null}
</span>
</div>); </div>);
}); });
if (credentials.length > 0) { if (credentials.length > 0) {
+20 -8
View File
@@ -186,16 +186,23 @@ class MessagesView extends React.Component {
getVisibleMessageRange(holderRect) { getVisibleMessageRange(holderRect) {
let min = Number.MAX_SAFE_INTEGER, max = -1; let min = Number.MAX_SAFE_INTEGER, max = -1;
this.chatMessageRefs.forEach((ref, seq) => { let visibilityStatus = false;
this.chatMessageRefs.every((ref, seq) => {
if (ref.current) { if (ref.current) {
const { top, bottom, height } = ref.current.getBoundingClientRect(); const { top, bottom, height } = ref.current.getBoundingClientRect();
const visible = top <= holderRect.top ? holderRect.top - top <= height : bottom - holderRect.bottom <= height; const visible = top <= holderRect.top ? holderRect.top - top <= height : bottom - holderRect.bottom <= height;
if (visible) { if (visible) {
visibilityStatus = true;
min = Math.min(min, seq); min = Math.min(min, seq);
max = Math.max(max, seq); max = Math.max(max, seq);
} else if (visibilityStatus) {
// The remaining elements are no longer visible, no need to iterate them.
return false;
} }
} }
return true;
}); });
return max >= min ? {min: min, max: max} : {min: 0, max: 0}; return max >= min ? {min: min, max: max} : {min: 0, max: 0};
} }
@@ -274,12 +281,12 @@ class MessagesView extends React.Component {
} }
if (topic) { if (topic) {
if ((this.state.topic != prevState.topic) || !prevProps.ready) { if ((this.state.topic != prevState.topic) || (this.props.myUserId && !prevProps.myUserId)) {
// Don't immediately subscribe to a new p2p topic, wait for the first message. // Don't immediately subscribe to a new p2p topic, wait for the first message.
const newTopic = (this.props.newTopicParams && this.props.newTopicParams._topicName == this.props.topic); const newTopic = (this.props.newTopicParams && this.props.newTopicParams._topicName == this.props.topic);
if (topic.isP2PType() && newTopic && !IMMEDIATE_P2P_SUBSCRIPTION) { if (topic.isP2PType() && newTopic && !IMMEDIATE_P2P_SUBSCRIPTION) {
topic.getMeta(topic.startMetaQuery().withDesc().build()); topic.getMeta(topic.startMetaQuery().withDesc().build());
} else { } else if (this.props.myUserId) {
this.subscribe(topic); this.subscribe(topic);
} }
} else if (topic.isSubscribed() && this.state.isReader && !prevState.isReader) { } else if (topic.isSubscribed() && this.state.isReader && !prevState.isReader) {
@@ -321,7 +328,8 @@ class MessagesView extends React.Component {
showGoToLastButton: false, showGoToLastButton: false,
dragging: false, dragging: false,
pins: [], pins: [],
selectedPin: 0 selectedPin: 0,
subsVersion: 0
}; };
} else if (nextProps.topic != prevState.topic) { } else if (nextProps.topic != prevState.topic) {
const topic = nextProps.tinode.getTopic(nextProps.topic); const topic = nextProps.tinode.getTopic(nextProps.topic);
@@ -478,7 +486,6 @@ class MessagesView extends React.Component {
// Is this a new topic? // Is this a new topic?
const newTopic = (this.props.newTopicParams && this.props.newTopicParams._topicName == this.props.topic); const newTopic = (this.props.newTopicParams && this.props.newTopicParams._topicName == this.props.topic);
// Don't request the tags. They are useless unless the user // Don't request the tags. They are useless unless the user
// is the owner and is editing the topic. // is the owner and is editing the topic.
let getQuery = topic.startMetaQuery().withLaterDesc().withLaterSub().withAux(); let getQuery = topic.startMetaQuery().withLaterDesc().withLaterSub().withAux();
@@ -503,6 +510,9 @@ class MessagesView extends React.Component {
if (this.state.topic != ctrl.topic) { if (this.state.topic != ctrl.topic) {
this.setState({topic: ctrl.topic}); this.setState({topic: ctrl.topic});
} }
if (this.state.deleted) {
this.setState({deleted: false});
}
this.props.onNewTopicCreated(this.props.topic, ctrl.topic); this.props.onNewTopicCreated(this.props.topic, ctrl.topic);
// If there are unsent messages (except hard-failed and video call messages), // If there are unsent messages (except hard-failed and video call messages),
// try sending them now. Hard-failed and unsent video call messages will be dropped. // try sending them now. Hard-failed and unsent video call messages will be dropped.
@@ -576,13 +586,14 @@ class MessagesView extends React.Component {
showGoToLastButton: (pos > SHOW_GO_TO_LAST_DIST) && (pos < this.state.scrollPosition), showGoToLastButton: (pos > SHOW_GO_TO_LAST_DIST) && (pos < this.state.scrollPosition),
}); });
if (this.state.fetchingMessages) { if (this.state.fetchingMessages || this.processingScrollEvent) {
return; return;
} }
if (event.target.scrollTop <= FETCH_PAGE_TRIGGER) { if (event.target.scrollTop <= FETCH_PAGE_TRIGGER) {
const topic = this.props.tinode.getTopic(this.state.topic); const topic = this.props.tinode.getTopic(this.state.topic);
if (topic && topic.isSubscribed()) { if (topic && topic.isSubscribed()) {
this.processingScrollEvent = true;
const {min, max} = this.getVisibleMessageRange(event.target.getBoundingClientRect()); const {min, max} = this.getVisibleMessageRange(event.target.getBoundingClientRect());
const gaps = topic.msgHasMoreMessages(min, max, false); const gaps = topic.msgHasMoreMessages(min, max, false);
if (gaps.length > 0) { if (gaps.length > 0) {
@@ -594,6 +605,7 @@ class MessagesView extends React.Component {
} }
} }
} }
this.processingScrollEvent = false;
} }
/* Mount drag and drop events */ /* Mount drag and drop events */
@@ -705,12 +717,12 @@ class MessagesView extends React.Component {
if (this.state.topic) { if (this.state.topic) {
const subs = []; const subs = [];
const topic = this.props.tinode.getTopic(this.state.topic); const topic = this.props.tinode.getTopic(this.state.topic);
topic.subscribers((sub) => { topic.subscribers(sub => {
if (sub.online && sub.user != this.props.myUserId) { if (sub.online && sub.user != this.props.myUserId) {
subs.push(sub); subs.push(sub);
} }
}); });
const newState = {onlineSubs: subs}; const newState = {onlineSubs: subs, subsVersion: this.state.subsVersion + 1};
const peer = topic.p2pPeerDesc(); const peer = topic.p2pPeerDesc();
if (peer) { if (peer) {
Object.assign(newState, { Object.assign(newState, {
+21 -5
View File
@@ -48,7 +48,23 @@ class PasswordResetView extends React.PureComponent {
componentDidMount() { componentDidMount() {
const parsed = HashNavigation.parseUrlHash(window.location.hash); const parsed = HashNavigation.parseUrlHash(window.location.hash);
this.setState({token: parsed.params.token, scheme: parsed.params.scheme}); const newState = {
token: parsed.params.token,
scheme: parsed.params.scheme,
code: parsed.params.code || ''
}
this.savedCred = parsed.params.cred;
if (this.props.reqCredMethod && parsed.params.cred) {
newState[this.props.reqCredMethod] = this.savedCred;
}
this.setState(newState);
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.reqCredMethod != this.props.reqCredMethod && this.props.reqCredMethod) {
this.setState({[this.props.reqCredMethod]: this.savedCred || ''});
}
} }
handleSubmit(e) { handleSubmit(e) {
@@ -57,12 +73,12 @@ class PasswordResetView extends React.PureComponent {
// Reset using token. // Reset using token.
this.props.onReset(this.state.password.trim(), { this.props.onReset(this.state.password.trim(), {
scheme: 'token', scheme: 'token',
token: this.state.token secret: this.state.token
}); });
} else if (this.state.code && this.props.reqCredMethod) { } else if (this.state.code && this.props.reqCredMethod) {
// Reset using security code. // Reset using security code.
// The secret is structured as <code>:<cred_method>:<cred_value>, "123456:email:alice@example.com". // The secret is structured as <code>:<cred_method>:<cred_value>, "123456:email:alice@example.com".
const cred = this.state.email.trim() || this.state.tel.trim(); const cred = this.props.reqCredMethod == 'email' ? this.state.email.trim() : this.state.tel.trim();
this.props.onReset(this.state.password.trim(), { this.props.onReset(this.state.password.trim(), {
scheme: 'code', scheme: 'code',
secret: btoa(`${this.state.code}:${this.props.reqCredMethod}:${cred}`) secret: btoa(`${this.state.code}:${this.props.reqCredMethod}:${cred}`)
@@ -102,7 +118,7 @@ class PasswordResetView extends React.PureComponent {
} }
render() { render() {
const showCredentialInput = !(this.state.token && this.state.scheme); const showCredentialInput = !((this.state.token || (this.state.code && this.props.reqCredMethod)) && this.state.scheme);
const showPasswordInput = !showCredentialInput || this.state.haveCode || this.state.sent; const showPasswordInput = !showCredentialInput || this.state.haveCode || this.state.sent;
const passwordInput = (<> const passwordInput = (<>
@@ -193,7 +209,7 @@ class PasswordResetView extends React.PureComponent {
{this.state.haveCode || this.state.sent ? codeInput : null} {this.state.haveCode || this.state.sent ? codeInput : null}
{showPasswordInput ? passwordInput : null} {showPasswordInput ? passwordInput : null}
<div className="dialog-buttons"> <div className="dialog-buttons">
{this.state.haveCode || this.state.sent ? null : {this.state.haveCode || this.state.sent || this.state.token || this.state.code ? null :
<a href="#" onClick={this.handleShowCodeField} style={{marginRight: 'auto'}}> <a href="#" onClick={this.handleShowCodeField} style={{marginRight: 'auto'}}>
<FormattedMessage id="password_i_have_code" defaultMessage="I have code" <FormattedMessage id="password_i_have_code" defaultMessage="I have code"
description="Call to open field to enter password reset code" /> description="Call to open field to enter password reset code" />
+1
View File
@@ -181,6 +181,7 @@ class SidepanelView extends React.PureComponent {
tinode={this.props.tinode} tinode={this.props.tinode}
myUserId={this.props.myUserId} myUserId={this.props.myUserId}
trustedBadges={this.props.trustedBadges} trustedBadges={this.props.trustedBadges}
reqCredMethod={this.props.reqCredMethod}
onShowCountrySelector={this.props.onShowCountrySelector} onShowCountrySelector={this.props.onShowCountrySelector}
onNavigate={this.props.onNavigate} onNavigate={this.props.onNavigate}
onCredAdd={this.props.onCredAdd} onCredAdd={this.props.onCredAdd}
+16 -6
View File
@@ -83,7 +83,12 @@ const messages = defineMessages({
cred_confirmed_successfully: { cred_confirmed_successfully: {
id: 'cred_confirmed_successfully', id: 'cred_confirmed_successfully',
defaultMessage: 'Confirmed successfully', defaultMessage: 'Confirmed successfully',
description: 'Message explaining that the credential was successfully validated.' description: 'Notification message that the credential was successfully validated.'
},
password_reset_success: {
id: 'password_reset_success',
defaultMessage: 'Password reset successfully',
description: 'Notification message that the password was successfully reset.'
} }
}); });
@@ -763,6 +768,7 @@ class TinodeWeb extends React.Component {
parsed.path[0] = 'cred'; parsed.path[0] = 'cred';
parsed.params['method'] = params.cred[0]; parsed.params['method'] = params.cred[0];
parsed.params['token'] = params.token; parsed.params['token'] = params.token;
parsed.params['code'] = params.code;
HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsed.path, parsed.params)); HashNavigation.navigateTo(HashNavigation.composeUrlHash(parsed.path, parsed.params));
} }
@@ -848,7 +854,7 @@ class TinodeWeb extends React.Component {
} }
} else if (what == 'read') { } else if (what == 'read') {
this.resetContactList(); this.resetContactList();
} else if (what == 'msg') { } else if (what == 'msg' && cont) {
// Check if the topic is archived. If so, don't play a sound. // Check if the topic is archived. If so, don't play a sound.
const topic = this.tinode.getTopic(cont.topic); const topic = this.tinode.getTopic(cont.topic);
const archived = topic && topic.isArchived(); const archived = topic && topic.isArchived();
@@ -887,7 +893,7 @@ class TinodeWeb extends React.Component {
} 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);
} }
} }
@@ -1019,6 +1025,7 @@ class TinodeWeb extends React.Component {
} else { } else {
// Currently selected contact deleted // Currently selected contact deleted
this.setState({ this.setState({
topicSelected: null,
errorText: '', errorText: '',
errorLevel: null, errorLevel: null,
mobilePanel: 'sidepanel', mobilePanel: 'sidepanel',
@@ -1287,7 +1294,7 @@ class TinodeWeb extends React.Component {
} }
handleCredConfirm(method, response) { handleCredConfirm(method, response) {
TinodeWeb.navigateToCredentialsView({cred: [method]}); TinodeWeb.navigateToCredentialsView({cred: [method], code: response});
} }
// User clicked Cancel button in Setting or Sign Up panel. // User clicked Cancel button in Setting or Sign Up panel.
@@ -1709,6 +1716,7 @@ class TinodeWeb extends React.Component {
if (this.tinode.isAuthenticated()) { if (this.tinode.isAuthenticated()) {
// Adding new email or phone number in account setting. // Adding new email or phone number in account setting.
this.tinode.getMeTopic().setMeta({cred: {meth: cred, resp: code}}) this.tinode.getMeTopic().setMeta({cred: {meth: cred, resp: code}})
.then(_ => HashNavigation.navigateTo(HashNavigation.setUrlSidePanel(window.location.hash, 'contacts')))
.catch(err => this.handleError(err.message, 'err')); .catch(err => this.handleError(err.message, 'err'));
} else { } else {
// Credential validation on signup. // Credential validation on signup.
@@ -1734,9 +1742,11 @@ class TinodeWeb extends React.Component {
} else { } else {
this.tinode.connect() this.tinode.connect()
.then(_ => this.tinode.updateAccountBasic(null, null, newPassword, {scheme: tempAuth.scheme, secret: secret})) .then(_ => this.tinode.updateAccountBasic(null, null, newPassword, {scheme: tempAuth.scheme, secret: secret}))
.then(_ => HashNavigation.navigateTo('')) .then(_ => {
this.handleError(this.props.intl.formatMessage(messages.password_reset_success), 'info');
HashNavigation.navigateTo('');
})
.catch(err => { .catch(err => {
// Socket error
this.handleError(err.message, 'err'); this.handleError(err.message, 'err');
}); });
} }
+1 -1
View File
@@ -100,7 +100,7 @@ class ValidationView extends React.PureComponent {
(numbers_only) => <input type="text" id="enter-confirmation-code" (numbers_only) => <input type="text" id="enter-confirmation-code"
placeholder={numbers_only} placeholder={numbers_only}
value={this.state.code} onChange={this.handleCodeChange} value={this.state.code} onChange={this.handleCodeChange}
onKeyPress={this.handleKeyPress} required /> onKeyDown={this.handleKeyPress} required />
}</FormattedMessage> }</FormattedMessage>
</div> </div>
<div className="dialog-buttons"> <div className="dialog-buttons">
+18 -17
View File
@@ -58,8 +58,11 @@ class CredentialEdit extends React.PureComponent {
handleSubmit(e) { handleSubmit(e) {
e.preventDefault(); e.preventDefault();
const value = this.props.method == 'email' ? this.state.email : this.state.tel; const value = this.props.method == 'email' ? this.state.email : this.state.tel;
if (value) { if (this.state.code) {
this.props.onSubmit(this.props.method, value); this.props.onError(null);
this.props.onCredConfirm(this.props.method, this.state.code);
} else if (value) {
this.props.onCredAdd(this.props.method, value);
this.setState({sent: true}); this.setState({sent: true});
const msg = this.props.method == 'email' ? messages.password_reset_email_sent : messages.password_reset_sms_sent; const msg = this.props.method == 'email' ? messages.password_reset_email_sent : messages.password_reset_sms_sent;
this.props.onError(this.props.intl.formatMessage(msg), 'info'); this.props.onError(this.props.intl.formatMessage(msg), 'info');
@@ -68,8 +71,8 @@ class CredentialEdit extends React.PureComponent {
render() { render() {
const readyForCode = this.state.sent || !this.props.done; const readyForCode = this.state.sent || !this.props.done;
const currentValue = CredentialEdit.formatPhoneNumber(readyForCode ? const rawValue = readyForCode ? (this.state.tel || this.state.email) : this.props.val;
(this.state.tel || this.state.email) : this.props.val); const formattedValue = this.props.method == 'tel' ? CredentialEdit.formatPhoneNumber(rawValue) : rawValue;
const changeEmail = ( const changeEmail = (
<><div className="panel-form-row"> <><div className="panel-form-row">
@@ -140,21 +143,19 @@ class CredentialEdit extends React.PureComponent {
); );
return (<form className="panel-form-column" onSubmit={this.handleSubmit}> return (<form className="panel-form-column" onSubmit={this.handleSubmit}>
{readyForCode ? null : {readyForCode ?
this.props.method == 'email' ? (this.props.method == 'email' ? newEmailLabel : this.props.method == 'tel' ? newPhoneLabel : null)
changeEmail :
: this.props.method == 'tel' ? (this.props.method == 'email' ? changeEmail : this.props.method == 'tel' ? changePhone : null)
changePhone }
: null}
<div className="panel-form-row"> <div className="panel-form-row">
<tt className="quoted">{currentValue}</tt> <tt className="quoted">{formattedValue}</tt>
</div> </div>
{readyForCode ? null : {readyForCode ?
this.props.method == 'email' ? null
newEmailInput :
: this.props.method == 'tel' ? (this.props.method == 'email' ? newEmailInput : this.props.method == 'tel' ? newPhoneInput : null)
newPhoneInput }
: null}
{readyForCode ? codeInput : null} {readyForCode ? codeInput : null}
<div className="dialog-buttons"> <div className="dialog-buttons">
<button className="secondary" onClick={this.props.onCancel}> <button className="secondary" onClick={this.props.onCancel}>
+1 -1
View File
@@ -39,7 +39,7 @@ export default class ErrorPanel extends React.PureComponent {
{this.props.text} {this.props.text}
{this.props.action ? {this.props.action ?
<> <>
&#32;<a href="#" &nbsp;&#32;<a href="#"
style={{ whiteSpace: 'nowrap' }} style={{ whiteSpace: 'nowrap' }}
onClick={(e) => {e.preventDefault(); this.props.action();}}> onClick={(e) => {e.preventDefault(); this.props.action();}}>
{this.props.actionText} {this.props.actionText}
+3 -3
View File
@@ -8,7 +8,7 @@ export default class VisiblePassword extends React.PureComponent {
this.inputRef = React.createRef(); this.inputRef = React.createRef();
this.state = { this.state = {
value: this.props.value, value: this.props.value || '',
visible: false visible: false
}; };
@@ -39,7 +39,7 @@ export default class VisiblePassword extends React.PureComponent {
handleKeyDown(e) { handleKeyDown(e) {
if (e.keyCode == 27) { if (e.keyCode == 27) {
// Escape pressed // Escape pressed
this.setState({value: this.props.value, visible: false}); this.setState({value: this.props.value || '', visible: false});
if (this.props.onFinished) { if (this.props.onFinished) {
this.props.onFinished(); this.props.onFinished();
} }
@@ -68,7 +68,7 @@ export default class VisiblePassword extends React.PureComponent {
return ( return (
<div tabIndex="-1" className="group-focus" onBlur={this.handleEditingFinished}> <div tabIndex="-1" className="group-focus" 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}
placeholder={this.props.placeholder} placeholder={this.props.placeholder}
required={this.props.required ? 'required' : ''} required={this.props.required ? 'required' : ''}
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"290.prod.js","mappings":"2MASA,MAAMA,UAA6BC,IAAAA,cACjCC,YAAYC,GACVC,MAAMD,GAENE,KAAKC,UAAY,GACjB,MAAM,kBAAEC,GAAsBJ,EAAMK,KACpCC,EAAAA,SAAuBC,IACPA,EAAGC,KAAKC,MAAM,KACtBC,SAAQC,IACZT,KAAKC,UAAUS,KAAK,CAClBJ,KAAMG,EAAKE,OACXC,KAAMP,EAAGO,KACTC,MAAMC,EAAAA,EAAAA,IAAUT,EAAGO,MACnBG,KAAMb,EAAkBG,EAAGO,KAAM,CAACI,KAAM,YACxC,GACF,IAGJhB,KAAKC,UAAUgB,MAAK,CAACC,EAAGC,IAAMD,EAAEH,KAAKK,cAAcD,EAAEJ,OACvD,CAEAM,oBACMrB,KAAKsB,aACPtB,KAAKsB,YAAYC,eAAe,CAACC,MAAO,SAAUC,OAAQ,WAE9D,CAEAC,SACE,MAAMzB,EAAY,GACZ0B,EAAW3B,KAAKF,MAAM6B,UAAY,KAYxC,OAXA3B,KAAKC,UAAUO,SAAQ,CAACoB,EAAGC,KACzB,MAAMC,EAASF,EAAEhB,MAAQe,EAAW,YAAc,GAClD1B,EAAUS,KAAKd,IAAAA,cAAA,MAAImC,UAAWD,EAAOE,IAAKH,EACxCI,IAAKA,IAAYL,EAAEhB,MAAQe,IAAW3B,KAAKsB,YAAcW,EAAG,EAC5DC,QAASC,GAAKnC,KAAKF,MAAMsC,SAASR,EAAEhB,KAAMgB,EAAEtB,OAC5CV,IAAAA,cAAA,QAAMmC,UAAU,gBAAgBH,EAAEf,MAClCjB,IAAAA,cAAA,QAAMmC,UAAU,WAAU,IAAOH,EAAEb,MACnCnB,IAAAA,cAAA,QAAMmC,UAAU,aAAY,KAAQH,EAAEtB,OAClC,IAINV,IAAAA,cAAA,OAAKmC,UAAU,mBAAmBD,MAAO,CAACO,OAAQ,UAChDzC,IAAAA,cAAA,MAAImC,UAAU,0BACX9B,GAIT,EAGF,SAAeqC,EAAAA,EAAAA,YAAW3C,E","sources":["webpack://tinode-webapp/./src/widgets/phone-country-selector.jsx"],"sourcesContent":["// Selector for country dialing code.\n\nimport React from 'react';\nimport { injectIntl } from 'react-intl';\n\nimport * as dcodes from '../dcodes.json';\n\nimport { flagEmoji } from '../lib/strformat';\n\nclass PhoneCountrySelector extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.countries = [];\n const { formatDisplayName } = props.intl;\n dcodes.default.forEach(dc => {\n const parts = dc.dial.split(',');\n parts.forEach(part => {\n this.countries.push({\n dial: part.trim(),\n code: dc.code,\n flag: flagEmoji(dc.code),\n name: formatDisplayName(dc.code, {type: 'region'})\n });\n });\n });\n\n this.countries.sort((a, b) => a.name.localeCompare(b.name));\n }\n\n componentDidMount() {\n if (this.selectedRef) {\n this.selectedRef.scrollIntoView({block: 'center', inline: 'nearest'});\n }\n }\n\n render() {\n const countries = [];\n const selected = this.props.selected || 'US';\n this.countries.forEach((c, idx) => {\n const style = (c.code == selected ? 'selected ' : '');\n countries.push(<li className={style} key={idx}\n ref={ref => {if (c.code == selected) {this.selectedRef = ref}}}\n onClick={_ => this.props.onSubmit(c.code, c.dial)}>\n <span className=\"country-flag\">{c.flag}</span>\n <span className=\"country\">&nbsp;{c.name}</span>\n <span className=\"dial-code\">&nbsp;+{c.dial}</span>\n </li>);\n });\n\n return (\n <div className=\"scrollable-panel\" style={{height: '30rem'}}>\n <ul className=\"phone-country-selector\">\n {countries}\n </ul>\n </div>\n );\n }\n}\n\nexport default injectIntl(PhoneCountrySelector);\n"],"names":["PhoneCountrySelector","React","constructor","props","super","this","countries","formatDisplayName","intl","dcodes","dc","dial","split","forEach","part","push","trim","code","flag","flagEmoji","name","type","sort","a","b","localeCompare","componentDidMount","selectedRef","scrollIntoView","block","inline","render","selected","c","idx","style","className","key","ref","onClick","_","onSubmit","height","injectIntl"],"sourceRoot":""} {"version":3,"file":"290.prod.js","mappings":"2MASA,MAAMA,UAA6BC,IAAAA,cACjCC,YAAYC,GACVC,MAAMD,GAENE,KAAKC,UAAY,GACjB,MAAM,kBAAEC,GAAsBJ,EAAMK,KACpCC,EAAeC,SAAQC,IACPA,EAAGC,KAAKC,MAAM,KACtBH,SAAQI,IACZT,KAAKC,UAAUS,KAAK,CAClBH,KAAME,EAAKE,OACXC,KAAMN,EAAGM,KACTC,MAAMC,EAAAA,EAAAA,IAAUR,EAAGM,MACnBG,KAAMb,EAAkBI,EAAGM,KAAM,CAACI,KAAM,YACxC,GACF,IAGJhB,KAAKC,UAAUgB,MAAK,CAACC,EAAGC,IAAMD,EAAEH,KAAKK,cAAcD,EAAEJ,OACvD,CAEAM,oBACMrB,KAAKsB,aACPtB,KAAKsB,YAAYC,eAAe,CAACC,MAAO,SAAUC,OAAQ,WAE9D,CAEAC,SACE,MAAMzB,EAAY,GACZ0B,EAAW3B,KAAKF,MAAM6B,UAAY,KAYxC,OAXA3B,KAAKC,UAAUI,SAAQ,CAACuB,EAAGC,KACzB,MAAMC,EAASF,EAAEhB,MAAQe,EAAW,YAAc,GAClD1B,EAAUS,KAAKd,IAAAA,cAAA,MAAImC,UAAWD,EAAOE,IAAKH,EACxCI,IAAKA,IAAYL,EAAEhB,MAAQe,IAAW3B,KAAKsB,YAAcW,EAAG,EAC5DC,QAASC,GAAKnC,KAAKF,MAAMsC,SAASR,EAAEhB,KAAMgB,EAAErB,OAC5CX,IAAAA,cAAA,QAAMmC,UAAU,gBAAgBH,EAAEf,MAClCjB,IAAAA,cAAA,QAAMmC,UAAU,WAAU,IAAOH,EAAEb,MACnCnB,IAAAA,cAAA,QAAMmC,UAAU,aAAY,KAAQH,EAAErB,OAClC,IAINX,IAAAA,cAAA,OAAKmC,UAAU,mBAAmBD,MAAO,CAACO,OAAQ,UAChDzC,IAAAA,cAAA,MAAImC,UAAU,0BACX9B,GAIT,EAGF,SAAeqC,EAAAA,EAAAA,YAAW3C,E","sources":["webpack://tinode-webapp/./src/widgets/phone-country-selector.jsx"],"sourcesContent":["// Selector for country dialing code.\n\nimport React from 'react';\nimport { injectIntl } from 'react-intl';\n\nimport * as dcodes from '../dcodes.json';\n\nimport { flagEmoji } from '../lib/strformat';\n\nclass PhoneCountrySelector extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.countries = [];\n const { formatDisplayName } = props.intl;\n dcodes.default.forEach(dc => {\n const parts = dc.dial.split(',');\n parts.forEach(part => {\n this.countries.push({\n dial: part.trim(),\n code: dc.code,\n flag: flagEmoji(dc.code),\n name: formatDisplayName(dc.code, {type: 'region'})\n });\n });\n });\n\n this.countries.sort((a, b) => a.name.localeCompare(b.name));\n }\n\n componentDidMount() {\n if (this.selectedRef) {\n this.selectedRef.scrollIntoView({block: 'center', inline: 'nearest'});\n }\n }\n\n render() {\n const countries = [];\n const selected = this.props.selected || 'US';\n this.countries.forEach((c, idx) => {\n const style = (c.code == selected ? 'selected ' : '');\n countries.push(<li className={style} key={idx}\n ref={ref => {if (c.code == selected) {this.selectedRef = ref}}}\n onClick={_ => this.props.onSubmit(c.code, c.dial)}>\n <span className=\"country-flag\">{c.flag}</span>\n <span className=\"country\">&nbsp;{c.name}</span>\n <span className=\"dial-code\">&nbsp;+{c.dial}</span>\n </li>);\n });\n\n return (\n <div className=\"scrollable-panel\" style={{height: '30rem'}}>\n <ul className=\"phone-country-selector\">\n {countries}\n </ul>\n </div>\n );\n }\n}\n\nexport default injectIntl(PhoneCountrySelector);\n"],"names":["PhoneCountrySelector","React","constructor","props","super","this","countries","formatDisplayName","intl","dcodes","forEach","dc","dial","split","part","push","trim","code","flag","flagEmoji","name","type","sort","a","b","localeCompare","componentDidMount","selectedRef","scrollIntoView","block","inline","render","selected","c","idx","style","className","key","ref","onClick","_","onSubmit","height","injectIntl"],"sourceRoot":""}
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+24876 -23054
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+20 -9
View File
@@ -61,13 +61,20 @@ class AccountSettingsView extends (react__WEBPACK_IMPORTED_MODULE_0___default().
val: this.state.credEdit.val, val: this.state.credEdit.val,
done: this.state.credEdit.done, done: this.state.credEdit.done,
onShowCountrySelector: this.props.onShowCountrySelector, onShowCountrySelector: this.props.onShowCountrySelector,
onSubmit: this.props.onCredAdd, onCredAdd: this.props.onCredAdd,
onCredConfirm: this.props.onCredConfirm,
onCancel: _ => this.setState({ onCancel: _ => this.setState({
credEdit: undefined credEdit: undefined
}), }),
onError: this.props.onError onError: this.props.onError
}); });
} }
const validated = {};
this.state.credentials.forEach(cred => {
if (cred.done) {
validated[cred.meth] = (validated[cred.meth] || 0) + 1;
}
});
const credentials = []; const credentials = [];
this.state.credentials.forEach((cred, idx) => { this.state.credentials.forEach((cred, idx) => {
if (!['email', 'tel'].includes(cred.meth)) { if (!['email', 'tel'].includes(cred.meth)) {
@@ -89,9 +96,9 @@ class AccountSettingsView extends (react__WEBPACK_IMPORTED_MODULE_0___default().
credEdit: cred credEdit: cred
}); });
} }
}, val), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, " ", cred.done ? null : react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", { }, val), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, " ", cred.done ? null : react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: "material-icons" className: "material-icons"
}, "pending"), "\xA0", react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", { }, "pending"), "\xA0", !cred.done || validated[cred.meth] > 1 || this.props.reqCredMethod != cred.meth ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
href: "#", href: "#",
onClick: e => { onClick: e => {
e.preventDefault(); e.preventDefault();
@@ -99,7 +106,7 @@ class AccountSettingsView extends (react__WEBPACK_IMPORTED_MODULE_0___default().
} }
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", { }, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
className: "material-icons" className: "material-icons"
}, "delete")))))); }, "delete")) : null)));
}); });
if (credentials.length > 0) { if (credentials.length > 0) {
credentials.unshift(react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { credentials.unshift(react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", {
@@ -313,8 +320,11 @@ class CredentialEdit extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureC
handleSubmit(e) { handleSubmit(e) {
e.preventDefault(); e.preventDefault();
const value = this.props.method == 'email' ? this.state.email : this.state.tel; const value = this.props.method == 'email' ? this.state.email : this.state.tel;
if (value) { if (this.state.code) {
this.props.onSubmit(this.props.method, value); this.props.onError(null);
this.props.onCredConfirm(this.props.method, this.state.code);
} else if (value) {
this.props.onCredAdd(this.props.method, value);
this.setState({ this.setState({
sent: true sent: true
}); });
@@ -324,7 +334,8 @@ class CredentialEdit extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureC
} }
render() { render() {
const readyForCode = this.state.sent || !this.props.done; const readyForCode = this.state.sent || !this.props.done;
const currentValue = CredentialEdit.formatPhoneNumber(readyForCode ? this.state.tel || this.state.email : this.props.val); const rawValue = readyForCode ? this.state.tel || this.state.email : this.props.val;
const formattedValue = this.props.method == 'tel' ? CredentialEdit.formatPhoneNumber(rawValue) : rawValue;
const changeEmail = react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { const changeEmail = react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "panel-form-row" className: "panel-form-row"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { }, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", {
@@ -441,11 +452,11 @@ class CredentialEdit extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureC
return react__WEBPACK_IMPORTED_MODULE_0___default().createElement("form", { return react__WEBPACK_IMPORTED_MODULE_0___default().createElement("form", {
className: "panel-form-column", className: "panel-form-column",
onSubmit: this.handleSubmit onSubmit: this.handleSubmit
}, readyForCode ? null : this.props.method == 'email' ? changeEmail : this.props.method == 'tel' ? changePhone : null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { }, readyForCode ? this.props.method == 'email' ? newEmailLabel : this.props.method == 'tel' ? newPhoneLabel : null : this.props.method == 'email' ? changeEmail : this.props.method == 'tel' ? changePhone : null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "panel-form-row" className: "panel-form-row"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("tt", { }, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("tt", {
className: "quoted" className: "quoted"
}, currentValue)), readyForCode ? null : this.props.method == 'email' ? newEmailInput : this.props.method == 'tel' ? newPhoneInput : null, readyForCode ? codeInput : null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { }, formattedValue)), readyForCode ? null : this.props.method == 'email' ? newEmailInput : this.props.method == 'tel' ? newPhoneInput : null, readyForCode ? codeInput : null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "dialog-buttons" className: "dialog-buttons"
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { }, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", {
className: "secondary", className: "secondary",
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+20 -7
View File
@@ -61,20 +61,33 @@ class PasswordResetView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pu
} }
componentDidMount() { componentDidMount() {
const parsed = _lib_navigation_js__WEBPACK_IMPORTED_MODULE_4__["default"].parseUrlHash(window.location.hash); const parsed = _lib_navigation_js__WEBPACK_IMPORTED_MODULE_4__["default"].parseUrlHash(window.location.hash);
this.setState({ const newState = {
token: parsed.params.token, token: parsed.params.token,
scheme: parsed.params.scheme scheme: parsed.params.scheme,
}); code: parsed.params.code || ''
};
this.savedCred = parsed.params.cred;
if (this.props.reqCredMethod && parsed.params.cred) {
newState[this.props.reqCredMethod] = this.savedCred;
}
this.setState(newState);
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.reqCredMethod != this.props.reqCredMethod && this.props.reqCredMethod) {
this.setState({
[this.props.reqCredMethod]: this.savedCred || ''
});
}
} }
handleSubmit(e) { handleSubmit(e) {
e.preventDefault(); e.preventDefault();
if (this.state.token) { if (this.state.token) {
this.props.onReset(this.state.password.trim(), { this.props.onReset(this.state.password.trim(), {
scheme: 'token', scheme: 'token',
token: this.state.token secret: this.state.token
}); });
} else if (this.state.code && this.props.reqCredMethod) { } else if (this.state.code && this.props.reqCredMethod) {
const cred = this.state.email.trim() || this.state.tel.trim(); const cred = this.props.reqCredMethod == 'email' ? this.state.email.trim() : this.state.tel.trim();
this.props.onReset(this.state.password.trim(), { this.props.onReset(this.state.password.trim(), {
scheme: 'code', scheme: 'code',
secret: btoa(`${this.state.code}:${this.props.reqCredMethod}:${cred}`) secret: btoa(`${this.state.code}:${this.props.reqCredMethod}:${cred}`)
@@ -119,7 +132,7 @@ class PasswordResetView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pu
}); });
} }
render() { render() {
const showCredentialInput = !(this.state.token && this.state.scheme); const showCredentialInput = !((this.state.token || this.state.code && this.props.reqCredMethod) && this.state.scheme);
const showPasswordInput = !showCredentialInput || this.state.haveCode || this.state.sent; const showPasswordInput = !showCredentialInput || this.state.haveCode || this.state.sent;
const passwordInput = react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { const passwordInput = react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", {
className: "small gray", className: "small gray",
@@ -222,7 +235,7 @@ class PasswordResetView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pu
onSubmit: this.handleSubmit onSubmit: this.handleSubmit
}, !this.state.sent && showCredentialInput ? credentialInput : null, this.state.haveCode || this.state.sent ? codeInput : null, showPasswordInput ? passwordInput : null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { }, !this.state.sent && showCredentialInput ? credentialInput : null, this.state.haveCode || this.state.sent ? codeInput : null, showPasswordInput ? passwordInput : null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
className: "dialog-buttons" className: "dialog-buttons"
}, this.state.haveCode || this.state.sent ? null : react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", { }, this.state.haveCode || this.state.sent || this.state.token || this.state.code ? null : react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
href: "#", href: "#",
onClick: this.handleShowCodeField, onClick: this.handleShowCodeField,
style: { style: {
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
{"version":3,"file":"src_widgets_phone-country-selector_jsx.dev.js","mappings":";;;;;;;;;;;;;;;;;;;AAE0B;AACc;AAEC;AAEI;AAE7C,MAAMI,oBAAoB,SAASJ,4DAAmB,CAAC;EACrDM,WAAWA,CAACC,KAAK,EAAE;IACjB,KAAK,CAACA,KAAK,CAAC;IAEZ,IAAI,CAACC,SAAS,GAAG,EAAE;IACnB,MAAM;MAAEC;IAAkB,CAAC,GAAGF,KAAK,CAACG,IAAI;IACxCR,iDAAsB,CAACW,EAAE,IAAI;MAC3B,MAAMC,KAAK,GAAGD,EAAE,CAACE,IAAI,CAACC,KAAK,CAAC,GAAG,CAAC;MAChCF,KAAK,CAACF,OAAO,CAACK,IAAI,IAAI;QACpB,IAAI,CAACT,SAAS,CAACU,IAAI,CAAC;UAClBH,IAAI,EAAEE,IAAI,CAACE,IAAI,EAAE;UACjBC,IAAI,EAAEP,EAAE,CAACO,IAAI;UACbC,IAAI,EAAElB,yDAAS,CAACU,EAAE,CAACO,IAAI,CAAC;UACxBE,IAAI,EAAEb,iBAAiB,CAACI,EAAE,CAACO,IAAI,EAAE;YAACG,IAAI,EAAE;UAAQ,CAAC;QACnD,CAAC,CAAC;MACJ,CAAC,CAAC;IACJ,CAAC,CAAC;IAEF,IAAI,CAACf,SAAS,CAACgB,IAAI,CAAC,CAACC,CAAC,EAAEC,CAAC,KAAKD,CAAC,CAACH,IAAI,CAACK,aAAa,CAACD,CAAC,CAACJ,IAAI,CAAC,CAAC;EAC7D;EAEAM,iBAAiBA,CAAA,EAAG;IAClB,IAAI,IAAI,CAACC,WAAW,EAAE;MACpB,IAAI,CAACA,WAAW,CAACC,cAAc,CAAC;QAACC,KAAK,EAAE,QAAQ;QAAEC,MAAM,EAAE;MAAS,CAAC,CAAC;IACvE;EACF;EAEAC,MAAMA,CAAA,EAAG;IACP,MAAMzB,SAAS,GAAG,EAAE;IACpB,MAAM0B,QAAQ,GAAG,IAAI,CAAC3B,KAAK,CAAC2B,QAAQ,IAAI,IAAI;IAC5C,IAAI,CAAC1B,SAAS,CAACI,OAAO,CAAC,CAACuB,CAAC,EAAEC,GAAG,KAAK;MACjC,MAAMC,KAAK,GAAIF,CAAC,CAACf,IAAI,IAAIc,QAAQ,GAAG,WAAW,GAAG,EAAG;MACrD1B,SAAS,CAACU,IAAI,CAAClB,0DAAA;QAAIuC,SAAS,EAAEF,KAAM;QAACG,GAAG,EAAEJ,GAAI;QAC5CK,GAAG,EAAEA,GAAG,IAAI;UAAC,IAAIN,CAAC,CAACf,IAAI,IAAIc,QAAQ,EAAE;YAAC,IAAI,CAACL,WAAW,GAAGY,GAAG;UAAA;QAAC,CAAE;QAC/DC,OAAO,EAAEC,CAAC,IAAI,IAAI,CAACpC,KAAK,CAACqC,QAAQ,CAACT,CAAC,CAACf,IAAI,EAAEe,CAAC,CAACpB,IAAI;MAAE,GAClDf,0DAAA;QAAMuC,SAAS,EAAC;MAAc,GAAEJ,CAAC,CAACd,IAAI,CAAQ,EAC9CrB,0DAAA;QAAMuC,SAAS,EAAC;MAAS,GAAC,MAAM,EAACJ,CAAC,CAACb,IAAI,CAAQ,EAC/CtB,0DAAA;QAAMuC,SAAS,EAAC;MAAW,GAAC,OAAO,EAACJ,CAAC,CAACpB,IAAI,CAAQ,CAC/C,CAAC;IACR,CAAC,CAAC;IAEF,OACEf,0DAAA;MAAKuC,SAAS,EAAC,kBAAkB;MAACF,KAAK,EAAE;QAACQ,MAAM,EAAE;MAAO;IAAE,GACzD7C,0DAAA;MAAIuC,SAAS,EAAC;IAAwB,GACnC/B,SAAS,CACP,CACD;EAEV;AACF;AAEA,iEAAeP,sDAAU,CAACG,oBAAoB,CAAC","sources":["webpack://tinode-webapp/./src/widgets/phone-country-selector.jsx"],"sourcesContent":["// Selector for country dialing code.\n\nimport React from 'react';\nimport { injectIntl } from 'react-intl';\n\nimport * as dcodes from '../dcodes.json';\n\nimport { flagEmoji } from '../lib/strformat';\n\nclass PhoneCountrySelector extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.countries = [];\n const { formatDisplayName } = props.intl;\n dcodes.default.forEach(dc => {\n const parts = dc.dial.split(',');\n parts.forEach(part => {\n this.countries.push({\n dial: part.trim(),\n code: dc.code,\n flag: flagEmoji(dc.code),\n name: formatDisplayName(dc.code, {type: 'region'})\n });\n });\n });\n\n this.countries.sort((a, b) => a.name.localeCompare(b.name));\n }\n\n componentDidMount() {\n if (this.selectedRef) {\n this.selectedRef.scrollIntoView({block: 'center', inline: 'nearest'});\n }\n }\n\n render() {\n const countries = [];\n const selected = this.props.selected || 'US';\n this.countries.forEach((c, idx) => {\n const style = (c.code == selected ? 'selected ' : '');\n countries.push(<li className={style} key={idx}\n ref={ref => {if (c.code == selected) {this.selectedRef = ref}}}\n onClick={_ => this.props.onSubmit(c.code, c.dial)}>\n <span className=\"country-flag\">{c.flag}</span>\n <span className=\"country\">&nbsp;{c.name}</span>\n <span className=\"dial-code\">&nbsp;+{c.dial}</span>\n </li>);\n });\n\n return (\n <div className=\"scrollable-panel\" style={{height: '30rem'}}>\n <ul className=\"phone-country-selector\">\n {countries}\n </ul>\n </div>\n );\n }\n}\n\nexport default injectIntl(PhoneCountrySelector);\n"],"names":["React","injectIntl","dcodes","flagEmoji","PhoneCountrySelector","PureComponent","constructor","props","countries","formatDisplayName","intl","default","forEach","dc","parts","dial","split","part","push","trim","code","flag","name","type","sort","a","b","localeCompare","componentDidMount","selectedRef","scrollIntoView","block","inline","render","selected","c","idx","style","createElement","className","key","ref","onClick","_","onSubmit","height"],"sourceRoot":""} {"version":3,"file":"src_widgets_phone-country-selector_jsx.dev.js","mappings":";;;;;;;;;;;;;;;;;;;AAE0B;AACc;AAEC;AAEI;AAE7C,MAAMI,oBAAoB,SAASJ,4DAAmB,CAAC;EACrDM,WAAWA,CAACC,KAAK,EAAE;IACjB,KAAK,CAACA,KAAK,CAAC;IAEZ,IAAI,CAACC,SAAS,GAAG,EAAE;IACnB,MAAM;MAAEC;IAAkB,CAAC,GAAGF,KAAK,CAACG,IAAI;IACxCR,yCAAc,CAACU,OAAO,CAACC,EAAE,IAAI;MAC3B,MAAMC,KAAK,GAAGD,EAAE,CAACE,IAAI,CAACC,KAAK,CAAC,GAAG,CAAC;MAChCF,KAAK,CAACF,OAAO,CAACK,IAAI,IAAI;QACpB,IAAI,CAACT,SAAS,CAACU,IAAI,CAAC;UAClBH,IAAI,EAAEE,IAAI,CAACE,IAAI,CAAC,CAAC;UACjBC,IAAI,EAAEP,EAAE,CAACO,IAAI;UACbC,IAAI,EAAElB,yDAAS,CAACU,EAAE,CAACO,IAAI,CAAC;UACxBE,IAAI,EAAEb,iBAAiB,CAACI,EAAE,CAACO,IAAI,EAAE;YAACG,IAAI,EAAE;UAAQ,CAAC;QACnD,CAAC,CAAC;MACJ,CAAC,CAAC;IACJ,CAAC,CAAC;IAEF,IAAI,CAACf,SAAS,CAACgB,IAAI,CAAC,CAACC,CAAC,EAAEC,CAAC,KAAKD,CAAC,CAACH,IAAI,CAACK,aAAa,CAACD,CAAC,CAACJ,IAAI,CAAC,CAAC;EAC7D;EAEAM,iBAAiBA,CAAA,EAAG;IAClB,IAAI,IAAI,CAACC,WAAW,EAAE;MACpB,IAAI,CAACA,WAAW,CAACC,cAAc,CAAC;QAACC,KAAK,EAAE,QAAQ;QAAEC,MAAM,EAAE;MAAS,CAAC,CAAC;IACvE;EACF;EAEAC,MAAMA,CAAA,EAAG;IACP,MAAMzB,SAAS,GAAG,EAAE;IACpB,MAAM0B,QAAQ,GAAG,IAAI,CAAC3B,KAAK,CAAC2B,QAAQ,IAAI,IAAI;IAC5C,IAAI,CAAC1B,SAAS,CAACI,OAAO,CAAC,CAACuB,CAAC,EAAEC,GAAG,KAAK;MACjC,MAAMC,KAAK,GAAIF,CAAC,CAACf,IAAI,IAAIc,QAAQ,GAAG,WAAW,GAAG,EAAG;MACrD1B,SAAS,CAACU,IAAI,CAAClB,0DAAA;QAAIuC,SAAS,EAAEF,KAAM;QAACG,GAAG,EAAEJ,GAAI;QAC5CK,GAAG,EAAEA,GAAG,IAAI;UAAC,IAAIN,CAAC,CAACf,IAAI,IAAIc,QAAQ,EAAE;YAAC,IAAI,CAACL,WAAW,GAAGY,GAAG;UAAA;QAAC,CAAE;QAC/DC,OAAO,EAAEC,CAAC,IAAI,IAAI,CAACpC,KAAK,CAACqC,QAAQ,CAACT,CAAC,CAACf,IAAI,EAAEe,CAAC,CAACpB,IAAI;MAAE,GAClDf,0DAAA;QAAMuC,SAAS,EAAC;MAAc,GAAEJ,CAAC,CAACd,IAAW,CAAC,EAC9CrB,0DAAA;QAAMuC,SAAS,EAAC;MAAS,GAAC,MAAM,EAACJ,CAAC,CAACb,IAAW,CAAC,EAC/CtB,0DAAA;QAAMuC,SAAS,EAAC;MAAW,GAAC,OAAO,EAACJ,CAAC,CAACpB,IAAW,CAC/C,CAAC,CAAC;IACR,CAAC,CAAC;IAEF,OACEf,0DAAA;MAAKuC,SAAS,EAAC,kBAAkB;MAACF,KAAK,EAAE;QAACQ,MAAM,EAAE;MAAO;IAAE,GACzD7C,0DAAA;MAAIuC,SAAS,EAAC;IAAwB,GACnC/B,SACC,CACD,CAAC;EAEV;AACF;AAEA,iEAAeP,sDAAU,CAACG,oBAAoB,CAAC","sources":["webpack://tinode-webapp/./src/widgets/phone-country-selector.jsx"],"sourcesContent":["// Selector for country dialing code.\n\nimport React from 'react';\nimport { injectIntl } from 'react-intl';\n\nimport * as dcodes from '../dcodes.json';\n\nimport { flagEmoji } from '../lib/strformat';\n\nclass PhoneCountrySelector extends React.PureComponent {\n constructor(props) {\n super(props);\n\n this.countries = [];\n const { formatDisplayName } = props.intl;\n dcodes.default.forEach(dc => {\n const parts = dc.dial.split(',');\n parts.forEach(part => {\n this.countries.push({\n dial: part.trim(),\n code: dc.code,\n flag: flagEmoji(dc.code),\n name: formatDisplayName(dc.code, {type: 'region'})\n });\n });\n });\n\n this.countries.sort((a, b) => a.name.localeCompare(b.name));\n }\n\n componentDidMount() {\n if (this.selectedRef) {\n this.selectedRef.scrollIntoView({block: 'center', inline: 'nearest'});\n }\n }\n\n render() {\n const countries = [];\n const selected = this.props.selected || 'US';\n this.countries.forEach((c, idx) => {\n const style = (c.code == selected ? 'selected ' : '');\n countries.push(<li className={style} key={idx}\n ref={ref => {if (c.code == selected) {this.selectedRef = ref}}}\n onClick={_ => this.props.onSubmit(c.code, c.dial)}>\n <span className=\"country-flag\">{c.flag}</span>\n <span className=\"country\">&nbsp;{c.name}</span>\n <span className=\"dial-code\">&nbsp;+{c.dial}</span>\n </li>);\n });\n\n return (\n <div className=\"scrollable-panel\" style={{height: '30rem'}}>\n <ul className=\"phone-country-selector\">\n {countries}\n </ul>\n </div>\n );\n }\n}\n\nexport default injectIntl(PhoneCountrySelector);\n"],"names":["React","injectIntl","dcodes","flagEmoji","PhoneCountrySelector","PureComponent","constructor","props","countries","formatDisplayName","intl","default","forEach","dc","parts","dial","split","part","push","trim","code","flag","name","type","sort","a","b","localeCompare","componentDidMount","selectedRef","scrollIntoView","block","inline","render","selected","c","idx","style","createElement","className","key","ref","onClick","_","onSubmit","height"],"sourceRoot":""}
+92 -71
View File
@@ -378,34 +378,34 @@ class CommError extends Error {
__webpack_require__.r(__webpack_exports__); __webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "DEFAULT_MESSAGES_PAGE": () => (/* binding */ DEFAULT_MESSAGES_PAGE), /* harmony export */ DEFAULT_MESSAGES_PAGE: () => (/* binding */ DEFAULT_MESSAGES_PAGE),
/* harmony export */ "DEL_CHAR": () => (/* binding */ DEL_CHAR), /* harmony export */ DEL_CHAR: () => (/* binding */ DEL_CHAR),
/* harmony export */ "EXPIRE_PROMISES_PERIOD": () => (/* binding */ EXPIRE_PROMISES_PERIOD), /* harmony export */ EXPIRE_PROMISES_PERIOD: () => (/* binding */ EXPIRE_PROMISES_PERIOD),
/* harmony export */ "EXPIRE_PROMISES_TIMEOUT": () => (/* binding */ EXPIRE_PROMISES_TIMEOUT), /* harmony export */ EXPIRE_PROMISES_TIMEOUT: () => (/* binding */ EXPIRE_PROMISES_TIMEOUT),
/* harmony export */ "LIBRARY": () => (/* binding */ LIBRARY), /* harmony export */ LIBRARY: () => (/* binding */ LIBRARY),
/* harmony export */ "LOCAL_SEQID": () => (/* binding */ LOCAL_SEQID), /* harmony export */ LOCAL_SEQID: () => (/* binding */ LOCAL_SEQID),
/* harmony export */ "MAX_PINNED_COUNT": () => (/* binding */ MAX_PINNED_COUNT), /* harmony export */ MAX_PINNED_COUNT: () => (/* binding */ MAX_PINNED_COUNT),
/* harmony export */ "MESSAGE_STATUS_FAILED": () => (/* binding */ MESSAGE_STATUS_FAILED), /* harmony export */ MESSAGE_STATUS_FAILED: () => (/* binding */ MESSAGE_STATUS_FAILED),
/* harmony export */ "MESSAGE_STATUS_FATAL": () => (/* binding */ MESSAGE_STATUS_FATAL), /* harmony export */ MESSAGE_STATUS_FATAL: () => (/* binding */ MESSAGE_STATUS_FATAL),
/* harmony export */ "MESSAGE_STATUS_NONE": () => (/* binding */ MESSAGE_STATUS_NONE), /* harmony export */ MESSAGE_STATUS_NONE: () => (/* binding */ MESSAGE_STATUS_NONE),
/* harmony export */ "MESSAGE_STATUS_QUEUED": () => (/* binding */ MESSAGE_STATUS_QUEUED), /* harmony export */ MESSAGE_STATUS_QUEUED: () => (/* binding */ MESSAGE_STATUS_QUEUED),
/* harmony export */ "MESSAGE_STATUS_READ": () => (/* binding */ MESSAGE_STATUS_READ), /* harmony export */ MESSAGE_STATUS_READ: () => (/* binding */ MESSAGE_STATUS_READ),
/* harmony export */ "MESSAGE_STATUS_RECEIVED": () => (/* binding */ MESSAGE_STATUS_RECEIVED), /* harmony export */ MESSAGE_STATUS_RECEIVED: () => (/* binding */ MESSAGE_STATUS_RECEIVED),
/* harmony export */ "MESSAGE_STATUS_SENDING": () => (/* binding */ MESSAGE_STATUS_SENDING), /* harmony export */ MESSAGE_STATUS_SENDING: () => (/* binding */ MESSAGE_STATUS_SENDING),
/* harmony export */ "MESSAGE_STATUS_SENT": () => (/* binding */ MESSAGE_STATUS_SENT), /* harmony export */ MESSAGE_STATUS_SENT: () => (/* binding */ MESSAGE_STATUS_SENT),
/* harmony export */ "MESSAGE_STATUS_TO_ME": () => (/* binding */ MESSAGE_STATUS_TO_ME), /* harmony export */ MESSAGE_STATUS_TO_ME: () => (/* binding */ MESSAGE_STATUS_TO_ME),
/* harmony export */ "PROTOCOL_VERSION": () => (/* binding */ PROTOCOL_VERSION), /* harmony export */ PROTOCOL_VERSION: () => (/* binding */ PROTOCOL_VERSION),
/* harmony export */ "RECV_TIMEOUT": () => (/* binding */ RECV_TIMEOUT), /* harmony export */ RECV_TIMEOUT: () => (/* binding */ RECV_TIMEOUT),
/* harmony export */ "TOPIC_CHAN": () => (/* binding */ TOPIC_CHAN), /* harmony export */ TOPIC_CHAN: () => (/* binding */ TOPIC_CHAN),
/* harmony export */ "TOPIC_FND": () => (/* binding */ TOPIC_FND), /* harmony export */ TOPIC_FND: () => (/* binding */ TOPIC_FND),
/* harmony export */ "TOPIC_GRP": () => (/* binding */ TOPIC_GRP), /* harmony export */ TOPIC_GRP: () => (/* binding */ TOPIC_GRP),
/* harmony export */ "TOPIC_ME": () => (/* binding */ TOPIC_ME), /* harmony export */ TOPIC_ME: () => (/* binding */ TOPIC_ME),
/* harmony export */ "TOPIC_NEW": () => (/* binding */ TOPIC_NEW), /* harmony export */ TOPIC_NEW: () => (/* binding */ TOPIC_NEW),
/* harmony export */ "TOPIC_NEW_CHAN": () => (/* binding */ TOPIC_NEW_CHAN), /* harmony export */ TOPIC_NEW_CHAN: () => (/* binding */ TOPIC_NEW_CHAN),
/* harmony export */ "TOPIC_P2P": () => (/* binding */ TOPIC_P2P), /* harmony export */ TOPIC_P2P: () => (/* binding */ TOPIC_P2P),
/* harmony export */ "TOPIC_SYS": () => (/* binding */ TOPIC_SYS), /* harmony export */ TOPIC_SYS: () => (/* binding */ TOPIC_SYS),
/* harmony export */ "USER_NEW": () => (/* binding */ USER_NEW), /* harmony export */ USER_NEW: () => (/* binding */ USER_NEW),
/* harmony export */ "VERSION": () => (/* binding */ VERSION) /* harmony export */ VERSION: () => (/* binding */ VERSION)
/* harmony export */ }); /* harmony export */ });
/* harmony import */ var _version_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../version.js */ "./version.js"); /* harmony import */ var _version_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../version.js */ "./version.js");
@@ -792,7 +792,7 @@ class DB {
#onError = _ => {}; #onError = _ => {};
#logger = _ => {}; #logger = _ => {};
db = null; db = null;
disabled = false; disabled = true;
constructor(onError, logger) { constructor(onError, logger) {
this.#onError = onError || this.#onError; this.#onError = onError || this.#onError;
this.#logger = logger || this.#logger; this.#logger = logger || this.#logger;
@@ -906,7 +906,7 @@ class DB {
}; };
}); });
} }
markTopicAsDeleted(name) { markTopicAsDeleted(name, deleted) {
if (!this.isReady()) { if (!this.isReady()) {
return this.disabled ? Promise.resolve() : Promise.reject(new Error("not initialized")); return this.disabled ? Promise.resolve() : Promise.reject(new Error("not initialized"));
} }
@@ -922,8 +922,10 @@ class DB {
const req = trx.objectStore('topic').get(name); const req = trx.objectStore('topic').get(name);
req.onsuccess = event => { req.onsuccess = event => {
const topic = event.target.result; const topic = event.target.result;
topic._deleted = true; if (topic && topic._deleted != deleted) {
trx.objectStore('topic').put(topic); topic._deleted = deleted;
trx.objectStore('topic').put(topic);
}
trx.commit(); trx.commit();
}; };
}); });
@@ -3049,7 +3051,7 @@ class TopicFnd extends _topic_js__WEBPACK_IMPORTED_MODULE_1__["default"] {
constructor(callbacks) { constructor(callbacks) {
super(_config_js__WEBPACK_IMPORTED_MODULE_0__.TOPIC_FND, callbacks); super(_config_js__WEBPACK_IMPORTED_MODULE_0__.TOPIC_FND, callbacks);
} }
_processMetaSub(subs) { _processMetaSubs(subs) {
let updateCount = Object.getOwnPropertyNames(this._contacts).length; let updateCount = Object.getOwnPropertyNames(this._contacts).length;
this._contacts = {}; this._contacts = {};
for (let idx in subs) { for (let idx in subs) {
@@ -3374,7 +3376,7 @@ class TopicMe extends _topic_js__WEBPACK_IMPORTED_MODULE_2__["default"] {
this.onMetaDesc(this); this.onMetaDesc(this);
} }
} }
_processMetaSub(subs) { _processMetaSubs(subs) {
let updateCount = 0; let updateCount = 0;
subs.forEach(sub => { subs.forEach(sub => {
const topicName = sub.topic; const topicName = sub.topic;
@@ -3491,12 +3493,14 @@ class TopicMe extends _topic_js__WEBPACK_IMPORTED_MODULE_2__["default"] {
this.getMeta(this.startMetaQuery().withLaterOneSub(pres.src).build()); this.getMeta(this.startMetaQuery().withLaterOneSub(pres.src).build());
break; break;
case 'acs': case 'acs':
if (cont.acs) { if (!pres.tgt) {
cont.acs.updateAll(pres.dacs); if (cont.acs) {
} else { cont.acs.updateAll(pres.dacs);
cont.acs = new _access_mode_js__WEBPACK_IMPORTED_MODULE_0__["default"]().updateAll(pres.dacs); } else {
cont.acs = new _access_mode_js__WEBPACK_IMPORTED_MODULE_0__["default"]().updateAll(pres.dacs);
}
cont.touched = new Date();
} }
cont.touched = new Date();
break; break;
case 'ua': case 'ua':
cont.seen = { cont.seen = {
@@ -3515,10 +3519,11 @@ class TopicMe extends _topic_js__WEBPACK_IMPORTED_MODULE_2__["default"] {
cont.unread = cont.seq - cont.read; cont.unread = cont.seq - cont.read;
break; break;
case 'gone': case 'gone':
this._tinode.cacheRemTopic(pres.src);
if (!cont._deleted) { if (!cont._deleted) {
cont._deleted = true; cont._deleted = true;
cont._attached = false; cont._attached = false;
this._tinode._db.markTopicAsDeleted(pres.src); this._tinode._db.markTopicAsDeleted(pres.src, true);
} else { } else {
this._tinode._db.remTopic(pres.src); this._tinode._db.remTopic(pres.src);
} }
@@ -3548,7 +3553,13 @@ class TopicMe extends _topic_js__WEBPACK_IMPORTED_MODULE_2__["default"] {
} }
} else if (pres.what == 'tags') { } else if (pres.what == 'tags') {
this.getMeta(this.startMetaQuery().withTags().build()); this.getMeta(this.startMetaQuery().withTags().build());
} else if (pres.what == 'msg') {
this.getMeta(this.startMetaQuery().withOneSub(undefined, pres.src).build());
const dummy = this._tinode.getTopic(pres.src);
dummy._deleted = false;
this._tinode._db.updTopic(dummy);
} }
this._refreshContact(pres.what, cont);
} }
if (this.onPres) { if (this.onPres) {
this.onPres(pres); this.onPres(pres);
@@ -3627,13 +3638,13 @@ class MetaGetBuilder {
this.what = {}; this.what = {};
} }
#get_desc_ims() { #get_desc_ims() {
return this.topic.updated; return this.topic._deleted ? undefined : this.topic.updated;
} }
#get_subs_ims() { #get_subs_ims() {
if (this.topic.isP2PType()) { if (this.topic.isP2PType()) {
return this.#get_desc_ims(); return this.#get_desc_ims();
} }
return this.topic._lastSubsUpdate; return this.topic._deleted ? undefined : this.topic._lastSubsUpdate;
} }
withData(since, before, limit) { withData(since, before, limit) {
this.what['data'] = { this.what['data'] = {
@@ -3858,9 +3869,6 @@ class Topic {
if (this._attached) { if (this._attached) {
return Promise.resolve(this); return Promise.resolve(this);
} }
if (this._deleted) {
return Promise.reject(new Error("Conversation deleted"));
}
return this._tinode.subscribe(this.name || _config_js__WEBPACK_IMPORTED_MODULE_3__.TOPIC_NEW, getParams, setParams).then(ctrl => { return this._tinode.subscribe(this.name || _config_js__WEBPACK_IMPORTED_MODULE_3__.TOPIC_NEW, getParams, setParams).then(ctrl => {
if (ctrl.code >= 300) { if (ctrl.code >= 300) {
return ctrl; return ctrl;
@@ -4067,7 +4075,7 @@ class Topic {
} }
} }
params.sub._noForwarding = true; params.sub._noForwarding = true;
this._processMetaSub([params.sub]); this._processMetaSubs([params.sub]);
} }
if (params.desc) { if (params.desc) {
if (ctrl.params && ctrl.params.acs) { if (ctrl.params && ctrl.params.acs) {
@@ -4248,7 +4256,7 @@ class Topic {
} }
if (update) { if (update) {
this._tinode.note(this.name, what, seq); this._tinode.note(this.name, what, seq);
this._updateReadRecv(what, seq); this._updateMyReadRecv(what, seq);
if (this.acs != null && !this.acs.isMuted()) { if (this.acs != null && !this.acs.isMuted()) {
const me = this._tinode.getMeTopic(); const me = this._tinode.getMeTopic();
me._refreshContact(what, this); me._refreshContact(what, this);
@@ -4284,7 +4292,7 @@ class Topic {
} }
return this._tinode.videoCall(this.name, seq, evt, payload); return this._tinode.videoCall(this.name, seq, evt, payload);
} }
_updateReadRecv(what, seq, ts) { _updateMyReadRecv(what, seq, ts) {
let oldVal, let oldVal,
doUpdate = false; doUpdate = false;
seq = seq | 0; seq = seq | 0;
@@ -4455,6 +4463,9 @@ class Topic {
} }
msgHasMoreMessages(min, max, newer) { msgHasMoreMessages(min, max, newer) {
const gaps = []; const gaps = [];
if (min >= max) {
return gaps;
}
let maxSeq = 0; let maxSeq = 0;
let gap; let gap;
this._messages.forEach((msg, prev) => { this._messages.forEach((msg, prev) => {
@@ -4666,7 +4677,15 @@ class Topic {
this.onData(data); this.onData(data);
} }
const what = outgoing ? 'read' : 'msg'; const what = outgoing ? 'read' : 'msg';
this._updateReadRecv(what, data.seq, data.ts); this._updateMyReadRecv(what, data.seq, data.ts);
if (!outgoing && data.from) {
this._routeInfo({
what: 'read',
from: data.from,
seq: data.seq,
_noForwarding: true
});
}
this._tinode.getMeTopic()._refreshContact(what, this); this._tinode.getMeTopic()._refreshContact(what, this);
} }
_routeMeta(meta) { _routeMeta(meta) {
@@ -4674,7 +4693,7 @@ class Topic {
this._processMetaDesc(meta.desc); this._processMetaDesc(meta.desc);
} }
if (meta.sub && meta.sub.length > 0) { if (meta.sub && meta.sub.length > 0) {
this._processMetaSub(meta.sub); this._processMetaSubs(meta.sub);
} }
if (meta.del) { if (meta.del) {
this._processDelMessages(meta.del.clear, meta.del.delseq); this._processDelMessages(meta.del.clear, meta.del.delseq);
@@ -4712,7 +4731,7 @@ class Topic {
break; break;
case 'upd': case 'upd':
if (pres.src && !this._tinode.isTopicCached(pres.src)) { if (pres.src && !this._tinode.isTopicCached(pres.src)) {
this.getMeta(this.startMetaQuery().withLaterOneSub(pres.src).build()); this.getMeta(this.startMetaQuery().withOneSub(undefined, pres.src).build());
} }
break; break;
case 'aux': case 'aux':
@@ -4735,11 +4754,11 @@ class Topic {
user.acs = acs; user.acs = acs;
} }
user.updated = new Date(); user.updated = new Date();
this._processMetaSub([user]); this._processMetaSubs([user]);
} }
} else { } else {
user.acs.updateAll(pres.dacs); user.acs.updateAll(pres.dacs);
this._processMetaSub([{ this._processMetaSubs([{
user: uid, user: uid,
updated: new Date(), updated: new Date(),
acs: user.acs acs: user.acs
@@ -4768,12 +4787,14 @@ class Topic {
if (msg) { if (msg) {
this.msgStatus(msg, true); this.msgStatus(msg, true);
} }
if (this._tinode.isMe(info.from)) { if (this._tinode.isMe(info.from) && !info._noForwarding) {
this._updateReadRecv(info.what, info.seq); this._updateMyReadRecv(info.what, info.seq);
} }
this._tinode.getMeTopic()._refreshContact(info.what, this); this._tinode.getMeTopic()._refreshContact(info.what, this);
break; break;
case 'kp': case 'kp':
case 'kpa':
case 'kpv':
break; break;
case 'call': case 'call':
break; break;
@@ -4804,7 +4825,7 @@ class Topic {
this.onMetaDesc(this); this.onMetaDesc(this);
} }
} }
_processMetaSub(subs) { _processMetaSubs(subs) {
for (let idx in subs) { for (let idx in subs) {
const sub = subs[idx]; const sub = subs[idx];
sub.online = !!sub.online; sub.online = !!sub.online;
@@ -4970,16 +4991,16 @@ class Topic {
__webpack_require__.r(__webpack_exports__); __webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "clipRange": () => (/* binding */ clipRange), /* harmony export */ clipRange: () => (/* binding */ clipRange),
/* harmony export */ "isUrlRelative": () => (/* binding */ isUrlRelative), /* harmony export */ isUrlRelative: () => (/* binding */ isUrlRelative),
/* harmony export */ "jsonParseHelper": () => (/* binding */ jsonParseHelper), /* harmony export */ jsonParseHelper: () => (/* binding */ jsonParseHelper),
/* harmony export */ "listToRanges": () => (/* binding */ listToRanges), /* harmony export */ listToRanges: () => (/* binding */ listToRanges),
/* harmony export */ "mergeObj": () => (/* binding */ mergeObj), /* harmony export */ mergeObj: () => (/* binding */ mergeObj),
/* harmony export */ "mergeToCache": () => (/* binding */ mergeToCache), /* harmony export */ mergeToCache: () => (/* binding */ mergeToCache),
/* harmony export */ "normalizeArray": () => (/* binding */ normalizeArray), /* harmony export */ normalizeArray: () => (/* binding */ normalizeArray),
/* harmony export */ "normalizeRanges": () => (/* binding */ normalizeRanges), /* harmony export */ normalizeRanges: () => (/* binding */ normalizeRanges),
/* harmony export */ "rfc3339DateString": () => (/* binding */ rfc3339DateString), /* harmony export */ rfc3339DateString: () => (/* binding */ rfc3339DateString),
/* harmony export */ "simplify": () => (/* binding */ simplify) /* harmony export */ simplify: () => (/* binding */ simplify)
/* harmony export */ }); /* harmony export */ });
/* harmony import */ var _access_mode_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./access-mode.js */ "./src/access-mode.js"); /* harmony import */ var _access_mode_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./access-mode.js */ "./src/access-mode.js");
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./config.js */ "./src/config.js"); /* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./config.js */ "./src/config.js");
@@ -5181,7 +5202,7 @@ function clipRange(src, clip) {
__webpack_require__.r(__webpack_exports__); __webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "PACKAGE_VERSION": () => (/* binding */ PACKAGE_VERSION) /* harmony export */ PACKAGE_VERSION: () => (/* binding */ PACKAGE_VERSION)
/* harmony export */ }); /* harmony export */ });
const PACKAGE_VERSION = "0.23.0-rc1"; const PACKAGE_VERSION = "0.23.0-rc1";
@@ -5275,9 +5296,9 @@ var __webpack_exports__ = {};
\***********************/ \***********************/
__webpack_require__.r(__webpack_exports__); __webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "AccessMode": () => (/* reexport safe */ _access_mode_js__WEBPACK_IMPORTED_MODULE_0__["default"]), /* harmony export */ AccessMode: () => (/* reexport safe */ _access_mode_js__WEBPACK_IMPORTED_MODULE_0__["default"]),
/* harmony export */ "Drafty": () => (/* reexport default from dynamic */ _drafty_js__WEBPACK_IMPORTED_MODULE_5___default.a), /* harmony export */ Drafty: () => (/* reexport default from dynamic */ _drafty_js__WEBPACK_IMPORTED_MODULE_5___default.a),
/* harmony export */ "Tinode": () => (/* binding */ Tinode) /* harmony export */ Tinode: () => (/* binding */ Tinode)
/* harmony export */ }); /* harmony export */ });
/* harmony import */ var _access_mode_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./access-mode.js */ "./src/access-mode.js"); /* harmony import */ var _access_mode_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./access-mode.js */ "./src/access-mode.js");
/* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./config.js */ "./src/config.js"); /* harmony import */ var _config_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./config.js */ "./src/config.js");
@@ -5712,7 +5733,7 @@ class Tinode {
} else if (pkt.ctrl.params.what == 'sub') { } else if (pkt.ctrl.params.what == 'sub') {
const topic = this.#cacheGet('topic', pkt.ctrl.topic); const topic = this.#cacheGet('topic', pkt.ctrl.topic);
if (topic) { if (topic) {
topic._processMetaSub([]); topic._processMetaSubs([]);
} }
} }
} }
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,3 +1,3 @@
// This is a generated file. Don't edit. // This is a generated file. Don't edit.
const PACKAGE_VERSION = "0.23.0-rc1"; const PACKAGE_VERSION = "0.23.0-rc2";