266 lines
7.6 KiB
JavaScript
266 lines
7.6 KiB
JavaScript
class QuoteApp {
|
||
constructor() {
|
||
this.quotes = [];
|
||
this.author = "Денис Шулепов";
|
||
this.serverUrl = window.location.origin;
|
||
this.init();
|
||
}
|
||
|
||
init() {
|
||
this.bindEvents();
|
||
this.loadQuotes();
|
||
}
|
||
|
||
async loadQuotes() {
|
||
try {
|
||
const response = await fetch(`${this.serverUrl}/api/quotes`);
|
||
if (response.ok) {
|
||
this.quotes = await response.json();
|
||
this.renderQuotes();
|
||
} else {
|
||
throw new Error("Ошибка загрузки цитат");
|
||
}
|
||
} catch (error) {
|
||
console.error("Ошибка при загрузке цитат:", error);
|
||
this.showError("Не удалось загрузить цитаты с сервера");
|
||
this.showEmptyState();
|
||
}
|
||
}
|
||
|
||
bindEvents() {
|
||
// События удалены - только автоматическая публикация
|
||
}
|
||
|
||
showEmptyState() {
|
||
const quotesFeed = document.getElementById("quotesFeed");
|
||
quotesFeed.innerHTML = `
|
||
<div class="empty-state">
|
||
<h2>Добро пожаловать в гейский цитатник!</h2>
|
||
<p>Ожидаем первую автоматическую публикацию цитаты...</p>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
renderQuotes() {
|
||
const quotesFeed = document.getElementById("quotesFeed");
|
||
|
||
if (this.quotes.length === 0) {
|
||
this.showEmptyState();
|
||
return;
|
||
}
|
||
|
||
quotesFeed.innerHTML = this.quotes
|
||
.map((quote) => this.createQuoteCard(quote))
|
||
.join("");
|
||
this.bindQuoteActions();
|
||
}
|
||
|
||
createQuoteCard(quote) {
|
||
const date = new Date(quote.date).toLocaleDateString("ru-RU", {
|
||
day: "numeric",
|
||
month: "long",
|
||
year: "numeric",
|
||
hour: "2-digit",
|
||
minute: "2-digit",
|
||
});
|
||
|
||
return `
|
||
<div class="quote-card" data-id="${quote.id}">
|
||
<div class="quote-header">
|
||
<img src="photo_2023-10-27_20-55-45.jpg" alt="${this.escapeHtml(
|
||
quote.author
|
||
)}" class="quote-avatar">
|
||
<div class="quote-author-info">
|
||
<div class="quote-author">${this.escapeHtml(
|
||
quote.author
|
||
)}</div>
|
||
<div class="quote-date">${date}</div>
|
||
</div>
|
||
</div>
|
||
<div class="quote-text">${this.escapeHtml(quote.text)}</div>
|
||
<div class="quote-actions">
|
||
<button class="quote-action-btn copy-btn" onclick="app.copyQuote('${
|
||
quote.id
|
||
}')">
|
||
📋 Копировать
|
||
</button>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
bindQuoteActions() {
|
||
// Дополнительные обработчики для карточек цитат
|
||
}
|
||
|
||
async generateNewQuote() {
|
||
const loading = document.getElementById("loading");
|
||
|
||
loading.style.display = "block";
|
||
|
||
try {
|
||
const response = await fetch(`${this.serverUrl}/api/quotes`, {
|
||
method: "POST",
|
||
});
|
||
|
||
if (response.ok) {
|
||
const newQuote = await response.json();
|
||
this.quotes.unshift(newQuote);
|
||
this.renderQuotes();
|
||
this.showSuccess("Новая цитата создана!");
|
||
} else {
|
||
throw new Error("Ошибка сервера");
|
||
}
|
||
} catch (error) {
|
||
console.error("Ошибка при генерации цитаты:", error);
|
||
this.showError(
|
||
"Не удалось сгенерировать цитату. Проверьте подключение к серверу."
|
||
);
|
||
} finally {
|
||
loading.style.display = "none";
|
||
}
|
||
}
|
||
|
||
async deleteQuote(id) {
|
||
if (confirm("Вы уверены, что хотите удалить эту цитату?")) {
|
||
try {
|
||
const response = await fetch(`${this.serverUrl}/api/quotes/${id}`, {
|
||
method: "DELETE",
|
||
});
|
||
|
||
if (response.ok) {
|
||
this.quotes = this.quotes.filter((q) => q.id !== id);
|
||
this.renderQuotes();
|
||
this.showSuccess("Цитата удалена!");
|
||
} else {
|
||
throw new Error("Ошибка сервера");
|
||
}
|
||
} catch (error) {
|
||
console.error("Ошибка при удалении цитаты:", error);
|
||
this.showError("Не удалось удалить цитату");
|
||
}
|
||
}
|
||
}
|
||
|
||
copyQuote(id) {
|
||
const quote = this.quotes.find((q) => q.id === id);
|
||
if (quote) {
|
||
const text = `"${quote.text}"\n— ${quote.author}`;
|
||
|
||
if (navigator.clipboard) {
|
||
navigator.clipboard
|
||
.writeText(text)
|
||
.then(() => {
|
||
this.showSuccess("Цитата скопирована в буфер обмена!");
|
||
})
|
||
.catch(() => {
|
||
this.fallbackCopyToClipboard(text);
|
||
});
|
||
} else {
|
||
this.fallbackCopyToClipboard(text);
|
||
}
|
||
}
|
||
}
|
||
|
||
fallbackCopyToClipboard(text) {
|
||
const textArea = document.createElement("textarea");
|
||
textArea.value = text;
|
||
textArea.style.position = "fixed";
|
||
textArea.style.left = "-999999px";
|
||
textArea.style.top = "-999999px";
|
||
document.body.appendChild(textArea);
|
||
textArea.focus();
|
||
textArea.select();
|
||
|
||
try {
|
||
document.execCommand("copy");
|
||
this.showSuccess("Цитата скопирована в буфер обмена!");
|
||
} catch (err) {
|
||
console.error("Не удалось скопировать текст:", err);
|
||
this.showError("Не удалось скопировать цитату");
|
||
}
|
||
|
||
document.body.removeChild(textArea);
|
||
}
|
||
|
||
showError(message) {
|
||
this.showNotification(message, "error");
|
||
}
|
||
|
||
showSuccess(message) {
|
||
this.showNotification(message, "success");
|
||
}
|
||
|
||
showNotification(message, type = "info") {
|
||
const notification = document.createElement("div");
|
||
notification.className = `notification notification-${type}`;
|
||
notification.textContent = message;
|
||
|
||
notification.style.cssText = `
|
||
position: fixed;
|
||
top: 20px;
|
||
right: 20px;
|
||
background: ${
|
||
type === "error"
|
||
? "#e74c3c"
|
||
: type === "success"
|
||
? "#27ae60"
|
||
: "#ff6b9d"
|
||
};
|
||
color: white;
|
||
padding: 15px 20px;
|
||
border-radius: 10px;
|
||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
|
||
z-index: 1000;
|
||
max-width: 300px;
|
||
font-size: 14px;
|
||
animation: slideIn 0.3s ease-out;
|
||
`;
|
||
|
||
document.body.appendChild(notification);
|
||
|
||
setTimeout(() => {
|
||
notification.style.animation = "slideOut 0.3s ease-out";
|
||
setTimeout(() => {
|
||
document.body.removeChild(notification);
|
||
}, 300);
|
||
}, 3000);
|
||
}
|
||
|
||
escapeHtml(text) {
|
||
const div = document.createElement("div");
|
||
div.textContent = text;
|
||
return div.innerHTML;
|
||
}
|
||
}
|
||
|
||
// Добавляем стили для анимаций уведомлений
|
||
const style = document.createElement("style");
|
||
style.textContent = `
|
||
@keyframes slideIn {
|
||
from {
|
||
transform: translateX(100%);
|
||
opacity: 0;
|
||
}
|
||
to {
|
||
transform: translateX(0);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
@keyframes slideOut {
|
||
from {
|
||
transform: translateX(0);
|
||
opacity: 1;
|
||
}
|
||
to {
|
||
transform: translateX(100%);
|
||
opacity: 0;
|
||
}
|
||
}
|
||
`;
|
||
document.head.appendChild(style);
|
||
|
||
// Инициализация приложения
|
||
const app = new QuoteApp();
|