We are launching a docker version (server version) today so we want to just organize the repo so its easier to navigate.
226 lines
8.7 KiB
JavaScript
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; |