switch all modals over to ember-concurrency and gh-task-button
This commit is contained in:
parent
0e7d455351
commit
01cc9ae9be
@ -1,10 +1,9 @@
|
||||
import injectService from 'ember-service/inject';
|
||||
import ModalComponent from 'ghost-admin/components/modals/base';
|
||||
import {task} from 'ember-concurrency';
|
||||
|
||||
export default ModalComponent.extend({
|
||||
|
||||
submitting: false,
|
||||
|
||||
ghostPaths: injectService(),
|
||||
notifications: injectService(),
|
||||
store: injectService(),
|
||||
@ -28,18 +27,21 @@ export default ModalComponent.extend({
|
||||
this.get('notifications').showAPIError(error, {key: 'all-content.delete'});
|
||||
},
|
||||
|
||||
deleteAll: task(function* () {
|
||||
try {
|
||||
yield this._deleteAll();
|
||||
this._unloadData();
|
||||
this._showSuccess();
|
||||
} catch (error) {
|
||||
this._showFailure(error);
|
||||
} finally {
|
||||
this.send('closeModal');
|
||||
}
|
||||
}).drop(),
|
||||
|
||||
actions: {
|
||||
confirm() {
|
||||
this.set('submitting', true);
|
||||
|
||||
this._deleteAll().then(() => {
|
||||
this._unloadData();
|
||||
this._showSuccess();
|
||||
}).catch((error) => {
|
||||
this._showFailure(error);
|
||||
}).finally(() => {
|
||||
this.send('closeModal');
|
||||
});
|
||||
this.get('deleteAll').perform();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,11 +1,10 @@
|
||||
import {alias} from 'ember-computed';
|
||||
import injectService from 'ember-service/inject';
|
||||
import ModalComponent from 'ghost-admin/components/modals/base';
|
||||
import {task} from 'ember-concurrency';
|
||||
|
||||
export default ModalComponent.extend({
|
||||
|
||||
submitting: false,
|
||||
|
||||
post: alias('model'),
|
||||
|
||||
notifications: injectService(),
|
||||
@ -33,17 +32,20 @@ export default ModalComponent.extend({
|
||||
this.get('notifications').showAPIError(error, {key: 'post.delete.failed'});
|
||||
},
|
||||
|
||||
deletePost: task(function* () {
|
||||
try {
|
||||
yield this._deletePost();
|
||||
this._success();
|
||||
} catch (e) {
|
||||
this._failure(e);
|
||||
} finally {
|
||||
this.send('closeModal');
|
||||
}
|
||||
}).drop(),
|
||||
|
||||
actions: {
|
||||
confirm() {
|
||||
this.set('submitting', true);
|
||||
|
||||
this._deletePost().then(() => {
|
||||
this._success();
|
||||
}, (error) => {
|
||||
this._failure(error);
|
||||
}).finally(() => {
|
||||
this.send('closeModal');
|
||||
});
|
||||
this.get('deletePost').perform();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,20 +1,19 @@
|
||||
import {alias} from 'ember-computed';
|
||||
import ModalComponent from 'ghost-admin/components/modals/base';
|
||||
import {invokeAction} from 'ember-invoke-action';
|
||||
import {task} from 'ember-concurrency';
|
||||
|
||||
export default ModalComponent.extend({
|
||||
|
||||
submitting: false,
|
||||
|
||||
subscriber: alias('model'),
|
||||
|
||||
deleteSubscriber: task(function* () {
|
||||
yield invokeAction(this, 'confirm');
|
||||
}).drop(),
|
||||
|
||||
actions: {
|
||||
confirm() {
|
||||
this.set('submitting', true);
|
||||
|
||||
invokeAction(this, 'confirm').finally(() => {
|
||||
this.set('submitting', false);
|
||||
});
|
||||
this.get('deleteSubscriber').perform();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,24 +1,27 @@
|
||||
import computed, {alias} from 'ember-computed';
|
||||
import ModalComponent from 'ghost-admin/components/modals/base';
|
||||
import {invokeAction} from 'ember-invoke-action';
|
||||
import {task} from 'ember-concurrency';
|
||||
|
||||
export default ModalComponent.extend({
|
||||
|
||||
submitting: false,
|
||||
|
||||
tag: alias('model'),
|
||||
|
||||
postInflection: computed('tag.count.posts', function () {
|
||||
return this.get('tag.count.posts') > 1 ? 'posts' : 'post';
|
||||
}),
|
||||
|
||||
deleteTag: task(function* () {
|
||||
try {
|
||||
yield invokeAction(this, 'confirm');
|
||||
} finally {
|
||||
this.send('closeModal');
|
||||
}
|
||||
}).drop(),
|
||||
|
||||
actions: {
|
||||
confirm() {
|
||||
this.set('submitting', true);
|
||||
|
||||
invokeAction(this, 'confirm').finally(() => {
|
||||
this.send('closeModal');
|
||||
});
|
||||
this.get('deleteTag').perform();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,21 +1,24 @@
|
||||
import ModalComponent from 'ghost-admin/components/modals/base';
|
||||
import {alias} from 'ember-computed';
|
||||
import {invokeAction} from 'ember-invoke-action';
|
||||
import {task} from 'ember-concurrency';
|
||||
|
||||
export default ModalComponent.extend({
|
||||
|
||||
submitting: false,
|
||||
|
||||
theme: alias('model.theme'),
|
||||
download: alias('model.download'),
|
||||
|
||||
deleteTheme: task(function* () {
|
||||
try {
|
||||
yield invokeAction(this, 'confirm');
|
||||
} finally {
|
||||
this.send('closeModal');
|
||||
}
|
||||
}).drop(),
|
||||
|
||||
actions: {
|
||||
confirm() {
|
||||
this.set('submitting', true);
|
||||
|
||||
invokeAction(this, 'confirm').finally(() => {
|
||||
this.send('closeModal');
|
||||
});
|
||||
this.get('deleteTheme').perform();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,20 +1,23 @@
|
||||
import ModalComponent from 'ghost-admin/components/modals/base';
|
||||
import {invokeAction} from 'ember-invoke-action';
|
||||
import {alias} from 'ember-computed';
|
||||
import {task} from 'ember-concurrency';
|
||||
|
||||
export default ModalComponent.extend({
|
||||
|
||||
submitting: false,
|
||||
|
||||
user: alias('model'),
|
||||
|
||||
deleteUser: task(function* () {
|
||||
try {
|
||||
yield invokeAction(this, 'confirm');
|
||||
} finally {
|
||||
this.send('closeModal');
|
||||
}
|
||||
}).drop(),
|
||||
|
||||
actions: {
|
||||
confirm() {
|
||||
this.set('submitting', true);
|
||||
|
||||
invokeAction(this, 'confirm').finally(() => {
|
||||
this.send('closeModal');
|
||||
});
|
||||
this.get('deleteUser').perform();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -4,6 +4,7 @@ import {A as emberA} from 'ember-array/utils';
|
||||
import run from 'ember-runloop';
|
||||
import ModalComponent from 'ghost-admin/components/modals/base';
|
||||
import ValidationEngine from 'ghost-admin/mixins/validation-engine';
|
||||
import {task} from 'ember-concurrency';
|
||||
|
||||
const {Promise} = RSVP;
|
||||
|
||||
@ -13,7 +14,6 @@ export default ModalComponent.extend(ValidationEngine, {
|
||||
role: null,
|
||||
roles: null,
|
||||
authorRole: null,
|
||||
submitting: false,
|
||||
|
||||
validationType: 'inviteUser',
|
||||
|
||||
@ -85,42 +85,49 @@ export default ModalComponent.extend(ValidationEngine, {
|
||||
});
|
||||
},
|
||||
|
||||
sendInvitation: task(function* () {
|
||||
let email = this.get('email');
|
||||
let role = this.get('role');
|
||||
let notifications = this.get('notifications');
|
||||
let notificationText = `Invitation sent! (${email})`;
|
||||
let invite;
|
||||
|
||||
try {
|
||||
yield this.validate();
|
||||
|
||||
invite = this.get('store').createRecord('invite', {
|
||||
email,
|
||||
role
|
||||
});
|
||||
|
||||
yield invite.save();
|
||||
|
||||
// If sending the invitation email fails, the API will still return a status of 201
|
||||
// but the invite's status in the response object will be 'invited-pending'.
|
||||
if (invite.get('status') === 'pending') {
|
||||
notifications.showAlert('Invitation email was not sent. Please try resending.', {type: 'error', key: 'invite.send.failed'});
|
||||
} else {
|
||||
notifications.showNotification(notificationText, {key: 'invite.send.success'});
|
||||
}
|
||||
|
||||
this.send('closeModal');
|
||||
} catch (error) {
|
||||
// validation will reject and cause this to be called with no error
|
||||
if (error) {
|
||||
invite.deleteRecord();
|
||||
notifications.showAPIError(error, {key: 'invite.send'});
|
||||
this.send('closeModal');
|
||||
}
|
||||
}
|
||||
}).drop(),
|
||||
|
||||
actions: {
|
||||
setRole(role) {
|
||||
this.set('role', role);
|
||||
},
|
||||
|
||||
confirm() {
|
||||
let email = this.get('email');
|
||||
let role = this.get('role');
|
||||
let notifications = this.get('notifications');
|
||||
let invite;
|
||||
|
||||
this.validate().then(() => {
|
||||
this.set('submitting', true);
|
||||
|
||||
invite = this.get('store').createRecord('invite', {
|
||||
email,
|
||||
role
|
||||
});
|
||||
|
||||
invite.save().then(() => {
|
||||
let notificationText = `Invitation sent! (${email})`;
|
||||
|
||||
// If sending the invitation email fails, the API will still return a status of 201
|
||||
// but the invite's status in the response object will be 'invited-pending'.
|
||||
if (invite.get('status') === 'pending') {
|
||||
notifications.showAlert('Invitation email was not sent. Please try resending.', {type: 'error', key: 'invite.send.failed'});
|
||||
} else {
|
||||
notifications.showNotification(notificationText, {key: 'invite.send.success'});
|
||||
}
|
||||
}).catch((error) => {
|
||||
invite.deleteRecord();
|
||||
notifications.showAPIError(error, {key: 'invite.send'});
|
||||
}).finally(() => {
|
||||
this.send('closeModal');
|
||||
});
|
||||
});
|
||||
this.get('sendInvitation').perform();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,8 +1,35 @@
|
||||
import {A as emberA} from 'ember-array/utils';
|
||||
import ModalComponent from 'ghost-admin/components/modals/base';
|
||||
import {isInvalidError} from 'ember-ajax/errors';
|
||||
import {task} from 'ember-concurrency';
|
||||
|
||||
export default ModalComponent.extend({
|
||||
|
||||
addSubscriber: task(function* () {
|
||||
try {
|
||||
yield this.get('confirm')();
|
||||
this.send('closeModal');
|
||||
} catch (error) {
|
||||
// TODO: server-side validation errors should be serialized
|
||||
// properly so that errors are added to the model's errors
|
||||
// property
|
||||
if (error && isInvalidError(error)) {
|
||||
let [firstError] = error.errors;
|
||||
let {message} = firstError;
|
||||
|
||||
if (message && message.match(/email/i)) {
|
||||
this.get('model.errors').add('email', message);
|
||||
this.get('model.hasValidated').pushObject('email');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// this is a route action so it should bubble up to the global
|
||||
// error handler
|
||||
throw error;
|
||||
}
|
||||
}).drop(),
|
||||
|
||||
actions: {
|
||||
updateEmail(newEmail) {
|
||||
this.set('model.email', newEmail);
|
||||
@ -11,35 +38,7 @@ export default ModalComponent.extend({
|
||||
},
|
||||
|
||||
confirm() {
|
||||
let confirmAction = this.get('confirm');
|
||||
|
||||
this.set('submitting', true);
|
||||
|
||||
confirmAction().then(() => {
|
||||
this.send('closeModal');
|
||||
}).catch((error) => {
|
||||
// TODO: server-side validation errors should be serialized
|
||||
// properly so that errors are added to the model's errors
|
||||
// property
|
||||
if (error && isInvalidError(error)) {
|
||||
let [firstError] = error.errors;
|
||||
let {message} = firstError;
|
||||
|
||||
if (message && message.match(/email/i)) {
|
||||
this.get('model.errors').add('email', message);
|
||||
this.get('model.hasValidated').pushObject('email');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// this is a route action so it should bubble up to the global
|
||||
// error handler
|
||||
throw error;
|
||||
}).finally(() => {
|
||||
if (!this.get('isDestroying') && !this.get('isDestroyed')) {
|
||||
this.set('submitting', false);
|
||||
}
|
||||
});
|
||||
this.get('addSubscriber').perform();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -5,11 +5,11 @@ import {htmlSafe} from 'ember-string';
|
||||
import ModalComponent from 'ghost-admin/components/modals/base';
|
||||
import ValidationEngine from 'ghost-admin/mixins/validation-engine';
|
||||
import {isVersionMismatchError} from 'ghost-admin/services/ajax';
|
||||
import {task} from 'ember-concurrency';
|
||||
|
||||
export default ModalComponent.extend(ValidationEngine, {
|
||||
validationType: 'signin',
|
||||
|
||||
submitting: false,
|
||||
authenticationError: null,
|
||||
|
||||
config: injectService(),
|
||||
@ -44,7 +44,7 @@ export default ModalComponent.extend(ValidationEngine, {
|
||||
|
||||
this.set('authenticationError', null);
|
||||
|
||||
this.validate({property: 'signin'}).then(() => {
|
||||
return this.validate({property: 'signin'}).then(() => {
|
||||
this._authenticate().then(() => {
|
||||
this.get('notifications').closeAlerts();
|
||||
this.send('closeModal');
|
||||
@ -74,7 +74,7 @@ export default ModalComponent.extend(ValidationEngine, {
|
||||
this.toggleProperty('submitting');
|
||||
this.set('authenticationError', '');
|
||||
|
||||
this.get('torii')
|
||||
return this.get('torii')
|
||||
.open('ghost-oauth2', {type: 'signin'})
|
||||
.then((authentication) => {
|
||||
this.get('session').set('skipAuthSuccessHandler', true);
|
||||
@ -93,13 +93,17 @@ export default ModalComponent.extend(ValidationEngine, {
|
||||
});
|
||||
},
|
||||
|
||||
reauthenticate: task(function* () {
|
||||
if (this.get('config.ghostOAuth')) {
|
||||
yield this._oauthConfirm();
|
||||
} else {
|
||||
yield this._passwordConfirm();
|
||||
}
|
||||
}).drop(),
|
||||
|
||||
actions: {
|
||||
confirm() {
|
||||
if (this.get('config.ghostOAuth')) {
|
||||
return this._oauthConfirm();
|
||||
} else {
|
||||
return this._passwordConfirm();
|
||||
}
|
||||
this.get('reauthenticate').perform();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,17 +1,21 @@
|
||||
import ModalComponent from 'ghost-admin/components/modals/base';
|
||||
import {invokeAction} from 'ember-invoke-action';
|
||||
import {task} from 'ember-concurrency';
|
||||
|
||||
export default ModalComponent.extend({
|
||||
user: null,
|
||||
submitting: false,
|
||||
|
||||
transferOwnership: task(function* () {
|
||||
try {
|
||||
yield invokeAction(this, 'confirm');
|
||||
} finally {
|
||||
this.send('closeModal');
|
||||
}
|
||||
}).drop(),
|
||||
|
||||
actions: {
|
||||
confirm() {
|
||||
this.set('submitting', true);
|
||||
|
||||
invokeAction(this, 'confirm').finally(() => {
|
||||
this.send('closeModal');
|
||||
});
|
||||
this.get('transferOwnership').perform();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -3,10 +3,10 @@ import injectService from 'ember-service/inject';
|
||||
import {isEmpty} from 'ember-utils';
|
||||
import ModalComponent from 'ghost-admin/components/modals/base';
|
||||
import cajaSanitizers from 'ghost-admin/utils/caja-sanitizers';
|
||||
import {task} from 'ember-concurrency';
|
||||
|
||||
export default ModalComponent.extend({
|
||||
model: null,
|
||||
submitting: false,
|
||||
|
||||
url: '',
|
||||
newUrl: '',
|
||||
@ -65,6 +65,25 @@ export default ModalComponent.extend({
|
||||
},
|
||||
// end validation
|
||||
|
||||
uploadImage: task(function* () {
|
||||
let model = this.get('model.model');
|
||||
let newUrl = this.get('newUrl');
|
||||
let result = this._validateUrl(newUrl);
|
||||
let notifications = this.get('notifications');
|
||||
|
||||
if (result === true) {
|
||||
this.set('image', newUrl);
|
||||
|
||||
try {
|
||||
yield model.save();
|
||||
} catch (e) {
|
||||
notifications.showAPIError(e, {key: 'image.upload'});
|
||||
} finally {
|
||||
this.send('closeModal');
|
||||
}
|
||||
}
|
||||
}).drop(),
|
||||
|
||||
actions: {
|
||||
fileUploaded(url) {
|
||||
this.set('url', url);
|
||||
@ -77,21 +96,7 @@ export default ModalComponent.extend({
|
||||
},
|
||||
|
||||
confirm() {
|
||||
let model = this.get('model.model');
|
||||
let newUrl = this.get('newUrl');
|
||||
let result = this._validateUrl(newUrl);
|
||||
let notifications = this.get('notifications');
|
||||
|
||||
if (result === true) {
|
||||
this.set('submitting', true);
|
||||
this.set('image', newUrl);
|
||||
|
||||
model.save().catch((err) => {
|
||||
notifications.showAPIError(err, {key: 'image.upload'});
|
||||
}).finally(() => {
|
||||
this.send('closeModal');
|
||||
});
|
||||
}
|
||||
this.get('uploadImage').perform();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -9,5 +9,5 @@
|
||||
|
||||
<div class="modal-footer">
|
||||
<button {{action "closeModal"}} class="btn btn-default btn-minor">Cancel</button>
|
||||
{{#gh-spin-button action="confirm" class="btn btn-red" submitting=submitting}}Delete{{/gh-spin-button}}
|
||||
</div>
|
||||
{{#gh-task-button task=deleteAll class="btn btn-red"}}Delete{{/gh-task-button}}
|
||||
</div>
|
@ -13,5 +13,5 @@
|
||||
|
||||
<div class="modal-footer">
|
||||
<button {{action "closeModal"}} class="btn btn-default btn-minor">Cancel</button>
|
||||
{{#gh-spin-button action="confirm" class="btn btn-red" submitting=submitting}}Delete{{/gh-spin-button}}
|
||||
</div>
|
||||
{{#gh-task-button task=deletePost class="btn btn-red"}}Delete{{/gh-task-button}}
|
||||
</div>
|
@ -9,5 +9,5 @@
|
||||
|
||||
<div class="modal-footer">
|
||||
<button {{action "closeModal"}} class="btn btn-default btn-minor">Cancel</button>
|
||||
{{#gh-spin-button action="confirm" class="btn btn-red" submitting=submitting}}Delete{{/gh-spin-button}}
|
||||
</div>
|
||||
{{#gh-task-button task=deleteSubscriber class="btn btn-red"}}Delete{{/gh-task-button}}
|
||||
</div>
|
@ -13,5 +13,5 @@
|
||||
|
||||
<div class="modal-footer">
|
||||
<button {{action "closeModal"}} class="btn btn-default btn-minor">Cancel</button>
|
||||
{{#gh-spin-button action="confirm" class="btn btn-red" submitting=submitting}}Delete{{/gh-spin-button}}
|
||||
</div>
|
||||
{{#gh-task-button task=deleteTag class="btn btn-red"}}Delete{{/gh-task-button}}
|
||||
</div>
|
@ -16,5 +16,5 @@
|
||||
|
||||
<div class="modal-footer">
|
||||
<button {{action "closeModal"}} class="btn btn-default btn-minor">Cancel</button>
|
||||
{{#gh-spin-button action="confirm" class="btn btn-red" submitting=submitting}}Delete{{/gh-spin-button}}
|
||||
</div>
|
||||
{{#gh-task-button task=deleteTheme class="btn btn-red"}}Delete{{/gh-task-button}}
|
||||
</div>
|
@ -13,5 +13,5 @@
|
||||
|
||||
<div class="modal-footer">
|
||||
<button {{action "closeModal"}} class="btn btn-default btn-minor">Cancel</button>
|
||||
{{#gh-spin-button action="confirm" class="btn btn-red" submitting=submitting}}Delete{{/gh-spin-button}}
|
||||
</div>
|
||||
{{#gh-task-button task=deleteUser class="btn btn-red"}}Delete{{/gh-task-button}}
|
||||
</div>
|
@ -42,5 +42,5 @@
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
{{#gh-spin-button action="confirm" class="btn btn-green" submitting=submitting}}Send invitation now{{/gh-spin-button}}
|
||||
</div>
|
||||
{{#gh-task-button task=sendInvitation class="btn btn-green"}}Send invitation now{{/gh-task-button}}
|
||||
</div>
|
@ -25,5 +25,5 @@
|
||||
|
||||
<div class="modal-footer">
|
||||
<button {{action "closeModal"}} class="btn btn-default btn-minor">Cancel</button>
|
||||
{{#gh-spin-button action="confirm" class="btn btn-green" submitting=submitting}}Add{{/gh-spin-button}}
|
||||
</div>
|
||||
{{#gh-task-button task=addSubscriber class="btn btn-green"}}Add{{/gh-task-button}}
|
||||
</div>
|
@ -6,17 +6,17 @@
|
||||
<div class="modal-body {{if authenticationError 'error'}}">
|
||||
|
||||
{{#if config.ghostOAuth}}
|
||||
{{#gh-spin-button class="login btn btn-blue btn-block" type="submit" action="confirm" tabindex="3" submitting=loggingIn autoWidth="false"}}Sign in with Ghost{{/gh-spin-button}}
|
||||
{{#gh-task-button task=reauthenticate class="login btn btn-blue btn-block" tabindex="3" autoWidth="false"}}Sign in with Ghost{{/gh-task-button}}
|
||||
{{else}}
|
||||
<form id="login" class="login-form" method="post" novalidate="novalidate" {{action "confirm" on="submit"}}>
|
||||
{{#gh-validation-status-container class="password-wrap" errors=errors property="password" hasValidated=hasValidated}}
|
||||
{{gh-input password class="password" type="password" placeholder="Password" name="password" update=(action (mut password))}}
|
||||
{{/gh-validation-status-container}}
|
||||
{{#gh-spin-button class="btn btn-blue" type="submit" submitting=submitting}}Log in{{/gh-spin-button}}
|
||||
{{#gh-task-button task=reauthenticate class="btn btn-blue" type="submit"}}Log in{{/gh-task-button}}
|
||||
</form>
|
||||
{{/if}}
|
||||
|
||||
{{#if authenticationError}}
|
||||
<p class="response">{{authenticationError}}</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
@ -12,5 +12,5 @@
|
||||
|
||||
<div class="modal-footer">
|
||||
<button {{action "closeModal"}} class="btn btn-default btn-minor">Cancel</button>
|
||||
{{#gh-spin-button action="confirm" class="btn btn-red" submitting=submitting}}Yep - I'm sure{{/gh-spin-button}}
|
||||
</div>
|
||||
{{#gh-task-button task=transferOwnership class="btn btn-red"}}Yep - I'm sure{{/gh-task-button}}
|
||||
</div>
|
@ -18,5 +18,5 @@
|
||||
|
||||
<div class="modal-footer">
|
||||
<button {{action "closeModal"}} class="btn btn-default btn-minor">Cancel</button>
|
||||
{{#gh-spin-button action="confirm" class="btn btn-blue right js-button-accept" submitting=submitting}}Save{{/gh-spin-button}}
|
||||
</div>
|
||||
{{#gh-task-button task=uploadImage class="btn btn-blue right js-button-accept"}}Save{{/gh-task-button}}
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user