diff --git a/app/assets/javascripts/initializePage.js.erb b/app/assets/javascripts/initializePage.js.erb index f5e91a7ed97cb..a0c5f0434531a 100644 --- a/app/assets/javascripts/initializePage.js.erb +++ b/app/assets/javascripts/initializePage.js.erb @@ -42,6 +42,8 @@ function callInitalizers(){ initializeTimeFixer(); initializeDashboardSort(); initializePWAFunctionality(); + initializeEllipsisMenu(); + initializeArchivedPostFilter(); if (!initializeLiveArticle.called){ initializeLiveArticle(); diff --git a/app/assets/javascripts/initializers/initializeArchivedPostFilter.js b/app/assets/javascripts/initializers/initializeArchivedPostFilter.js new file mode 100644 index 0000000000000..788c2ca8ff76c --- /dev/null +++ b/app/assets/javascripts/initializers/initializeArchivedPostFilter.js @@ -0,0 +1,37 @@ +function archivedPosts() { + return document.getElementsByClassName('single-article-archived'); +} + +function showArchivedPosts() { + var posts = archivedPosts(); + + for (var i = 0; i < posts.length; i += 1) { + posts[i].classList.remove('hidden'); + } +} + +function hideArchivedPosts() { + var posts = archivedPosts(); + + for (var i = 0; i < posts.length; i += 1) { + posts[i].classList.add('hidden'); + } +} + +function toggleArchivedPosts(e) { + var link = e.target; + + if (link.innerHTML.match(/Show/)) { + link.innerHTML = 'Hide Archived'; + showArchivedPosts(); + } else { + link.innerHTML = 'Show Archived'; + hideArchivedPosts(); + } +} + +function initializeArchivedPostFilter() { + var link = document.getElementById('toggleArchivedLink'); + + link.addEventListener('click', toggleArchivedPosts); +} diff --git a/app/assets/javascripts/initializers/initializeEllipsisMenu.js b/app/assets/javascripts/initializers/initializeEllipsisMenu.js new file mode 100644 index 0000000000000..7363504b9f7c1 --- /dev/null +++ b/app/assets/javascripts/initializers/initializeEllipsisMenu.js @@ -0,0 +1,150 @@ +// SUBMITTING FORM // + +function getFormValues(form) { + var articleId = form.action.match(/\/(\d+)$/)[1]; + var inputs = form.querySelectorAll('input'); + var formData = { id: articleId, article: {} }; + + for (var i = 0; i < inputs.length; i += 1) { + var input = inputs[i]; + var name = input.getAttribute('name'); + var value = input.getAttribute('value'); + + if (name.match(/\[(.*)\]/)) { + var key = name.match(/\[(.*)\]$/)[1]; + formData.article[key] = value; + } else { + formData[name] = value; + } + } + + return formData; +} + +function toggleArchived(article, needsArchived) { + if (needsArchived === 'true') { + article.classList.add('single-article-archived', 'hidden'); + } else { + article.classList.remove('single-article-archived'); + } +} + +function toggleNotifications(submit, action) { + if (action === 'Mute Notifications') { + submit.setAttribute('value', 'Receive Notifications'); + } else { + submit.setAttribute('value', 'Mute Notifications'); + } +} + +function onXhrSuccess(form, article, values) { + if (values.article.archived) { + toggleArchived(article, values.article.archived); + } else { + var submit = form.querySelector('input[type="submit"]'); + var submitValue = submit.getAttribute('value'); + + toggleNotifications(submit, submitValue); + } + + article.querySelector('ul.ellipsis-menu').classList.add('hidden'); +} + +function handleFormSubmit(e) { + e.preventDefault(); + e.stopPropagation(); + + var form = e.target; + var values = getFormValues(form); + var data = JSON.stringify(values); + + var xhr = new XMLHttpRequest(); + xhr.open('PATCH', form.action); + xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.send(data); + + xhr.onload = function() { + var article = form.closest('div.single-article'); + + if (xhr.status === 200) { + onXhrSuccess(form, article, values); + } else { + article.querySelector('.dashboard-meta-details').innerHTML = + 'Failed to update article.'; + } + }; +} + +function initializeFormSubmit() { + var forms = document.querySelectorAll('ul.ellipsis-menu > li > form'); + + for (var i = 0; i < forms.length; i += 1) { + forms[i].addEventListener('submit', handleFormSubmit); + } +} + +// TOGGLING MENU // + +function getMenu(el) { + var parentDiv = el.closest('div.ellipsis-menu'); + var menu = parentDiv.querySelector('ul.ellipsis-menu'); + return menu; +} + +function hideIfNotAlreadyHidden(menu) { + if (!menu.classList.contains('hidden')) { + menu.classList.add('hidden'); + } +} + +function hideAllEllipsisMenusExcept(menu) { + var menus = document.querySelectorAll('ul.ellipsis-menu'); + + for (var i = 0; i < menus.length; i += 1) { + if (menus[i] !== menu) { + hideIfNotAlreadyHidden(menus[i]); + } + } +} + +function hideEllipsisMenus(e) { + if (!e.target.closest('div.ellipsis-menu')) { + var menus = document.querySelectorAll('ul.ellipsis-menu'); + + for (var i = 0; i < menus.length; i += 1) { + hideIfNotAlreadyHidden(menus[i]); + } + } +} + +function toggleEllipsisMenu(e) { + var menu = getMenu(e.target); + + // Make sure other ellipsis menus close when a new one + // is opened + hideAllEllipsisMenusExcept(menu); + + if (menu.classList.contains('hidden')) { + menu.classList.remove('hidden'); + } else { + menu.classList.add('hidden'); + } +} + +function initializeEllipsisMenuToggle() { + var buttons = document.getElementsByClassName('ellipsis-menu-btn'); + + for (var i = 0; i < buttons.length; i += 1) { + buttons[i].addEventListener('click', toggleEllipsisMenu); + } + + // Hide ellipsis menus when you click outside of the ellipsis menu parent div + document + .getElementsByTagName('BODY')[0] + .addEventListener('click', hideEllipsisMenus); +} + +function initializeEllipsisMenu() { + initializeEllipsisMenuToggle(); + initializeFormSubmit(); +} diff --git a/app/assets/stylesheets/dashboard.scss b/app/assets/stylesheets/dashboard.scss index ab6615e73ec98..6818f88417c10 100644 --- a/app/assets/stylesheets/dashboard.scss +++ b/app/assets/stylesheets/dashboard.scss @@ -318,6 +318,59 @@ } } + & div.ellipsis-menu { + float: right; + position: relative; + + & button.ellipsis-menu-btn { + display: block; + float: right; + margin-top: 1em; + border-radius: 15px; + + &:hover { + background-color: #eee; + } + } + + & ul.ellipsis-menu { + position: absolute; + right: 0; + display: block; + background-color: white; + padding-left: 0; + border-style: solid; + border-color: #ccc; + border-radius: 5px; + border-width: 1px; + margin-top: 2.75em; + + &.hidden { + display: none; + } + + & hr { + margin: 0; + color: #ccc; + background-color: #ccc; + height: 1px; + border: 0; + } + + & li.ellipsis-menu-item { + list-style: none; + + & form * { + background-color: transparent; + } + + &:hover { + background-color: #eee; + } + } + } + } + .dashboard-collection-org-details { .dashboard-top-pill { @include themeable( diff --git a/app/controllers/article_mutes_controller.rb b/app/controllers/article_mutes_controller.rb index b9892144a1401..35876b3a35a3b 100644 --- a/app/controllers/article_mutes_controller.rb +++ b/app/controllers/article_mutes_controller.rb @@ -5,6 +5,9 @@ def update @article = Article.find_by(id: params[:id]) authorize @article @article.update(receive_notifications: permitted_attributes(@article)[:receive_notifications]) - redirect_to "/dashboard" + respond_to do |format| + format.json { head :ok } + format.html { redirect_to "/dashboard" } + end end end diff --git a/app/controllers/articles_controller.rb b/app/controllers/articles_controller.rb index 7aa9ed58c4607..5875bd8ee6ce2 100644 --- a/app/controllers/articles_controller.rb +++ b/app/controllers/articles_controller.rb @@ -145,9 +145,16 @@ def update Notification.remove_all_without_delay(notifiable_id: @article.id, notifiable_type: "Article", action: "Published") path = "/#{@article.username}/#{@article.slug}?preview=#{@article.password}" end - redirect_to(params[:destination] || path) + + respond_to do |format| + format.json { head :ok } + format.html { redirect_to(params[:destination] || path) } + end else - render :edit + respond_to do |format| + format.html { redirect_to :edit } + format.json { head :unprocessable_entity } + end end end diff --git a/app/policies/article_policy.rb b/app/policies/article_policy.rb index 12b5b3ef81561..daffaf1a7e3d5 100644 --- a/app/policies/article_policy.rb +++ b/app/policies/article_policy.rb @@ -26,7 +26,8 @@ def preview? def permitted_attributes %i[title body_html body_markdown main_image published canonical_url description allow_small_edits allow_big_edits tag_list publish_under_org - video video_code video_source_url video_thumbnail_url receive_notifications] + video video_code video_source_url video_thumbnail_url receive_notifications + archived] end private diff --git a/app/views/dashboards/_dashboard_article.html.erb b/app/views/dashboards/_dashboard_article.html.erb index 3538d83585e75..018e6879118ca 100644 --- a/app/views/dashboards/_dashboard_article.html.erb +++ b/app/views/dashboards/_dashboard_article.html.erb @@ -1,4 +1,4 @@ -
"> +
<%= "single-article-archived hidden" if article.archived %>">
<% if article.organization_id %> <%= article.organization&.name %> @@ -7,6 +7,26 @@ Series: <%= article.series %> <% end %>
+ +
+ + +
+

<%= article.title %>

<% if article.published %> @@ -29,6 +49,8 @@ DELETE <% elsif article.published %> MANAGE + <% else %> + DELETE <% end %> <% if article.published %> diff --git a/app/views/dashboards/show.html.erb b/app/views/dashboards/show.html.erb index 9f72d06155196..28d3c0b4beec1 100644 --- a/app/views/dashboards/show.html.erb +++ b/app/views/dashboards/show.html.erb @@ -29,6 +29,9 @@ <% end %> <%= select_tag "dashhboard_sort", options_for_select(sort_options, params[:sort]) %> + <% if @articles.any? {|article| article.archived } %> + <%= link_to "Show archived", "javascript:;", id: "toggleArchivedLink" %> + <% end %>
<% @articles.each do |article| %> <%= render "dashboard_article", article: article, org_admin: false, manage_view: false %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index afcda820939c6..b93eeae75ac22 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -55,6 +55,7 @@ <%= yield(:page_meta) %> <%= favicon_link_tag %> + <%= stylesheet_link_tag "https://fonts.googleapis.com/icon?family=Material+Icons" %> "> "> "> diff --git a/db/migrate/20190504015859_add_archived_to_articles.rb b/db/migrate/20190504015859_add_archived_to_articles.rb new file mode 100644 index 0000000000000..b34daa49bec85 --- /dev/null +++ b/db/migrate/20190504015859_add_archived_to_articles.rb @@ -0,0 +1,5 @@ +class AddArchivedToArticles < ActiveRecord::Migration[5.2] + def change + add_column :articles, :archived, :boolean, default: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 1beff6747638b..1fc34fcbd1233 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -52,6 +52,7 @@ t.float "amount_due", default: 0.0 t.float "amount_paid", default: 0.0 t.boolean "approved", default: false + t.boolean "archived", default: false t.boolean "automatically_renew", default: false t.text "body_html" t.text "body_markdown"