Files
WaifuBoard/desktop/src/scripts/utils/list-modal-manager.js
itsskaiya 28ff6ccc68 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.
2025-12-16 21:50:22 -05:00

226 lines
8.7 KiB
JavaScript

const ListModalManager = {
API_BASE: '/api',
currentData: null,
isInList: false,
currentEntry: null,
STATUS_MAP: {
CURRENT: 'CURRENT',
COMPLETED: 'COMPLETED',
PLANNING: 'PLANNING',
PAUSED: 'PAUSED',
DROPPED: 'DROPPED',
REPEATING: 'REPEATING'
},
getEntryType(data) {
if (!data) return 'ANIME';
if (data.entry_type) return data.entry_type.toUpperCase();
return 'ANIME';
},
async checkIfInList(entryId, source = 'anilist', entryType) {
if (!AuthUtils.isAuthenticated()) return false;
const url = `${this.API_BASE}/list/entry/${entryId}?source=${source}&entry_type=${entryType}`;
try {
const response = await fetch(url, {
headers: AuthUtils.getSimpleAuthHeaders()
});
if (response.ok) {
const data = await response.json();
this.isInList = data.found && !!data.entry;
this.currentEntry = data.entry || null;
} else {
this.isInList = false;
this.currentEntry = null;
}
return this.isInList;
} catch (error) {
console.error('Error checking list entry:', error);
return false;
}
},
updateButton(buttonSelector = '.hero-buttons .btn-blur') {
const btn = document.querySelector(buttonSelector);
if (!btn) return;
if (this.isInList) {
btn.innerHTML = `
<svg width="20" height="20" fill="currentColor" viewBox="0 0 24 24">
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
</svg>
In Your ${this.currentData?.format ? 'Library' : 'List'}
`;
btn.style.background = 'rgba(34, 197, 94, 0.2)';
btn.style.color = '#22c55e';
btn.style.borderColor = 'rgba(34, 197, 94, 0.3)';
} else {
btn.innerHTML = `+ Add to ${this.currentData?.format ? 'Library' : 'List'}`;
btn.style.background = null;
btn.style.color = null;
btn.style.borderColor = null;
}
},
open(data, source = 'anilist') {
if (!AuthUtils.isAuthenticated()) {
NotificationUtils.error('Please log in to manage your list.');
return;
}
this.currentData = data;
const entryType = this.getEntryType(data);
const totalUnits = data.episodes || data.chapters || data.volumes || 999;
const modalTitle = document.getElementById('modal-title');
const deleteBtn = document.getElementById('modal-delete-btn');
const progressLabel = document.querySelector('label[for="entry-progress"]') ||
document.getElementById('progress-label');
if (this.isInList && this.currentEntry) {
document.getElementById('entry-status').value = this.currentEntry.status || 'PLANNING';
document.getElementById('entry-progress').value = this.currentEntry.progress || 0;
document.getElementById('entry-score').value = this.currentEntry.score || '';
document.getElementById('entry-start-date').value = this.currentEntry.start_date?.split('T')[0] || '';
document.getElementById('entry-end-date').value = this.currentEntry.end_date?.split('T')[0] || '';
document.getElementById('entry-repeat-count').value = this.currentEntry.repeat_count || 0;
document.getElementById('entry-notes').value = this.currentEntry.notes || '';
document.getElementById('entry-is-private').checked = this.currentEntry.is_private === true || this.currentEntry.is_private === 1;
modalTitle.textContent = `Edit ${entryType === 'ANIME' ? 'List' : 'Library'} Entry`;
deleteBtn.style.display = 'block';
} else {
document.getElementById('entry-status').value = 'PLANNING';
document.getElementById('entry-progress').value = 0;
document.getElementById('entry-score').value = '';
document.getElementById('entry-start-date').value = '';
document.getElementById('entry-end-date').value = '';
document.getElementById('entry-repeat-count').value = 0;
document.getElementById('entry-notes').value = '';
document.getElementById('entry-is-private').checked = false;
modalTitle.textContent = `Add to ${entryType === 'ANIME' ? 'List' : 'Library'}`;
deleteBtn.style.display = 'none';
}
const statusSelect = document.getElementById('entry-status');
[...statusSelect.options].forEach(opt => {
if (opt.value === 'CURRENT') {
opt.textContent = entryType === 'ANIME' ? 'Watching' : 'Reading';
}
});
if (progressLabel) {
if (entryType === 'ANIME') {
progressLabel.textContent = 'Episodes Watched';
} else if (entryType === 'MANGA') {
progressLabel.textContent = 'Chapters Read';
} else {
progressLabel.textContent = 'Volumes/Parts Read';
}
}
document.getElementById('entry-progress').max = totalUnits;
document.getElementById('add-list-modal').classList.add('active');
},
close() {
document.getElementById('add-list-modal').classList.remove('active');
},
async save(entryId, source = 'anilist') {
const uiStatus = document.getElementById('entry-status').value;
const status = this.STATUS_MAP[uiStatus] || uiStatus;
const progress = parseInt(document.getElementById('entry-progress').value) || 0;
const scoreValue = document.getElementById('entry-score').value;
const score = scoreValue ? parseFloat(scoreValue) : null;
const start_date = document.getElementById('entry-start-date').value || null;
const end_date = document.getElementById('entry-end-date').value || null;
const repeat_count = parseInt(document.getElementById('entry-repeat-count').value) || 0;
const notes = document.getElementById('entry-notes').value || null;
const is_private = document.getElementById('entry-is-private').checked;
const entryType = this.getEntryType(this.currentData);
try {
const response = await fetch(`${this.API_BASE}/list/entry`, {
method: 'POST',
headers: AuthUtils.getAuthHeaders(),
body: JSON.stringify({
entry_id: entryId,
source,
entry_type: entryType,
status,
progress,
score,
start_date,
end_date,
repeat_count,
notes,
is_private
})
});
if (!response.ok) throw new Error('Failed to save entry');
const data = await response.json();
this.isInList = true;
this.currentEntry = data.entry;
this.updateButton();
this.close();
NotificationUtils.success(this.isInList ? 'Updated successfully!' : 'Added to your list!');
} catch (error) {
console.error('Error saving to list:', error);
NotificationUtils.error('Failed to save. Please try again.');
}
},
async delete(entryId, source = 'anilist') {
if (!confirm(`Remove this ${this.getEntryType(this.currentData).toLowerCase()} from your list?`)) {
return;
}
const entryType = this.getEntryType(this.currentData);
try {
const response = await fetch(
`${this.API_BASE}/list/entry/${entryId}?source=${source}&entry_type=${entryType}`,
{
method: 'DELETE',
headers: AuthUtils.getSimpleAuthHeaders()
}
);
if (!response.ok) throw new Error('Failed to delete entry');
this.isInList = false;
this.currentEntry = null;
this.updateButton();
this.close();
NotificationUtils.success('Removed from your list');
} catch (error) {
console.error('Error deleting from list:', error);
NotificationUtils.error('Failed to remove. Please try again.');
}
}
};
document.addEventListener('DOMContentLoaded', () => {
const modal = document.getElementById('add-list-modal');
if (modal) {
modal.addEventListener('click', (e) => {
if (e.target.id === 'add-list-modal') {
ListModalManager.close();
}
});
}
});
window.ListModalManager = ListModalManager;