mirror of
https://github.com/discourse/discourse.git
synced 2024-11-25 02:11:08 -06:00
DEV: Migrate publish page modal to Glimmer and DModal (#22663)
This PR migrates the publish page modal to a Glimmer component and DModal. Most of the code is lift-and-shift. However, the component state getters were implemented using meta-programming in the original controller. They have all been inlined here for clarity, searchability, etc.
This commit is contained in:
parent
20ec7ac174
commit
c2e90f8c07
@ -0,0 +1,105 @@
|
||||
<DModal
|
||||
@closeModal={{@closeModal}}
|
||||
@title={{i18n "topic.publish_page.title"}}
|
||||
class="publish-page-modal"
|
||||
>
|
||||
<:body>
|
||||
{{#if this.unpublished}}
|
||||
<p>{{i18n "topic.publish_page.unpublished"}}</p>
|
||||
{{else}}
|
||||
<ConditionalLoadingSpinner @condition={{this.initializing}}>
|
||||
<p class="publish-description">{{i18n
|
||||
"topic.publish_page.description"
|
||||
}}</p>
|
||||
|
||||
<form>
|
||||
<div class="controls">
|
||||
<label>{{i18n "topic.publish_page.slug"}}</label>
|
||||
<TextField
|
||||
@value={{this.publishedPage.slug}}
|
||||
@onChange={{this.checkSlug}}
|
||||
@onChangeImmediate={{this.startCheckSlug}}
|
||||
@disabled={{this.existing}}
|
||||
@class="publish-slug"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<label>{{i18n "topic.publish_page.public"}}</label>
|
||||
|
||||
<p class="description">
|
||||
<Input
|
||||
@type="checkbox"
|
||||
@checked={{readonly this.publishedPage.public}}
|
||||
{{on "click" this.onChangePublic}}
|
||||
/>
|
||||
{{i18n "topic.publish_page.public_description"}}
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="publish-url">
|
||||
<ConditionalLoadingSpinner @condition={{this.checking}} />
|
||||
|
||||
{{#if this.existing}}
|
||||
<div class="current-url">
|
||||
{{i18n "topic.publish_page.publish_url"}}
|
||||
<div>
|
||||
<a
|
||||
href={{this.publishedPage.url}}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>{{this.publishedPage.url}}</a>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
{{#if this.showUrl}}
|
||||
<div class="valid-slug">
|
||||
{{i18n "topic.publish_page.preview_url"}}
|
||||
<div class="example-url">{{this.publishedPage.url}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.invalid}}
|
||||
{{i18n "topic.publish_page.invalid_slug"}}
|
||||
<span class="invalid-slug">{{this.reason}}.</span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
</ConditionalLoadingSpinner>
|
||||
{{/if}}
|
||||
</:body>
|
||||
<:footer>
|
||||
{{#if this.showUnpublish}}
|
||||
<DButton
|
||||
@label="topic.publish_page.unpublish"
|
||||
@icon="trash-alt"
|
||||
@class="btn-danger"
|
||||
@isLoading={{this.unpublishing}}
|
||||
@action={{this.unpublish}}
|
||||
/>
|
||||
|
||||
<DButton
|
||||
@class="close-publish-page"
|
||||
@icon="times"
|
||||
@label="close"
|
||||
@action={{@closeModal}}
|
||||
/>
|
||||
{{else if this.unpublished}}
|
||||
<DButton
|
||||
@label="topic.publish_page.publishing_settings"
|
||||
@action={{this.startNew}}
|
||||
/>
|
||||
{{else}}
|
||||
<DButton
|
||||
@label="topic.publish_page.publish"
|
||||
@class="btn-primary publish-page"
|
||||
@icon="file"
|
||||
@disabled={{this.disabled}}
|
||||
@isLoading={{this.saving}}
|
||||
@action={{this.publish}}
|
||||
/>
|
||||
{{/if}}
|
||||
</:footer>
|
||||
</DModal>
|
@ -0,0 +1,165 @@
|
||||
import { action } from "@ember/object";
|
||||
import { inject as service } from "@ember/service";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import Component from "@glimmer/component";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
||||
const States = {
|
||||
initializing: "initializing",
|
||||
checking: "checking",
|
||||
valid: "valid",
|
||||
invalid: "invalid",
|
||||
saving: "saving",
|
||||
new: "new",
|
||||
existing: "existing",
|
||||
unpublishing: "unpublishing",
|
||||
unpublished: "unpublished",
|
||||
};
|
||||
|
||||
export default class PublishPageModal extends Component {
|
||||
@service store;
|
||||
|
||||
@tracked state = States.initializing;
|
||||
@tracked reason = null;
|
||||
@tracked publishedPage = null;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.store
|
||||
.find("published_page", this.args.model.id)
|
||||
.then((page) => {
|
||||
this.state = States.existing;
|
||||
this.publishedPage = page;
|
||||
})
|
||||
.catch(this.startNew);
|
||||
}
|
||||
|
||||
get initializing() {
|
||||
return this.state === States.initializing;
|
||||
}
|
||||
|
||||
get checking() {
|
||||
return this.state === States.checking;
|
||||
}
|
||||
|
||||
get valid() {
|
||||
return this.state === States.valid;
|
||||
}
|
||||
|
||||
get invalid() {
|
||||
return this.state === States.invalid;
|
||||
}
|
||||
|
||||
get saving() {
|
||||
return this.state === States.saving;
|
||||
}
|
||||
|
||||
get new() {
|
||||
return this.state === States.new;
|
||||
}
|
||||
|
||||
get existing() {
|
||||
return this.state === States.existing;
|
||||
}
|
||||
|
||||
get unpublishing() {
|
||||
return this.state === States.unpublishing;
|
||||
}
|
||||
|
||||
get unpublished() {
|
||||
return this.state === States.unpublished;
|
||||
}
|
||||
|
||||
get disabled() {
|
||||
return this.state !== States.valid;
|
||||
}
|
||||
|
||||
get showUrl() {
|
||||
return (
|
||||
this.state === States.valid ||
|
||||
this.state === States.saving ||
|
||||
this.state === States.existing
|
||||
);
|
||||
}
|
||||
|
||||
get showUnpublish() {
|
||||
return this.state === States.existing || this.state === States.unpublishing;
|
||||
}
|
||||
|
||||
@action
|
||||
startCheckSlug() {
|
||||
if (this.state === States.existing) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.state = States.checking;
|
||||
}
|
||||
|
||||
@action
|
||||
checkSlug() {
|
||||
if (this.state === States.existing) {
|
||||
return;
|
||||
}
|
||||
return ajax("/pub/check-slug", {
|
||||
data: { slug: this.publishedPage.slug },
|
||||
}).then((result) => {
|
||||
if (result.valid_slug) {
|
||||
this.state = States.valid;
|
||||
} else {
|
||||
this.state = States.invalid;
|
||||
this.reason = result.reason;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
unpublish() {
|
||||
this.state = States.unpublishing;
|
||||
return this.publishedPage
|
||||
.destroyRecord()
|
||||
.then(() => {
|
||||
this.state = States.unpublished;
|
||||
this.args.model.set("publishedPage", null);
|
||||
})
|
||||
.catch((result) => {
|
||||
this.state = States.existing;
|
||||
popupAjaxError(result);
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
publish() {
|
||||
this.state = States.saving;
|
||||
|
||||
return this.publishedPage
|
||||
.update(this.publishedPage.getProperties("slug", "public"))
|
||||
.then(() => {
|
||||
this.state = States.existing;
|
||||
this.args.model.set("publishedPage", this.publishedPage);
|
||||
})
|
||||
.catch((errResult) => {
|
||||
popupAjaxError(errResult);
|
||||
this.state = States.existing;
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
startNew() {
|
||||
this.state = States.new;
|
||||
this.publishedPage = this.store.createRecord(
|
||||
"published_page",
|
||||
this.args.model.getProperties("id", "slug", "public")
|
||||
);
|
||||
this.checkSlug();
|
||||
}
|
||||
|
||||
@action
|
||||
onChangePublic(event) {
|
||||
this.publishedPage.set("public", event.target.checked);
|
||||
|
||||
if (this.showUnpublish) {
|
||||
this.publish();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
import { action, computed } from "@ember/object";
|
||||
import { equal, not } from "@ember/object/computed";
|
||||
import Controller from "@ember/controller";
|
||||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
|
||||
const States = {
|
||||
initializing: "initializing",
|
||||
checking: "checking",
|
||||
valid: "valid",
|
||||
invalid: "invalid",
|
||||
saving: "saving",
|
||||
new: "new",
|
||||
existing: "existing",
|
||||
unpublishing: "unpublishing",
|
||||
unpublished: "unpublished",
|
||||
};
|
||||
|
||||
const StateHelpers = {};
|
||||
Object.keys(States).forEach((name) => {
|
||||
StateHelpers[name] = equal("state", name);
|
||||
});
|
||||
|
||||
export default Controller.extend(ModalFunctionality, StateHelpers, {
|
||||
state: null,
|
||||
reason: null,
|
||||
publishedPage: null,
|
||||
disabled: not("valid"),
|
||||
|
||||
showUrl: computed("state", function () {
|
||||
return (
|
||||
this.state === States.valid ||
|
||||
this.state === States.saving ||
|
||||
this.state === States.existing
|
||||
);
|
||||
}),
|
||||
|
||||
showUnpublish: computed("state", function () {
|
||||
return this.state === States.existing || this.state === States.unpublishing;
|
||||
}),
|
||||
|
||||
onShow() {
|
||||
this.set("state", States.initializing);
|
||||
|
||||
this.store
|
||||
.find("published_page", this.model.id)
|
||||
.then((page) => {
|
||||
this.setProperties({ state: States.existing, publishedPage: page });
|
||||
})
|
||||
.catch(this.startNew);
|
||||
},
|
||||
|
||||
@action
|
||||
startCheckSlug() {
|
||||
if (this.state === States.existing) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.set("state", States.checking);
|
||||
},
|
||||
|
||||
@action
|
||||
checkSlug() {
|
||||
if (this.state === States.existing) {
|
||||
return;
|
||||
}
|
||||
return ajax("/pub/check-slug", {
|
||||
data: { slug: this.publishedPage.slug },
|
||||
}).then((result) => {
|
||||
if (result.valid_slug) {
|
||||
this.set("state", States.valid);
|
||||
} else {
|
||||
this.setProperties({ state: States.invalid, reason: result.reason });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@action
|
||||
unpublish() {
|
||||
this.set("state", States.unpublishing);
|
||||
return this.publishedPage
|
||||
.destroyRecord()
|
||||
.then(() => {
|
||||
this.set("state", States.unpublished);
|
||||
this.model.set("publishedPage", null);
|
||||
})
|
||||
.catch((result) => {
|
||||
this.set("state", States.existing);
|
||||
popupAjaxError(result);
|
||||
});
|
||||
},
|
||||
|
||||
@action
|
||||
publish() {
|
||||
this.set("state", States.saving);
|
||||
|
||||
return this.publishedPage
|
||||
.update(this.publishedPage.getProperties("slug", "public"))
|
||||
.then(() => {
|
||||
this.set("state", States.existing);
|
||||
this.model.set("publishedPage", this.publishedPage);
|
||||
})
|
||||
.catch((errResult) => {
|
||||
popupAjaxError(errResult);
|
||||
this.set("state", States.existing);
|
||||
});
|
||||
},
|
||||
|
||||
@action
|
||||
startNew() {
|
||||
this.setProperties({
|
||||
state: States.new,
|
||||
publishedPage: this.store.createRecord(
|
||||
"published_page",
|
||||
this.model.getProperties("id", "slug", "public")
|
||||
),
|
||||
});
|
||||
this.checkSlug();
|
||||
},
|
||||
|
||||
@action
|
||||
onChangePublic(isPublic) {
|
||||
this.publishedPage.set("public", isPublic);
|
||||
|
||||
if (this.showUnpublish) {
|
||||
this.publish();
|
||||
}
|
||||
},
|
||||
});
|
@ -11,6 +11,7 @@ import showModal from "discourse/lib/show-modal";
|
||||
import TopicFlag from "discourse/lib/flag-targets/topic-flag";
|
||||
import PostFlag from "discourse/lib/flag-targets/post-flag";
|
||||
import HistoryModal from "discourse/components/modal/history";
|
||||
import PublishPageModal from "discourse/components/modal/publish-page";
|
||||
|
||||
const SCROLL_DELAY = 500;
|
||||
|
||||
@ -112,9 +113,8 @@ const TopicRoute = DiscourseRoute.extend({
|
||||
@action
|
||||
showPagePublish() {
|
||||
const model = this.modelFor("topic");
|
||||
showModal("publish-page", {
|
||||
this.modal.show(PublishPageModal, {
|
||||
model,
|
||||
title: "topic.publish_page.title",
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -1,100 +0,0 @@
|
||||
<DModalBody>
|
||||
{{#if this.unpublished}}
|
||||
<p>{{i18n "topic.publish_page.unpublished"}}</p>
|
||||
{{else}}
|
||||
<ConditionalLoadingSpinner @condition={{this.initializing}}>
|
||||
<p class="publish-description">{{i18n
|
||||
"topic.publish_page.description"
|
||||
}}</p>
|
||||
|
||||
<form>
|
||||
<div class="controls">
|
||||
<label>{{i18n "topic.publish_page.slug"}}</label>
|
||||
<TextField
|
||||
@value={{this.publishedPage.slug}}
|
||||
@onChange={{action "checkSlug"}}
|
||||
@onChangeImmediate={{action "startCheckSlug"}}
|
||||
@disabled={{this.existing}}
|
||||
@class="publish-slug"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<label>{{i18n "topic.publish_page.public"}}</label>
|
||||
|
||||
<p class="description">
|
||||
<Input
|
||||
@type="checkbox"
|
||||
@checked={{readonly this.publishedPage.public}}
|
||||
{{on "click" (action "onChangePublic" value="target.checked")}}
|
||||
/>
|
||||
{{i18n "topic.publish_page.public_description"}}
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="publish-url">
|
||||
<ConditionalLoadingSpinner @condition={{this.checking}} />
|
||||
|
||||
{{#if this.existing}}
|
||||
<div class="current-url">
|
||||
{{i18n "topic.publish_page.publish_url"}}
|
||||
<div>
|
||||
<a
|
||||
href={{this.publishedPage.url}}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>{{this.publishedPage.url}}</a>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
{{#if this.showUrl}}
|
||||
<div class="valid-slug">
|
||||
{{i18n "topic.publish_page.preview_url"}}
|
||||
<div class="example-url">{{this.publishedPage.url}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.invalid}}
|
||||
{{i18n "topic.publish_page.invalid_slug"}}
|
||||
<span class="invalid-slug">{{this.reason}}.</span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
</ConditionalLoadingSpinner>
|
||||
{{/if}}
|
||||
</DModalBody>
|
||||
|
||||
<div class="modal-footer">
|
||||
{{#if this.showUnpublish}}
|
||||
<DButton
|
||||
@label="topic.publish_page.unpublish"
|
||||
@icon="trash-alt"
|
||||
@class="btn-danger"
|
||||
@isLoading={{this.unpublishing}}
|
||||
@action={{action "unpublish"}}
|
||||
/>
|
||||
|
||||
<DButton
|
||||
@class="close-publish-page"
|
||||
@icon="times"
|
||||
@label="close"
|
||||
@action={{action "closeModal"}}
|
||||
/>
|
||||
{{else if this.unpublished}}
|
||||
<DButton
|
||||
@label="topic.publish_page.publishing_settings"
|
||||
@action={{action "startNew"}}
|
||||
/>
|
||||
{{else}}
|
||||
<DButton
|
||||
@label="topic.publish_page.publish"
|
||||
@class="btn-primary publish-page"
|
||||
@icon="file"
|
||||
@disabled={{this.disabled}}
|
||||
@isLoading={{this.saving}}
|
||||
@action={{action "publish"}}
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user