Created
September 5, 2025 07:36
-
-
Save Stanislas-Poisson/b46889323b747b67c891d68a57f456ac to your computer and use it in GitHub Desktop.
Tampermonkey-PFS-PR.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name GitHub PR Filters - ParisFashionShops | |
| // @namespace http://tampermonkey.net/ | |
| // @version 1.0.0 | |
| // @description Finder-style filters with URL persistence for ParisFashionShops PRs | |
| // @author Stanislas Poisson <stanislas.poisson@parisfashionshops.com> | |
| // @match https://github.com/ParisFashionShops/*/pulls* | |
| // @grant none | |
| // ==/UserScript== | |
| ;(function () { | |
| 'use strict' | |
| // Filter state management - ALL EMPTY BY DEFAULT | |
| const filterState = { | |
| branch: '', // Empty = "All branches" | |
| assignment: '', // Empty = "All PRs" | |
| status: '', // Empty = "All statuses" | |
| review: '', // Empty = "All reviews" | |
| draft: '', // Empty = "All types" | |
| date: '', // Empty = "Any time" | |
| } | |
| let searchTimer = null | |
| let countdownInterval = null | |
| const SEARCH_DELAY = 1500 | |
| // Parse current URL to restore filter state | |
| function parseUrlFilters() { | |
| const urlParams = new URLSearchParams(window.location.search) | |
| const query = urlParams.get('q') | |
| console.debug('Parsing URL query:', query) | |
| // Reset ALL to empty (= "All" options selected) | |
| filterState.branch = '' | |
| filterState.assignment = '' | |
| filterState.status = '' | |
| filterState.review = '' | |
| filterState.draft = '' | |
| filterState.date = '' | |
| // If no query or only basic GitHub defaults, keep everything empty | |
| if ( | |
| !query || | |
| 'is:pr is:open' === query.trim() || | |
| 'is:open is:pr' === query.trim() || | |
| '' === query.trim() | |
| ) { | |
| console.debug('No specific filters detected, showing ALL options selected') | |
| return | |
| } | |
| // Parse specific filters only | |
| const branchMatch = query.match(/base:(\w+)/) | |
| if (branchMatch) { | |
| filterState.branch = branchMatch[1] | |
| console.debug('Found branch filter:', branchMatch[1]) | |
| } | |
| if (query.includes('assignee:@me')) { | |
| filterState.assignment = 'assigned-to-me' | |
| } else if (query.includes('author:@me')) { | |
| filterState.assignment = 'my-prs' | |
| } else if (query.includes('review-requested:@me')) { | |
| filterState.assignment = 'review-requested' | |
| } else if (query.includes('reviewed-by:@me')) { | |
| filterState.assignment = 'reviewed-by-me' | |
| } | |
| if ( | |
| query.includes('review:approved') && | |
| query.includes('status:success') && | |
| query.includes('-is:draft') | |
| ) { | |
| filterState.status = 'ready-to-merge' | |
| } else if (query.includes('review:approved') && query.includes('status:failure')) { | |
| filterState.status = 'almost-ready' | |
| } else if (query.includes('status:failure')) { | |
| filterState.status = 'conflicts' | |
| } else if (query.includes('-status:failure')) { | |
| filterState.status = 'no-conflicts' | |
| } | |
| if (query.includes('review:approved') && !filterState.status) { | |
| filterState.review = 'approved' | |
| } else if (query.includes('review:changes-requested')) { | |
| filterState.review = 'changes-requested' | |
| } else if (query.includes('review:none')) { | |
| filterState.review = 'no-review' | |
| } | |
| if (query.includes('is:draft')) { | |
| filterState.draft = 'draft' | |
| } else if (query.includes('-is:draft') && !filterState.status) { | |
| filterState.draft = 'no-draft' | |
| } | |
| if (query.includes('updated:>7-days-ago')) { | |
| filterState.date = 'recent-7d' | |
| } else if (query.includes('updated:>14-days-ago')) { | |
| filterState.date = 'recent-14d' | |
| } else if (query.includes('updated:<30-days-ago')) { | |
| filterState.date = 'old-30d' | |
| } else if (query.includes('updated:<60-days-ago')) { | |
| filterState.date = 'old-60d' | |
| } | |
| console.debug('Final parsed filters:', filterState) | |
| } | |
| // Create the Finder-style interface | |
| function createFilterInterface() { | |
| const container = document.createElement('div') | |
| container.style.cssText = ` | |
| background: #f6f8fa; | |
| border: 1px solid #d0d7de; | |
| border-radius: 6px; | |
| padding: 16px; | |
| margin: 16px 0; | |
| box-shadow: 0 1px 3px rgba(0,0,0,0.12); | |
| position: sticky; | |
| top: 0; | |
| z-index: 10; | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; | |
| ` | |
| const title = document.createElement('h3') | |
| title.textContent = 'Smart PR Filters' | |
| title.style.cssText = ` | |
| margin: 0 0 16px 0; | |
| color: #24292f; | |
| font-size: 16px; | |
| font-weight: 600; | |
| text-align: center; | |
| ` | |
| // Finder-style columns container | |
| const columnsContainer = document.createElement('div') | |
| columnsContainer.style.cssText = ` | |
| display: flex; | |
| gap: 2px; | |
| height: 200px; | |
| background: white; | |
| border-radius: 8px; | |
| overflow: hidden; | |
| box-shadow: inset 0 1px 3px rgba(0,0,0,0.1); | |
| ` | |
| // Filter columns definition - ALL OPTIONS FIRST | |
| const filterColumns = [ | |
| { | |
| title: '🎯 Target Branch', | |
| key: 'branch', | |
| width: '180px', | |
| options: [ | |
| { value: '', label: 'All branches', desc: 'any target branch' }, | |
| { value: 'dev', label: 'dev', desc: 'development branch' }, | |
| { value: 'preprod', label: 'preprod', desc: 'pre-production' }, | |
| { value: 'main', label: 'main', desc: 'main branch' }, | |
| ], | |
| }, | |
| { | |
| title: '👤 Assignment', | |
| key: 'assignment', | |
| width: '200px', | |
| options: [ | |
| { value: '', label: 'All PRs', desc: 'any assignee or author' }, | |
| { value: 'assigned-to-me', label: 'Assigned to me', desc: 'you are assigned' }, | |
| { value: 'my-prs', label: 'My PRs', desc: 'you created' }, | |
| { value: 'review-requested', label: 'Review requested', desc: 'your review needed' }, | |
| { value: 'reviewed-by-me', label: 'Already reviewed', desc: 'you reviewed' }, | |
| ], | |
| }, | |
| { | |
| title: '⚙️ Technical Status', | |
| key: 'status', | |
| width: '220px', | |
| options: [ | |
| { value: '', label: 'All statuses', desc: 'any technical status' }, | |
| { value: 'conflicts', label: 'With conflicts', desc: 'merge conflicts' }, | |
| { value: 'no-conflicts', label: 'No conflicts', desc: 'clean to merge' }, | |
| { value: 'almost-ready', label: 'Almost ready', desc: 'approved but checks failing' }, | |
| { value: 'ready-to-merge', label: 'Ready to merge', desc: 'approved and checks pass' }, | |
| ], | |
| }, | |
| { | |
| title: '📝 Review State', | |
| key: 'review', | |
| width: '200px', | |
| options: [ | |
| { value: '', label: 'All reviews', desc: 'any review state' }, | |
| { value: 'approved', label: 'Approved', desc: 'has approval' }, | |
| { value: 'changes-requested', label: 'Changes requested', desc: 'needs changes' }, | |
| { value: 'no-review', label: 'No review yet', desc: 'awaiting review' }, | |
| ], | |
| }, | |
| { | |
| title: '📄 Draft Status', | |
| key: 'draft', | |
| width: '180px', | |
| options: [ | |
| { value: '', label: 'All types', desc: 'draft and ready' }, | |
| { value: 'draft', label: 'Drafts only', desc: 'work in progress' }, | |
| { value: 'no-draft', label: 'Ready only', desc: 'not draft' }, | |
| ], | |
| }, | |
| { | |
| title: '📅 Last Update', | |
| key: 'date', | |
| width: '200px', | |
| options: [ | |
| { value: '', label: 'Any time', desc: 'any update date' }, | |
| { value: 'recent-7d', label: 'Last 7 days', desc: 'updated this week' }, | |
| { value: 'recent-14d', label: 'Last 2 weeks', desc: 'updated recently' }, | |
| { value: 'old-30d', label: 'Older than 30d', desc: 'needs attention' }, | |
| { value: 'old-60d', label: 'Older than 60d', desc: 'stale PRs' }, | |
| ], | |
| }, | |
| ] | |
| // Create columns | |
| filterColumns.forEach((column) => { | |
| const columnDiv = createFinderColumn(column) | |
| columnsContainer.appendChild(columnDiv) | |
| }) | |
| // Bottom section with query preview and controls | |
| const bottomSection = document.createElement('div') | |
| bottomSection.style.cssText = ` | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| margin-top: 16px; | |
| gap: 16px; | |
| ` | |
| // Query preview (left side) | |
| const queryPreview = document.createElement('div') | |
| queryPreview.id = 'query-preview' | |
| queryPreview.style.cssText = ` | |
| flex: 1; | |
| background: rgba(255,255,255,0.8); | |
| border: 1px solid #e1e4e8; | |
| border-radius: 6px; | |
| padding: 12px; | |
| font-size: 13px; | |
| color: #656d76; | |
| min-height: 20px; | |
| line-height: 1.3; | |
| ` | |
| // Controls (right side) | |
| const controlsContainer = document.createElement('div') | |
| controlsContainer.style.cssText = ` | |
| display: flex; | |
| flex-direction: column; | |
| align-items: flex-end; | |
| gap: 8px; | |
| ` | |
| // Countdown and search button | |
| const countdownContainer = document.createElement('div') | |
| countdownContainer.style.cssText = ` | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| ` | |
| // Progress bar | |
| const progressContainer = document.createElement('div') | |
| progressContainer.style.cssText = ` | |
| width: 120px; | |
| height: 4px; | |
| background: #e1e4e8; | |
| border-radius: 2px; | |
| overflow: hidden; | |
| position: relative; | |
| ` | |
| const progressBar = document.createElement('div') | |
| progressBar.id = 'progress-bar' | |
| progressBar.style.cssText = ` | |
| height: 100%; | |
| width: 100%; | |
| background: #0969da; | |
| border-radius: 2px; | |
| transition: width 0.05s linear; | |
| ` | |
| progressContainer.appendChild(progressBar) | |
| // Countdown text | |
| const countdownText = document.createElement('span') | |
| countdownText.id = 'countdown-text' | |
| countdownText.style.cssText = ` | |
| font-size: 12px; | |
| color: #656d76; | |
| font-family: 'SF Mono', Monaco, monospace; | |
| min-width: 60px; | |
| text-align: right; | |
| ` | |
| // Search button | |
| const searchButton = document.createElement('button') | |
| searchButton.textContent = 'Search Now' | |
| searchButton.style.cssText = ` | |
| padding: 8px 16px; | |
| background: #0969da; | |
| color: white; | |
| border: 1px solid #0969da; | |
| border-radius: 6px; | |
| cursor: pointer; | |
| font-size: 14px; | |
| font-weight: 600; | |
| transition: all 0.2s; | |
| ` | |
| searchButton.addEventListener('mouseenter', () => { | |
| searchButton.style.background = '#0550ae' | |
| }) | |
| searchButton.addEventListener('mouseleave', () => { | |
| searchButton.style.background = '#0969da' | |
| }) | |
| searchButton.addEventListener('click', applyFilters) | |
| // Reset button | |
| const resetButton = document.createElement('button') | |
| resetButton.textContent = 'Reset All' | |
| resetButton.style.cssText = ` | |
| padding: 6px 16px; | |
| background: transparent; | |
| color: #656d76; | |
| border: 1px solid #d0d7de; | |
| border-radius: 6px; | |
| cursor: pointer; | |
| font-size: 12px; | |
| transition: all 0.2s; | |
| ` | |
| resetButton.addEventListener('click', resetFilters) | |
| countdownContainer.appendChild(progressContainer) | |
| countdownContainer.appendChild(countdownText) | |
| countdownContainer.appendChild(searchButton) | |
| controlsContainer.appendChild(countdownContainer) | |
| controlsContainer.appendChild(resetButton) | |
| bottomSection.appendChild(queryPreview) | |
| bottomSection.appendChild(controlsContainer) | |
| container.appendChild(title) | |
| container.appendChild(columnsContainer) | |
| container.appendChild(bottomSection) | |
| return container | |
| } | |
| // Create Finder-style column | |
| function createFinderColumn(column) { | |
| const columnDiv = document.createElement('div') | |
| columnDiv.style.cssText = ` | |
| width: ${column.width}; | |
| border-right: 1px solid #e1e4e8; | |
| display: flex; | |
| flex-direction: column; | |
| background: white; | |
| ` | |
| // Column header | |
| const header = document.createElement('div') | |
| header.textContent = column.title | |
| header.style.cssText = ` | |
| padding: 8px 12px; | |
| background: #f6f8fa; | |
| border-bottom: 1px solid #e1e4e8; | |
| font-size: 12px; | |
| font-weight: 600; | |
| color: #656d76; | |
| text-transform: uppercase; | |
| letter-spacing: 0.5px; | |
| ` | |
| // Options container | |
| const optionsContainer = document.createElement('div') | |
| optionsContainer.style.cssText = ` | |
| flex: 1; | |
| overflow-y: auto; | |
| ` | |
| // Create options | |
| column.options.forEach((option) => { | |
| const optionDiv = document.createElement('div') | |
| optionDiv.style.cssText = ` | |
| padding: 10px 12px; | |
| cursor: pointer; | |
| border-bottom: 1px solid #f6f8fa; | |
| transition: all 0.2s; | |
| font-size: 13px; | |
| position: relative; | |
| ` | |
| // Check if this option is selected | |
| const isSelected = filterState[column.key] === option.value | |
| console.debug( | |
| `Option ${option.label}: filterState.${column.key} = '${filterState[column.key]}', option.value = '${ | |
| option.value | |
| }', isSelected = ${isSelected}` | |
| ) | |
| if (isSelected) { | |
| optionDiv.style.background = '#dbeafe' | |
| optionDiv.style.borderLeft = '3px solid #0969da' | |
| optionDiv.style.fontWeight = '600' | |
| } | |
| // Option label | |
| const label = document.createElement('div') | |
| label.textContent = option.label | |
| label.style.cssText = ` | |
| color: ${isSelected ? '#0969da' : '#24292f'}; | |
| margin-bottom: 2px; | |
| ` | |
| // Option description | |
| const desc = document.createElement('div') | |
| desc.textContent = option.desc | |
| desc.style.cssText = ` | |
| font-size: 11px; | |
| color: #656d76; | |
| font-style: italic; | |
| ` | |
| optionDiv.appendChild(label) | |
| optionDiv.appendChild(desc) | |
| // Hover effects | |
| optionDiv.addEventListener('mouseenter', () => { | |
| if (!isSelected) { | |
| optionDiv.style.background = '#f6f8fa' | |
| } | |
| }) | |
| optionDiv.addEventListener('mouseleave', () => { | |
| if (!isSelected) { | |
| optionDiv.style.background = 'white' | |
| } | |
| }) | |
| // Click handler | |
| optionDiv.addEventListener('click', () => { | |
| console.debug(`Clicked on ${column.key}: ${option.value}`) | |
| handleFilterChange(column.key, option.value) | |
| refreshColumnSelections() | |
| }) | |
| optionsContainer.appendChild(optionDiv) | |
| }) | |
| columnDiv.appendChild(header) | |
| columnDiv.appendChild(optionsContainer) | |
| return columnDiv | |
| } | |
| // Handle filter change | |
| function handleFilterChange(key, value) { | |
| console.debug(`Filter change: ${key} = ${value}`) | |
| filterState[key] = value | |
| setTimeout(() => { | |
| updateQueryPreview() | |
| startCountdown() | |
| }, 10) | |
| } | |
| // Refresh column selections | |
| function refreshColumnSelections() { | |
| const existingInterface = document.getElementById('pr-filters-addon') | |
| if (existingInterface) { | |
| const newInterface = createFilterInterface() | |
| newInterface.id = 'pr-filters-addon' | |
| existingInterface.parentNode.replaceChild(newInterface, existingInterface) | |
| setTimeout(() => { | |
| updateQueryPreview() | |
| }, 50) | |
| } | |
| } | |
| // Update query preview with natural language | |
| function updateQueryPreview() { | |
| const preview = document.getElementById('query-preview') | |
| if (!preview) { | |
| console.debug('Preview element not found') | |
| return | |
| } | |
| const parts = [] | |
| let hasSpecificFilters = false | |
| // Check each filter | |
| if (filterState.branch) { | |
| parts.push(`targeting ${filterState.branch} branch`) | |
| hasSpecificFilters = true | |
| } | |
| switch (filterState.assignment) { | |
| case 'assigned-to-me': | |
| parts.push(hasSpecificFilters ? 'assigned to you' : 'PRs assigned to you') | |
| hasSpecificFilters = true | |
| break | |
| case 'my-prs': | |
| parts.push(hasSpecificFilters ? 'created by you' : 'PRs created by you') | |
| hasSpecificFilters = true | |
| break | |
| case 'review-requested': | |
| parts.push( | |
| hasSpecificFilters | |
| ? 'where your review is requested' | |
| : 'PRs where your review is requested' | |
| ) | |
| hasSpecificFilters = true | |
| break | |
| case 'reviewed-by-me': | |
| parts.push( | |
| hasSpecificFilters ? 'you have already reviewed' : 'PRs you have already reviewed' | |
| ) | |
| hasSpecificFilters = true | |
| break | |
| default: | |
| break | |
| } | |
| switch (filterState.status) { | |
| case 'conflicts': | |
| parts.push('with merge conflicts') | |
| hasSpecificFilters = true | |
| break | |
| case 'no-conflicts': | |
| parts.push('without conflicts') | |
| hasSpecificFilters = true | |
| break | |
| case 'almost-ready': | |
| parts.push('that are approved but have failing checks') | |
| hasSpecificFilters = true | |
| break | |
| case 'ready-to-merge': | |
| parts.push('that are ready to merge (approved + passing checks)') | |
| hasSpecificFilters = true | |
| break | |
| default: | |
| break | |
| } | |
| switch (filterState.review) { | |
| case 'approved': | |
| parts.push('with approvals') | |
| hasSpecificFilters = true | |
| break | |
| case 'changes-requested': | |
| parts.push('with requested changes') | |
| hasSpecificFilters = true | |
| break | |
| case 'no-review': | |
| parts.push('awaiting review') | |
| hasSpecificFilters = true | |
| break | |
| default: | |
| break | |
| } | |
| switch (filterState.draft) { | |
| case 'draft': | |
| parts.push('in draft mode') | |
| hasSpecificFilters = true | |
| break | |
| case 'no-draft': | |
| parts.push('ready for review (not draft)') | |
| hasSpecificFilters = true | |
| break | |
| default: | |
| break | |
| } | |
| switch (filterState.date) { | |
| case 'recent-7d': | |
| parts.push('updated in the last 7 days') | |
| hasSpecificFilters = true | |
| break | |
| case 'recent-14d': | |
| parts.push('updated in the last 2 weeks') | |
| hasSpecificFilters = true | |
| break | |
| case 'old-30d': | |
| parts.push('not updated for 30+ days') | |
| hasSpecificFilters = true | |
| break | |
| case 'old-60d': | |
| parts.push('not updated for 60+ days') | |
| hasSpecificFilters = true | |
| break | |
| default: | |
| break | |
| } | |
| let finalText | |
| if (!hasSpecificFilters) { | |
| finalText = 'Searching for all open PRs.' | |
| } else { | |
| finalText = `Searching for ${ | |
| 1 === parts.length && parts[0].startsWith('PRs') ? parts[0] : `PRs ${parts.join(' ')}` | |
| }.` | |
| } | |
| preview.textContent = finalText | |
| console.debug('Updated preview:', finalText) | |
| } | |
| // Start visual countdown | |
| function startCountdown() { | |
| if (searchTimer) { | |
| clearTimeout(searchTimer) | |
| } | |
| if (countdownInterval) { | |
| clearInterval(countdownInterval) | |
| } | |
| const progressBar = document.getElementById('progress-bar') | |
| const countdownText = document.getElementById('countdown-text') | |
| if (!progressBar || !countdownText) { | |
| console.debug('Progress elements not found, retrying countdown...') | |
| setTimeout(startCountdown, 100) | |
| return | |
| } | |
| console.debug('Starting countdown...') | |
| let timeLeft = SEARCH_DELAY | |
| const startTime = Date.now() | |
| progressBar.style.width = '100%' | |
| countdownInterval = setInterval(() => { | |
| const elapsed = Date.now() - startTime | |
| timeLeft = Math.max(0, SEARCH_DELAY - elapsed) | |
| const progress = (timeLeft / SEARCH_DELAY) * 100 | |
| progressBar.style.width = `${progress}%` | |
| const seconds = Math.floor(timeLeft / 1000) | |
| const ms = Math.floor((timeLeft % 1000) / 10) | |
| countdownText.textContent = `${seconds}.${ms.toString().padStart(2, '0')}s` | |
| if (0 >= timeLeft) { | |
| clearInterval(countdownInterval) | |
| countdownText.textContent = 'Searching...' | |
| } | |
| }, 50) | |
| searchTimer = setTimeout(() => { | |
| clearInterval(countdownInterval) | |
| applyFilters() | |
| }, SEARCH_DELAY) | |
| } | |
| // Build GitHub query from filter state | |
| function buildQuery() { | |
| const queryParts = ['org:ParisFashionShops', 'is:pr', 'is:open'] | |
| if (filterState.branch) { | |
| queryParts.push(`base:${filterState.branch}`) | |
| } | |
| switch (filterState.assignment) { | |
| case 'assigned-to-me': | |
| queryParts.push('assignee:@me') | |
| break | |
| case 'my-prs': | |
| queryParts.push('author:@me') | |
| break | |
| case 'review-requested': | |
| queryParts.push('review-requested:@me') | |
| break | |
| case 'reviewed-by-me': | |
| queryParts.push('reviewed-by:@me') | |
| break | |
| default: | |
| break | |
| } | |
| switch (filterState.status) { | |
| case 'conflicts': | |
| queryParts.push('status:failure') | |
| break | |
| case 'no-conflicts': | |
| queryParts.push('-status:failure') | |
| break | |
| case 'almost-ready': | |
| queryParts.push('review:approved', 'status:failure') | |
| break | |
| case 'ready-to-merge': | |
| queryParts.push('review:approved', 'status:success', '-is:draft') | |
| break | |
| default: | |
| break | |
| } | |
| switch (filterState.review) { | |
| case 'approved': | |
| queryParts.push('review:approved') | |
| break | |
| case 'changes-requested': | |
| queryParts.push('review:changes-requested') | |
| break | |
| case 'no-review': | |
| queryParts.push('review:none') | |
| break | |
| default: | |
| break | |
| } | |
| switch (filterState.draft) { | |
| case 'draft': | |
| queryParts.push('is:draft') | |
| break | |
| case 'no-draft': | |
| queryParts.push('-is:draft') | |
| break | |
| default: | |
| break | |
| } | |
| switch (filterState.date) { | |
| case 'recent-7d': | |
| queryParts.push('updated:>7-days-ago') | |
| break | |
| case 'recent-14d': | |
| queryParts.push('updated:>14-days-ago') | |
| break | |
| case 'old-30d': | |
| queryParts.push('updated:<30-days-ago') | |
| break | |
| case 'old-60d': | |
| queryParts.push('updated:<60-days-ago') | |
| break | |
| default: | |
| break | |
| } | |
| return queryParts.join(' ') | |
| } | |
| // Apply filters | |
| function applyFilters() { | |
| if (searchTimer) { | |
| clearTimeout(searchTimer) | |
| } | |
| if (countdownInterval) { | |
| clearInterval(countdownInterval) | |
| } | |
| const query = buildQuery() | |
| const baseUrl = `${window.location.origin}${window.location.pathname}` | |
| const url = `${baseUrl}?q=${encodeURIComponent(query)}` | |
| console.debug('Applying filters with query:', query) | |
| const countdownText = document.getElementById('countdown-text') | |
| if (countdownText) { | |
| countdownText.textContent = 'Searching...' | |
| } | |
| window.location.href = url | |
| } | |
| // Reset filters - EVERYTHING BACK TO EMPTY | |
| function resetFilters() { | |
| if (searchTimer) { | |
| clearTimeout(searchTimer) | |
| } | |
| if (countdownInterval) { | |
| clearInterval(countdownInterval) | |
| } | |
| console.debug('Resetting all filters to empty (All options)') | |
| // ALL BACK TO EMPTY = "All" OPTIONS | |
| filterState.branch = '' | |
| filterState.assignment = '' | |
| filterState.status = '' | |
| filterState.review = '' | |
| filterState.draft = '' | |
| filterState.date = '' | |
| // Go to base URL without query params | |
| const baseUrl = `${window.location.origin}${window.location.pathname}` | |
| window.location.href = baseUrl | |
| } | |
| // Initialize | |
| function init() { | |
| console.debug('Initializing PR filters...') | |
| // Parse URL first | |
| parseUrlFilters() | |
| const checkAndInit = () => { | |
| const targetArea = document.querySelector('#repo-content-pjax-container div h1') | |
| if (targetArea && targetArea.parentElement && !document.querySelector('#pr-filters-addon')) { | |
| console.debug('Creating filter interface...') | |
| const filterInterface = createFilterInterface() | |
| filterInterface.id = 'pr-filters-addon' | |
| targetArea.parentElement.insertBefore(filterInterface, targetArea) | |
| setTimeout(() => { | |
| updateQueryPreview() | |
| console.debug('Interface initialized successfully') | |
| }, 100) | |
| return true | |
| } | |
| return false | |
| } | |
| if (!checkAndInit()) { | |
| setTimeout(checkAndInit, 1000) | |
| } | |
| } | |
| // Start | |
| if ('loading' === document.readyState) { | |
| document.addEventListener('DOMContentLoaded', init) | |
| } else { | |
| init() | |
| } | |
| })() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment