Organized the differences between server and docker versions.
We are launching a docker version (server version) today so we want to just organize the repo so its easier to navigate.
This commit is contained in:
176
desktop/src/scripts/utils/search-manager.js
Normal file
176
desktop/src/scripts/utils/search-manager.js
Normal file
@@ -0,0 +1,176 @@
|
||||
const SearchManager = {
|
||||
availableExtensions: [],
|
||||
searchTimeout: null,
|
||||
|
||||
init(inputSelector, resultsSelector, type = 'anime') {
|
||||
const searchInput = document.querySelector(inputSelector);
|
||||
const searchResults = document.querySelector(resultsSelector);
|
||||
|
||||
if (!searchInput || !searchResults) {
|
||||
console.error('Search elements not found');
|
||||
return;
|
||||
}
|
||||
|
||||
this.loadExtensions(type);
|
||||
|
||||
searchInput.addEventListener('input', (e) => {
|
||||
const query = e.target.value;
|
||||
clearTimeout(this.searchTimeout);
|
||||
|
||||
if (query.length < 2) {
|
||||
searchResults.classList.remove('active');
|
||||
searchResults.innerHTML = '';
|
||||
searchInput.style.borderRadius = '99px';
|
||||
return;
|
||||
}
|
||||
|
||||
this.searchTimeout = setTimeout(() => {
|
||||
this.search(query, type, searchResults);
|
||||
}, 300);
|
||||
});
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!e.target.closest('.search-wrapper')) {
|
||||
searchResults.classList.remove('active');
|
||||
searchInput.style.borderRadius = '99px';
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async loadExtensions(type) {
|
||||
try {
|
||||
const endpoint = type === 'book' ? '/api/extensions/book' : '/api/extensions/anime';
|
||||
const res = await fetch(endpoint);
|
||||
const data = await res.json();
|
||||
this.availableExtensions = data.extensions || [];
|
||||
console.log(`${type} extensions loaded:`, this.availableExtensions);
|
||||
} catch (err) {
|
||||
console.error('Error loading extensions:', err);
|
||||
}
|
||||
},
|
||||
|
||||
async search(query, type, resultsContainer) {
|
||||
try {
|
||||
let apiUrl, extensionName = null, finalQuery = query;
|
||||
|
||||
const parts = query.split(':');
|
||||
if (parts.length >= 2) {
|
||||
const potentialExtension = parts[0].trim().toLowerCase();
|
||||
const foundExtension = this.availableExtensions.find(
|
||||
ext => ext.toLowerCase() === potentialExtension
|
||||
);
|
||||
|
||||
if (foundExtension) {
|
||||
extensionName = foundExtension;
|
||||
finalQuery = parts.slice(1).join(':').trim();
|
||||
|
||||
if (finalQuery.length === 0) {
|
||||
this.renderResults([], resultsContainer, type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (extensionName) {
|
||||
const endpoint = type === 'book' ? 'books' : '';
|
||||
apiUrl = `/api/search/${endpoint ? endpoint + '/' : ''}${extensionName}?q=${encodeURIComponent(finalQuery)}`;
|
||||
} else {
|
||||
const endpoint = type === 'book' ? '/api/search/books' : '/api/search';
|
||||
apiUrl = `${endpoint}?q=${encodeURIComponent(query)}`;
|
||||
}
|
||||
|
||||
const res = await fetch(apiUrl);
|
||||
const data = await res.json();
|
||||
|
||||
const results = (data.results || []).map(item => ({
|
||||
...item,
|
||||
isExtensionResult: !!extensionName,
|
||||
extensionName
|
||||
}));
|
||||
|
||||
this.renderResults(results, resultsContainer, type);
|
||||
} catch (err) {
|
||||
console.error("Search Error:", err);
|
||||
this.renderResults([], resultsContainer, type);
|
||||
}
|
||||
},
|
||||
|
||||
renderResults(results, container, type) {
|
||||
container.innerHTML = '';
|
||||
|
||||
if (!results || results.length === 0) {
|
||||
container.innerHTML = '<div style="padding:1rem; color:#888; text-align:center">No results found</div>';
|
||||
} else {
|
||||
results.forEach(item => {
|
||||
const resultElement = this.createResultElement(item, type);
|
||||
container.appendChild(resultElement);
|
||||
});
|
||||
}
|
||||
|
||||
container.classList.add('active');
|
||||
const searchInput = container.previousElementSibling || document.querySelector('.search-input');
|
||||
if (searchInput) {
|
||||
searchInput.style.borderRadius = '12px 12px 0 0';
|
||||
}
|
||||
},
|
||||
|
||||
createResultElement(item, type) {
|
||||
const element = document.createElement('a');
|
||||
element.className = 'search-item';
|
||||
|
||||
if (type === 'book') {
|
||||
const title = item.title?.english || item.title?.romaji || "Unknown";
|
||||
const img = item.coverImage?.medium || item.coverImage?.large || '';
|
||||
const rating = Number.isInteger(item.averageScore) ? `${item.averageScore}%` : item.averageScore || 'N/A';
|
||||
const year = item.seasonYear || item.startDate?.year || '????';
|
||||
const format = item.format || 'MANGA';
|
||||
|
||||
element.href = item.isExtensionResult
|
||||
? `/book/${item.extensionName}/${item.id}`
|
||||
: `/book/${item.id}`;
|
||||
|
||||
element.innerHTML = `
|
||||
<img src="${img}" class="search-poster" alt="${title}">
|
||||
<div class="search-info">
|
||||
<div class="search-title">${title}</div>
|
||||
<div class="search-meta">
|
||||
<span class="rating-pill">${rating}</span>
|
||||
<span>• ${year}</span>
|
||||
<span>• ${format}</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
|
||||
const title = item.title?.english || item.title?.romaji || "Unknown Title";
|
||||
const img = item.coverImage?.medium || item.coverImage?.large || '';
|
||||
const rating = item.averageScore ? `${item.averageScore}%` : 'N/A';
|
||||
const year = item.seasonYear || '';
|
||||
const format = item.format || 'TV';
|
||||
|
||||
element.href = item.isExtensionResult
|
||||
? `/anime/${item.extensionName}/${item.id}`
|
||||
: `/anime/${item.id}`;
|
||||
|
||||
element.innerHTML = `
|
||||
<img src="${img}" class="search-poster" alt="${title}">
|
||||
<div class="search-info">
|
||||
<div class="search-title">${title}</div>
|
||||
<div class="search-meta">
|
||||
<span class="rating-pill">${rating}</span>
|
||||
<span>• ${year}</span>
|
||||
<span>• ${format}</span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
return element;
|
||||
},
|
||||
|
||||
getTitle(item) {
|
||||
return item.title?.english || item.title?.romaji || "Unknown Title";
|
||||
}
|
||||
};
|
||||
|
||||
window.SearchManager = SearchManager;
|
||||
Reference in New Issue
Block a user