* Add SiteConfig.feed_pinned_article and validation * Display pinned article at the top of feed * Add (basic) functionality to pin/unpin post * Admins can pin other users posts as well * Hide the button if looking at the non pinned post * Add pinned/unpinned snackbar message * Rename SiteConfig usage to Settings::General * Add pinned article to the Admin articles index * Show the pin post button when there's no pinned article * Move pinning to a separate controller * Fix SiteConfig reference * Hide PinController actions to unauthorized users * PinnedArticlesController#show action and refactor some of the code * Add Modal interaction * Fix modal-pinned checkbox interaction * Fixed pin/unpin post * Add ArticleDecorator#pinned? specs * Add PinnedArticlePolicy and PinnedArticlesController specs * Add ability to actually pin an article from the admin after submit * Add partial Cypress pin/unpin spec * Fix pinned article and add basic Cypress interaction tests * Add Crayons styling to modal * Only render the pinned article on the default Feed page * Use persisted? * Add some comments * Update app/javascript/articles/Article.jsx Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Update app/javascript/packs/homePageFeed.jsx Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Fix Cypress tests * Update app/javascript/admin/controllers/article_controller.js Co-authored-by: Ridhwana <Ridhwana.Khan16@gmail.com> * Fix pinning in article show page * Used PinnedArticle domain model * Fix spec * Update cypress/integration/adminFlows/articles/pinArticle.spec.js Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Update cypress/integration/adminFlows/articles/pinArticle.spec.js Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Update cypress/integration/adminFlows/articles/pinArticle.spec.js Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Update cypress/integration/adminFlows/articles/pinArticle.spec.js Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Update cypress/integration/adminFlows/articles/pinArticle.spec.js Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Update cypress/integration/articleFlows/pinArticle.spec.js Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Update cypress/integration/articleFlows/pinArticle.spec.js Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Update app/views/admin/articles/index.html.erb Co-authored-by: Suzanne Aitchison <suzanne@forem.com> * Fix merge woes * Add missing article pin post flows * Add missing admin article flows * Add Unpin to Admin as well * Add Audit::Log entries for pin/unpin actions * Update app/controllers/stories/feeds_controller.rb Co-authored-by: Michael Kohl <citizen428@dev.to> * Do not rate limit in E2E tests * Use .find instead of .filter * Rename ArticleIdValidator to ExistingArticleIdValidator * Treat draft and deleted articles the same * Make sure posts can be pinned after the pinned article is unpublished or deleted * Use .get directly * Fix spec and fix PinnedArticlesController#show * Strengthen pinArticle Cypress tests * Add Cypress test heading guard * Add another Cypress test heading guard * Remove duplicate validator * Try using the Tools: header instead of the article title Co-authored-by: Suzanne Aitchison <suzanne@forem.com> Co-authored-by: Ridhwana <Ridhwana.Khan16@gmail.com> Co-authored-by: Michael Kohl <citizen428@dev.to>
104 lines
3.2 KiB
JavaScript
104 lines
3.2 KiB
JavaScript
import { Controller } from 'stimulus';
|
|
|
|
export default class ArticleController extends Controller {
|
|
static classes = ['bgHighlighted', 'borderHighlighted'];
|
|
static targets = [
|
|
'featuredNumber',
|
|
'cardBody',
|
|
'pinnedCheckbox',
|
|
'unpinButton',
|
|
];
|
|
static values = { id: Number, pinPath: String };
|
|
|
|
increaseFeaturedNumber() {
|
|
// Increases the article's chances of being seen
|
|
const seconds = new Date().getTime() / 1000;
|
|
this.featuredNumberTarget.value = Math.round(seconds) + 300;
|
|
}
|
|
|
|
decreaseFeaturedNumber() {
|
|
// Decreases the article's chances of being seen
|
|
const seconds = new Date().getTime() / 1080;
|
|
this.featuredNumberTarget.value = Math.round(seconds);
|
|
}
|
|
|
|
highlightElement() {
|
|
const card = this.cardBodyTarget;
|
|
|
|
card.classList.add(this.bgHighlightedClass, this.borderHighlightedClass);
|
|
|
|
setTimeout(() => {
|
|
card.classList.remove(this.bgHighlightedClass);
|
|
}, 350);
|
|
}
|
|
|
|
togglePin(event) {
|
|
const checkbox = event.target;
|
|
|
|
// we're only interested in intercepting a checkbox going from
|
|
// unchecked to checked
|
|
if (!checkbox.checked) {
|
|
return;
|
|
}
|
|
|
|
// by preventing the default, we avoid visually selecting the checkbox,
|
|
// it will be responsibility of `pinArticle()` to determine if and when
|
|
// the checkbox state has to change
|
|
event.preventDefault();
|
|
|
|
this.pinArticle(checkbox);
|
|
}
|
|
|
|
async pinArticle(checkbox) {
|
|
const response = await fetch(this.pinPathValue, {
|
|
method: 'GET',
|
|
headers: {
|
|
'X-CSRF-Token': document.querySelector("meta[name='csrf-token']")
|
|
?.content,
|
|
},
|
|
credentials: 'same-origin',
|
|
});
|
|
|
|
if (response.ok) {
|
|
const pinnedArticle = await response.json();
|
|
|
|
// only show the modal if we're not re-pinning the current pin
|
|
if (pinnedArticle.id !== this.idValue) {
|
|
// By dispatching this custom event, we communicate with
|
|
// `ArticlePinnedModalController`, responsible to display the modal and
|
|
// determine the final state of the checkbox, depending on which action
|
|
// the user follows up with (ie. confirming the pin or dismissing)
|
|
// This technique is a good way to separate behavior and have Stimulus
|
|
// controllers talk with each other.
|
|
// See https://fullstackheroes.com/stimulusjs/create-custom-events/
|
|
document.dispatchEvent(
|
|
new CustomEvent('article-pinned-modal:open', {
|
|
detail: {
|
|
article: pinnedArticle,
|
|
checkboxId: this.pinnedCheckboxTarget.getAttribute('id'),
|
|
},
|
|
}),
|
|
);
|
|
} else {
|
|
checkbox.checked = true;
|
|
}
|
|
} else if (response.status === 404) {
|
|
// if there is no pinned article, it means we can go ahead and pin this one
|
|
checkbox.checked = true;
|
|
}
|
|
}
|
|
|
|
ajaxSuccess(event) {
|
|
if (event.target !== this.unpinButtonTarget) {
|
|
return;
|
|
}
|
|
|
|
// Replace the current Article HTML with the HTML sent by the server
|
|
const newArticle = document.createElement('div');
|
|
|
|
const [, , xhr] = event.detail;
|
|
newArticle.innerHTML = xhr.responseText;
|
|
|
|
this.element.innerHTML = newArticle.querySelector('.card').innerHTML;
|
|
}
|
|
}
|