Merge branch 'next' into vc
This commit is contained in:
Generated
+7
-7
@@ -15,7 +15,7 @@
|
|||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-intl": "^6.3.0",
|
"react-intl": "^6.3.0",
|
||||||
"tinode-sdk": "^0.22.6",
|
"tinode-sdk": "^0.22.7",
|
||||||
"webm-duration-fix": "^1.0.4"
|
"webm-duration-fix": "^1.0.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -7709,9 +7709,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss-load-config/node_modules/yaml": {
|
"node_modules/postcss-load-config/node_modules/yaml": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
|
||||||
"integrity": "sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==",
|
"integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 14"
|
"node": ">= 14"
|
||||||
@@ -9248,9 +9248,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/tinode-sdk": {
|
"node_modules/tinode-sdk": {
|
||||||
"version": "0.22.6",
|
"version": "0.22.7",
|
||||||
"resolved": "https://registry.npmjs.org/tinode-sdk/-/tinode-sdk-0.22.6.tgz",
|
"resolved": "https://registry.npmjs.org/tinode-sdk/-/tinode-sdk-0.22.7.tgz",
|
||||||
"integrity": "sha512-JMBW7sLFhrX2Ohkv2unkI4FEgQJLBtV4necRnzK2+5Gsq1wCHqAqPZXVPqzdU2Jumapxh5mZ3QrGLStay7vs3w=="
|
"integrity": "sha512-+tUmuvsQ0i3IIymk7sC2VyPHXuUTODUyA1C4GybYD2H8VlNc10zS6Zk8T3HiueaYutw9xk4j6E2qw7P9INGvcw=="
|
||||||
},
|
},
|
||||||
"node_modules/to-fast-properties": {
|
"node_modules/to-fast-properties": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
|
|||||||
@@ -32,9 +32,11 @@ 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)) {
|
||||||
|
if (params[key] !== undefined) {
|
||||||
args.push(key + '=' + encodeURIComponent(params[key]));
|
args.push(key + '=' + encodeURIComponent(params[key]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (args.length > 0) {
|
if (args.length > 0) {
|
||||||
url += '?' + args.join('&');
|
url += '?' + args.join('&');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>}
|
||||||
cred.done ? null :
|
{!cred.done || validated[cred.meth] > 1 || this.props.reqCredMethod != cred.meth ?
|
||||||
<>
|
|
||||||
<i className="material-icons">pending</i>
|
|
||||||
<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) {
|
||||||
|
|||||||
@@ -274,12 +274,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) {
|
||||||
@@ -478,7 +478,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 +502,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.
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ 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.
|
||||||
@@ -193,7 +193,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 ? 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" />
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -763,6 +763,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 +849,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 +888,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 +1020,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 +1289,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 +1711,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.
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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}>
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export default class ErrorPanel extends React.PureComponent {
|
|||||||
{this.props.text}
|
{this.props.text}
|
||||||
{this.props.action ?
|
{this.props.action ?
|
||||||
<>
|
<>
|
||||||
 <a href="#"
|
 <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}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
|||||||
import {
|
import {
|
||||||
connect,
|
connect,
|
||||||
LocalParticipant,
|
LocalParticipant,
|
||||||
|
LocalVideoTrack,
|
||||||
MediaDeviceFailure,
|
MediaDeviceFailure,
|
||||||
Participant,
|
Participant,
|
||||||
ParticipantEvent,
|
ParticipantEvent,
|
||||||
@@ -171,7 +172,7 @@ class VCPanel extends React.PureComponent {
|
|||||||
handleLocalTrackPublished(pub) {
|
handleLocalTrackPublished(pub) {
|
||||||
console.log('Local track published', pub);
|
console.log('Local track published', pub);
|
||||||
const track = pub.track;
|
const track = pub.track;
|
||||||
if (this.localRef.current) {
|
if ((track instanceof LocalVideoTrack) && this.localRef.current) {
|
||||||
const elem = this.localRef.current;
|
const elem = this.localRef.current;
|
||||||
track.attach(elem);
|
track.attach(elem);
|
||||||
// flip.
|
// flip.
|
||||||
|
|||||||
@@ -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
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+23
-12
@@ -3245,9 +3245,11 @@ class HashNavigation {
|
|||||||
const args = [];
|
const args = [];
|
||||||
for (const key in params) {
|
for (const key in params) {
|
||||||
if (params.hasOwnProperty(key)) {
|
if (params.hasOwnProperty(key)) {
|
||||||
|
if (params[key] !== undefined) {
|
||||||
args.push(key + '=' + encodeURIComponent(params[key]));
|
args.push(key + '=' + encodeURIComponent(params[key]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (args.length > 0) {
|
if (args.length > 0) {
|
||||||
url += '?' + args.join('&');
|
url += '?' + args.join('&');
|
||||||
}
|
}
|
||||||
@@ -5329,11 +5331,11 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (topic) {
|
if (topic) {
|
||||||
if (this.state.topic != prevState.topic || !prevProps.ready) {
|
if (this.state.topic != prevState.topic || this.props.myUserId && !prevProps.myUserId) {
|
||||||
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 && !_config_js__WEBPACK_IMPORTED_MODULE_19__.IMMEDIATE_P2P_SUBSCRIPTION) {
|
if (topic.isP2PType() && newTopic && !_config_js__WEBPACK_IMPORTED_MODULE_19__.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) {
|
||||||
@@ -5529,6 +5531,11 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
|
|||||||
topic: ctrl.topic
|
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);
|
||||||
let discard = [];
|
let discard = [];
|
||||||
topic.queuedMessages(pub => {
|
topic.queuedMessages(pub => {
|
||||||
@@ -7216,6 +7223,7 @@ class SidepanelView extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCo
|
|||||||
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,
|
||||||
@@ -8031,6 +8039,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().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;
|
||||||
_lib_navigation_js__WEBPACK_IMPORTED_MODULE_18__["default"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_18__["default"].composeUrlHash(parsed.path, parsed.params));
|
_lib_navigation_js__WEBPACK_IMPORTED_MODULE_18__["default"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_18__["default"].composeUrlHash(parsed.path, parsed.params));
|
||||||
}
|
}
|
||||||
handleLoginSuccessful() {
|
handleLoginSuccessful() {
|
||||||
@@ -8104,7 +8113,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
|||||||
}
|
}
|
||||||
} else if (what == 'read') {
|
} else if (what == 'read') {
|
||||||
this.resetContactList();
|
this.resetContactList();
|
||||||
} else if (what == 'msg') {
|
} else if (what == 'msg' && cont) {
|
||||||
const topic = this.tinode.getTopic(cont.topic);
|
const topic = this.tinode.getTopic(cont.topic);
|
||||||
const archived = topic && topic.isArchived();
|
const archived = topic && topic.isArchived();
|
||||||
if (cont.unread > 0 && this.state.messageSounds && !archived) {
|
if (cont.unread > 0 && this.state.messageSounds && !archived) {
|
||||||
@@ -8125,7 +8134,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (what == 'del') {} else if (what == 'upd' || what == 'call') {} else {
|
} else if (what == 'del') {} else if (what == 'upd' || what == 'call') {} else {
|
||||||
console.info("Unsupported (yet) presence update:", what, "in", cont.topic);
|
console.info("Unsupported (yet) presence update:", what, "in", (cont || {}).topic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tnMeSubsUpdated(unused) {
|
tnMeSubsUpdated(unused) {
|
||||||
@@ -8227,6 +8236,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
topicSelected: null,
|
||||||
errorText: '',
|
errorText: '',
|
||||||
errorLevel: null,
|
errorLevel: null,
|
||||||
mobilePanel: 'sidepanel',
|
mobilePanel: 'sidepanel',
|
||||||
@@ -8474,7 +8484,8 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
|||||||
}
|
}
|
||||||
handleCredConfirm(method, response) {
|
handleCredConfirm(method, response) {
|
||||||
TinodeWeb.navigateToCredentialsView({
|
TinodeWeb.navigateToCredentialsView({
|
||||||
cred: [method]
|
cred: [method],
|
||||||
|
code: response
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
handleSidepanelCancel() {
|
handleSidepanelCancel() {
|
||||||
@@ -8851,7 +8862,7 @@ class TinodeWeb extends (react__WEBPACK_IMPORTED_MODULE_0___default().Component)
|
|||||||
meth: cred,
|
meth: cred,
|
||||||
resp: code
|
resp: code
|
||||||
}
|
}
|
||||||
}).catch(err => this.handleError(err.message, 'err'));
|
}).then(_ => _lib_navigation_js__WEBPACK_IMPORTED_MODULE_18__["default"].navigateTo(_lib_navigation_js__WEBPACK_IMPORTED_MODULE_18__["default"].setUrlSidePanel(window.location.hash, 'contacts'))).catch(err => this.handleError(err.message, 'err'));
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
credMethod: cred,
|
credMethod: cred,
|
||||||
@@ -9468,7 +9479,7 @@ class ValidationView extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureC
|
|||||||
placeholder: numbers_only,
|
placeholder: numbers_only,
|
||||||
value: this.state.code,
|
value: this.state.code,
|
||||||
onChange: this.handleCodeChange,
|
onChange: this.handleCodeChange,
|
||||||
onKeyPress: this.handleKeyPress,
|
onKeyDown: this.handleKeyPress,
|
||||||
required: true
|
required: true
|
||||||
}))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
}))), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
|
||||||
className: "dialog-buttons"
|
className: "dialog-buttons"
|
||||||
@@ -12994,7 +13005,7 @@ class ErrorPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCompo
|
|||||||
className: "icon"
|
className: "icon"
|
||||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
|
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("i", {
|
||||||
className: "material-icons"
|
className: "material-icons"
|
||||||
}, level)), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, this.props.text, this.props.action ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
|
}, level)), react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", null, this.props.text, this.props.action ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement((react__WEBPACK_IMPORTED_MODULE_0___default().Fragment), null, "\xA0 ", react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", {
|
||||||
href: "#",
|
href: "#",
|
||||||
style: {
|
style: {
|
||||||
whiteSpace: 'nowrap'
|
whiteSpace: 'nowrap'
|
||||||
@@ -16821,7 +16832,7 @@ class VCPanel extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComponen
|
|||||||
handleLocalTrackPublished(pub) {
|
handleLocalTrackPublished(pub) {
|
||||||
console.log('Local track published', pub);
|
console.log('Local track published', pub);
|
||||||
const track = pub.track;
|
const track = pub.track;
|
||||||
if (this.localRef.current) {
|
if (track instanceof livekit_client__WEBPACK_IMPORTED_MODULE_2__.LocalVideoTrack && this.localRef.current) {
|
||||||
const elem = this.localRef.current;
|
const elem = this.localRef.current;
|
||||||
track.attach(elem);
|
track.attach(elem);
|
||||||
elem.style.transform = 'scale(-1, 1)';
|
elem.style.transform = 'scale(-1, 1)';
|
||||||
@@ -17192,7 +17203,7 @@ class VisiblePassword extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pure
|
|||||||
super(props);
|
super(props);
|
||||||
this.inputRef = react__WEBPACK_IMPORTED_MODULE_0___default().createRef();
|
this.inputRef = react__WEBPACK_IMPORTED_MODULE_0___default().createRef();
|
||||||
this.state = {
|
this.state = {
|
||||||
value: this.props.value,
|
value: this.props.value || '',
|
||||||
visible: false
|
visible: false
|
||||||
};
|
};
|
||||||
this.handleVisibility = this.handleVisibility.bind(this);
|
this.handleVisibility = this.handleVisibility.bind(this);
|
||||||
@@ -17222,7 +17233,7 @@ class VisiblePassword extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pure
|
|||||||
handleKeyDown(e) {
|
handleKeyDown(e) {
|
||||||
if (e.keyCode == 27) {
|
if (e.keyCode == 27) {
|
||||||
this.setState({
|
this.setState({
|
||||||
value: this.props.value,
|
value: this.props.value || '',
|
||||||
visible: false
|
visible: false
|
||||||
});
|
});
|
||||||
if (this.props.onFinished) {
|
if (this.props.onFinished) {
|
||||||
@@ -17253,7 +17264,7 @@ class VisiblePassword extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pure
|
|||||||
onBlur: this.handleEditingFinished
|
onBlur: this.handleEditingFinished
|
||||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", {
|
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement("input", {
|
||||||
className: "with-visibility",
|
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' : '',
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -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
@@ -71,7 +71,7 @@ class PasswordResetView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Pu
|
|||||||
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.state.email.trim() || this.state.tel.trim();
|
||||||
@@ -222,7 +222,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 ? 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
Reference in New Issue
Block a user