From aaa109466350c531b9238a61115b2877daca57d3 Mon Sep 17 00:00:00 2001 From: JakobDev Date: Thu, 25 May 2023 15:17:19 +0200 Subject: Add the ability to pin Issues (#24406) This adds the ability to pin important Issues and Pull Requests. You can also move pinned Issues around to change their Position. Resolves #2175. ## Screenshots ![grafik](https://user-images.githubusercontent.com/15185051/235123207-0aa39869-bb48-45c3-abe2-ba1e836046ec.png) ![grafik](https://user-images.githubusercontent.com/15185051/235123297-152a16ea-a857-451d-9a42-61f2cd54dd75.png) ![grafik](https://user-images.githubusercontent.com/15185051/235640782-cbfe25ec-6254-479a-a3de-133e585d7a2d.png) The Design was mostly copied from the Projects Board. ## Implementation This uses a new `pin_order` Column in the `issue` table. If the value is set to 0, the Issue is not pinned. If it's set to a bigger value, the value is the Position. 1 means it's the first pinned Issue, 2 means it's the second one etc. This is dived into Issues and Pull requests for each Repo. ## TODO - [x] You can currently pin as many Issues as you want. Maybe we should add a Limit, which is configurable. GitHub uses 3, but I prefer 6, as this is better for bigger Projects, but I'm open for suggestions. - [x] Pin and Unpin events need to be added to the Issue history. - [x] Tests - [x] Migration **The feature itself is currently fully working, so tester who may find weird edge cases are very welcome!** --------- Co-authored-by: silverwind Co-authored-by: Giteabot --- web_src/js/features/repo-issue-list.js | 60 ++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'web_src/js') diff --git a/web_src/js/features/repo-issue-list.js b/web_src/js/features/repo-issue-list.js index af0e80af81..cc50ec5f88 100644 --- a/web_src/js/features/repo-issue-list.js +++ b/web_src/js/features/repo-issue-list.js @@ -2,6 +2,7 @@ import $ from 'jquery'; import {updateIssuesMeta} from './repo-issue.js'; import {toggleElem} from '../utils/dom.js'; import {htmlEscape} from 'escape-goat'; +import {Sortable} from 'sortablejs'; function initRepoIssueListCheckboxes() { const $issueSelectAll = $('.issue-checkbox-all'); @@ -119,8 +120,67 @@ function initRepoIssueListAuthorDropdown() { }; } +function initPinRemoveButton() { + for (const button of document.getElementsByClassName('pinned-issue-unpin')) { + button.addEventListener('click', async (event) => { + const el = event.currentTarget; + const id = Number(el.getAttribute('data-issue-id')); + + // Send the unpin request + const response = await fetch(el.getAttribute('data-unpin-url'), { + method: 'delete', + headers: { + 'X-Csrf-Token': window.config.csrfToken, + 'Content-Type': 'application/json', + }, + }); + if (response.ok) { + // Delete the tooltip + el._tippy.destroy(); + // Remove the Card + el.closest(`div.pinned-issue-card[data-issue-id="${id}"]`).remove(); + } + }); + } +} + +async function pinMoveEnd(e) { + const url = e.item.getAttribute('data-move-url'); + const id = Number(e.item.getAttribute('data-issue-id')); + await fetch(url, { + method: 'post', + body: JSON.stringify({id, position: e.newIndex + 1}), + headers: { + 'X-Csrf-Token': window.config.csrfToken, + 'Content-Type': 'application/json', + }, + }); +} + +function initIssuePinSort() { + const pinDiv = document.getElementById('issue-pins'); + + if (pinDiv === null) return; + + // If the User is not a Repo Admin, we don't need to proceed + if (!pinDiv.hasAttribute('data-is-repo-admin')) return; + + initPinRemoveButton(); + + // If only one issue pinned, we don't need to make this Sortable + if (pinDiv.children.length < 2) return; + + new Sortable(pinDiv, { + group: 'shared', + animation: 150, + ghostClass: 'card-ghost', + onEnd: pinMoveEnd, + }); +} + export function initRepoIssueList() { if (!document.querySelectorAll('.page-content.repository.issue-list, .page-content.repository.milestone-issue-list').length) return; initRepoIssueListCheckboxes(); initRepoIssueListAuthorDropdown(); + initIssuePinSort(); } -- cgit v1.2.3