refactor: extract logic from QrScanner and UrlCleaner to composables
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
import { ref, watch, onUnmounted } from 'vue'
|
||||
import { Copy, Trash2, ExternalLink, Power, Zap, X, Settings, Download } from 'lucide-vue-next'
|
||||
import { useExtension } from '../../composables/useExtension'
|
||||
import { useLocalStorage } from '../../composables/useLocalStorage'
|
||||
import { useUrlCleaner } from '../../composables/useUrlCleaner'
|
||||
import ExtensionStatus from './common/ExtensionStatus.vue'
|
||||
import UrlCleanerExceptionsModal from './UrlCleanerExceptionsModal.vue'
|
||||
|
||||
@@ -10,29 +10,22 @@ import UrlCleanerExceptionsModal from './UrlCleanerExceptionsModal.vue'
|
||||
const { isExtensionReady, isListening, lastClipboardText, startListening, stopListening, writeClipboard } = useExtension()
|
||||
|
||||
const inputUrl = ref('')
|
||||
// Use local storage for history persistence
|
||||
const cleanedHistory = useLocalStorage('url-cleaner-history', [])
|
||||
const isWatchEnabled = useLocalStorage('url-cleaner-watch-enabled', false)
|
||||
|
||||
// Exceptions management
|
||||
const showExceptionsModal = ref(false)
|
||||
const defaultExceptions = [
|
||||
{ id: 'yt', domainPattern: '*.youtube.com', keepParams: ['v', 't'], keepHash: false, keepAllParams: false, isEnabled: true, isDefault: true },
|
||||
{ id: 'yt-short', domainPattern: 'youtu.be', keepParams: ['t'], keepHash: false, keepAllParams: false, isEnabled: true, isDefault: true }
|
||||
]
|
||||
const exceptions = useLocalStorage('url-cleaner-exceptions', defaultExceptions)
|
||||
|
||||
// Helper to match domain with glob pattern
|
||||
const matchDomain = (pattern, domain) => {
|
||||
// Escape regex chars except *
|
||||
const regexString = '^' + pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*') + '$'
|
||||
return new RegExp(regexString, 'i').test(domain)
|
||||
}
|
||||
const {
|
||||
cleanedHistory,
|
||||
isWatchEnabled,
|
||||
exceptions,
|
||||
defaultExceptions,
|
||||
processUrl: baseProcessUrl,
|
||||
removeEntry,
|
||||
clearHistory
|
||||
} = useUrlCleaner()
|
||||
|
||||
// Watch for clipboard changes from extension
|
||||
watch(lastClipboardText, (newText) => {
|
||||
if (isWatchEnabled.value && newText) {
|
||||
processUrl(newText, true)
|
||||
baseProcessUrl(newText, true, writeClipboard)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -94,114 +87,16 @@ const handleClean = () => {
|
||||
if (inputUrl.value) {
|
||||
const urls = inputUrl.value.split(/\r?\n/).filter(line => line.trim().length > 0)
|
||||
urls.forEach(url => {
|
||||
processUrl(url.trim(), false)
|
||||
baseProcessUrl(url.trim(), false, writeClipboard)
|
||||
})
|
||||
inputUrl.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
const processUrl = (text, autoClipboard = false) => {
|
||||
try {
|
||||
// Basic URL validation
|
||||
if (!text.match(/^https?:\/\//i)) {
|
||||
// Not a URL, ignore in watch mode
|
||||
if (autoClipboard) return
|
||||
}
|
||||
|
||||
const originalLength = text.length
|
||||
let cleanedUrl = text
|
||||
|
||||
try {
|
||||
const urlObj = new URL(text)
|
||||
const hostname = urlObj.hostname
|
||||
|
||||
// Check for exceptions
|
||||
const matchedRule = exceptions.value.find(rule =>
|
||||
rule.isEnabled && matchDomain(rule.domainPattern, hostname)
|
||||
)
|
||||
|
||||
if (matchedRule) {
|
||||
if (!matchedRule.keepAllParams) {
|
||||
// Exception logic: keep specific params
|
||||
const params = new URLSearchParams(urlObj.search)
|
||||
const keys = Array.from(params.keys())
|
||||
|
||||
for (const key of keys) {
|
||||
if (!matchedRule.keepParams.includes(key)) {
|
||||
params.delete(key)
|
||||
}
|
||||
}
|
||||
|
||||
urlObj.search = params.toString()
|
||||
}
|
||||
|
||||
if (!matchedRule.keepHash) {
|
||||
urlObj.hash = ''
|
||||
}
|
||||
} else {
|
||||
// Default behavior: remove all query params and hash
|
||||
if (urlObj.search || urlObj.hash) {
|
||||
urlObj.search = ''
|
||||
urlObj.hash = ''
|
||||
}
|
||||
}
|
||||
|
||||
cleanedUrl = urlObj.toString()
|
||||
// Remove trailing slash if it wasn't there before? usually keep it standard
|
||||
} catch (e) {
|
||||
// Invalid URL format
|
||||
if (!autoClipboard) {
|
||||
// Show error or just return original
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// If no change, ignore in watch mode to avoid loops
|
||||
if (cleanedUrl === text && autoClipboard) {
|
||||
return
|
||||
}
|
||||
|
||||
const newLength = cleanedUrl.length
|
||||
const savedChars = originalLength - newLength
|
||||
const savedPercent = originalLength > 0 ? Math.round((savedChars / originalLength) * 100) : 0
|
||||
|
||||
// Add to history
|
||||
const entry = {
|
||||
id: Date.now(),
|
||||
original: text,
|
||||
cleaned: cleanedUrl,
|
||||
savedPercent,
|
||||
timestamp: new Date().toLocaleTimeString()
|
||||
}
|
||||
|
||||
cleanedHistory.value.unshift(entry)
|
||||
|
||||
// Limit history
|
||||
if (cleanedHistory.value.length > 50) {
|
||||
cleanedHistory.value.pop()
|
||||
}
|
||||
|
||||
// Auto-copy back to clipboard if in watch mode
|
||||
if (autoClipboard && savedChars > 0) {
|
||||
writeClipboard(cleanedUrl)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error processing URL:', e)
|
||||
}
|
||||
}
|
||||
|
||||
const copyToClipboard = (text) => {
|
||||
navigator.clipboard.writeText(text)
|
||||
}
|
||||
|
||||
const removeEntry = (id) => {
|
||||
cleanedHistory.value = cleanedHistory.value.filter(item => item.id !== id)
|
||||
}
|
||||
|
||||
const clearHistory = () => {
|
||||
cleanedHistory.value = []
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
if (isListening.value) {
|
||||
stopListening()
|
||||
|
||||
Reference in New Issue
Block a user