Skip to content

Commit

Permalink
Merge pull request #189 from koya0/development
Browse files Browse the repository at this point in the history
feat: add availability filter for the issues
  • Loading branch information
zugdev authored Jan 5, 2025
2 parents d7f21f5 + 2274282 commit 97b86d7
Show file tree
Hide file tree
Showing 5 changed files with 249 additions and 144 deletions.
17 changes: 10 additions & 7 deletions src/home/fetch-github/fetch-and-display-previews.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { applyAvatarsToIssues, renderGitHubIssues } from "../rendering/render-gi
import { renderOrgHeaderLabel } from "../rendering/render-org-header";
import { closeModal } from "../rendering/render-preview-modal";
import { filterIssuesBySearch } from "../sorting/filter-issues-by-search";
import { filterIssuesByAvailability } from "../sorting/filter-issues-by-availability";
import { Sorting } from "../sorting/generate-sorting-buttons";
import { sortIssuesController } from "../sorting/sort-issues-controller";
import { checkCacheIntegrityAndSyncTasks } from "./cache-integrity";
Expand All @@ -12,6 +13,12 @@ export type Options = {
ordering: "normal" | "reverse";
};

export let isFilteringAvailableIssues = true;

export function swapAvailabilityFilter() {
isFilteringAvailableIssues = !isFilteringAvailableIssues;
}

// start at view based on URL
export let isProposalOnlyViewer = new URLSearchParams(window.location.search).get("proposal") === "true";

Expand Down Expand Up @@ -87,20 +94,16 @@ export async function displayGitHubIssues({
const sortedIssues = sortIssuesController(cachedTasks, sorting, options);
let sortedAndFiltered = sortedIssues.filter(getProposalsOnlyFilter(isProposalOnlyViewer));
sortedAndFiltered = filterIssuesByOrganization(sortedAndFiltered);
sortedAndFiltered = isFilteringAvailableIssues ? filterIssuesByAvailability(sortedAndFiltered) : sortedAndFiltered;
renderGitHubIssues(sortedAndFiltered, skipAnimation);
applyAvatarsToIssues();
}

export async function searchDisplayGitHubIssues({
searchText,
skipAnimation = false,
}: {
searchText: string;
skipAnimation?: boolean;
}) {
export async function searchDisplayGitHubIssues({ searchText, skipAnimation = false }: { searchText: string; skipAnimation?: boolean }) {
const searchResult = filterIssuesBySearch(searchText);
let filteredIssues = searchResult.filter(getProposalsOnlyFilter(isProposalOnlyViewer));
filteredIssues = filterIssuesByOrganization(filteredIssues);
filteredIssues = isFilteringAvailableIssues ? filterIssuesByAvailability(filteredIssues) : filteredIssues;
renderGitHubIssues(filteredIssues, skipAnimation);
applyAvatarsToIssues();
}
8 changes: 8 additions & 0 deletions src/home/sorting/filter-issues-by-availability.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { GitHubIssue } from "../github-types";

export function filterIssuesByAvailability(issues: GitHubIssue[]) {
return issues.filter((issue) => {
if (issue.assignee) return false;
return true;
});
}
104 changes: 83 additions & 21 deletions src/home/sorting/sorting-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { displayGitHubIssues, searchDisplayGitHubIssues } from "../fetch-github/
import { renderErrorInModal } from "../rendering/display-popup-modal";
import { proposalViewToggle } from "../rendering/render-github-issues";
import { Sorting } from "./generate-sorting-buttons";
import { isFilteringAvailableIssues, swapAvailabilityFilter } from "../fetch-github/fetch-and-display-previews";

export class SortingManager {
private _lastChecked: HTMLInputElement | null = null;
private _toolBarFilters: HTMLElement;
private _filterTextBox: HTMLInputElement;
private _filtersDiv: HTMLElement;
private _sortingButtons: HTMLElement;
private _instanceId: string;
private _sortingState: { [key: string]: "unsorted" | "ascending" | "descending" } = {}; // Track state for each sorting option
Expand All @@ -20,8 +21,12 @@ export class SortingManager {

// Initialize sorting buttons first
this._sortingButtons = this._generateSortingButtons(sortingOptions);
// Then initialize filter text box
this._filterTextBox = this._generateFilterTextBox();
// Initialize filters div
this._filtersDiv = this._generateFiltersDiv();
// Add filter search box to filters div
this._filtersDiv.appendChild(this._generateFilterTextBox());
// Add filter available issues button to filters div
this._filtersDiv.appendChild(this._generateFilterAvailableIssuesButton());

// Initialize sorting states to 'unsorted' for all options
sortingOptions.forEach((option) => {
Expand All @@ -30,10 +35,16 @@ export class SortingManager {
}

public render() {
this._toolBarFilters.appendChild(this._filterTextBox);
this._toolBarFilters.appendChild(this._filtersDiv);
this._toolBarFilters.appendChild(this._sortingButtons);
}

private _generateFiltersDiv() {
const div = document.createElement("div");
div.className = "filters";
return div;
}

private _generateFilterTextBox() {
const textBox = document.createElement("input");
textBox.type = "text";
Expand Down Expand Up @@ -115,10 +126,19 @@ export class SortingManager {
return textBox;
}

private _resetSearchBar() {
const filterTextBox = this._filtersDiv.querySelector('input[type="text"]') as HTMLInputElement;
filterTextBox.value = "";
const newURL = new URL(window.location.href);
newURL.searchParams.delete("search");
window.history.replaceState({}, "", newURL.toString());
}

private _resetSortButtons() {
this._sortingButtons.querySelectorAll('input[type="radio"]').forEach((input) => {
if (input instanceof HTMLInputElement) {
input.checked = false;
this._sortingState[input.value] = "unsorted";
input.setAttribute("data-ordering", "");
}
});
Expand Down Expand Up @@ -148,6 +168,54 @@ export class SortingManager {
return buttons;
}

private _generateFilterAvailableIssuesButton() {
const input = document.createElement("input");
input.type = "button";
input.value = "Unassigned";
input.id = `filter-availability-${this._instanceId}`;

input.addEventListener("click", () => {
swapAvailabilityFilter();
input.value = isFilteringAvailableIssues ? "Unassigned" : "All Issues";

try {
// Clear search when applying the filter
this._resetSearchBar();

const { sortingOption, sortingOrder } = this._detectSortingState();
void displayGitHubIssues({
sorting: sortingOption as Sorting,
options: { ordering: sortingOrder },
});
} catch (error) {
renderErrorInModal(error as Error);
}
});

return input;
}

private _detectSortingState() {
let sortingOption;
let sortingOrder = "normal";

for (const option of Object.keys(this._sortingState)) {
const order = this._sortingState[option];

if (order !== "unsorted") {
sortingOption = option;

if (order === "descending") {
sortingOrder = "normal";
} else if (order === "ascending") {
sortingOrder = "reverse";
}
break;
}
}
return { sortingOption, sortingOrder };
}

private _createRadioButton(option: string): HTMLInputElement {
const input = document.createElement("input");
input.type = "radio";
Expand All @@ -168,35 +236,26 @@ export class SortingManager {
const currentOrdering = input.getAttribute("data-ordering");
let newOrdering: string;

// Reset sort buttons
this._resetSortButtons();

// Determine the new ordering based on the current state
if (currentOrdering === "normal") {
newOrdering = "reverse";
this._sortingState[option] = "ascending";
} else if (currentOrdering === "reverse") {
newOrdering = "disabled";
this._sortingState[option] = "unsorted";
} else {
newOrdering = "normal";
this._sortingState[option] = "descending";
}

// Apply the new ordering state
input.setAttribute("data-ordering", newOrdering);
input.parentElement?.childNodes.forEach((node) => {
if (node instanceof HTMLInputElement) {
node.setAttribute("data-ordering", "");
}
});

// Clear search when applying a different sort
this._filterTextBox.value = "";
const newURL = new URL(window.location.href);
newURL.searchParams.delete("search");
window.history.replaceState({}, "", newURL.toString());

// Reset other buttons
input.parentElement?.childNodes.forEach((node) => {
if (node instanceof HTMLInputElement) {
node.setAttribute("data-ordering", "");
}
});
this._resetSearchBar();

if (newOrdering === "disabled") {
this._lastChecked = null;
Expand All @@ -209,7 +268,10 @@ export class SortingManager {

// Apply the sorting based on the new state (normal or reverse)
try {
void displayGitHubIssues({ sorting: option as Sorting, options: { ordering: newOrdering } });
void displayGitHubIssues({
sorting: option as Sorting,
options: { ordering: newOrdering },
});
} catch (error) {
renderErrorCatch(error as ErrorEvent);
}
Expand Down
Loading

0 comments on commit 97b86d7

Please sign in to comment.