Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom rate limit box location #293

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 33 additions & 12 deletions static/css/rate_limits.css
Original file line number Diff line number Diff line change
@@ -1,29 +1,47 @@
.rate-limits-section {
position: fixed;
top: 50px;
left: 50%;
transform: translateX(-50%);
top: 52px;
left: 180px;
padding: 16px 24px;
z-index: 999;
display: flex;
flex-direction: column;
align-items: center;
align-items: flex-start;
transition: none; /* Remove transition for smoother dragging */
cursor: move;
user-select: none;
}

.rate-limits-section .rate-limits-toggle {
cursor: grab;
}

.rate-limits-section.dragging {
opacity: 0.9;
cursor: grabbing !important;
}

.rate-limits-section.dragging .rate-limits-toggle {
cursor: grabbing;
}

.rate-limits-toggle {
background-color: #1a1a1a;
background-color: rgba(26, 26, 26, 0.95);
padding: 10px 15px;
border-radius: 0 0 4px 4px;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
cursor: pointer;
transition: background-color 0.2s;
min-width: 150px;
display: flex;
justify-content: space-between;
align-items: center;
color: #f4f4f4;
font-weight: 600;
text-align: center;
line-height: 0.6;
font-size: 1.0em;
font-weight: 500;
text-align: left;
line-height: 1.2;
font-size: 0.95em;
backdrop-filter: blur(10px);
}

.rate-limits-name {
Expand All @@ -48,8 +66,10 @@
}

.rate-limits-container {
position: relative;
background-color: #1a1a1a;
position: absolute;
top: calc(100% + 4px);
left: 0;
background-color: rgba(26, 26, 26, 0.95);
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
width: 300px;
Expand All @@ -60,6 +80,7 @@
display: none;
opacity: 0;
transition: opacity 0.3s ease;
backdrop-filter: blur(10px);
}

.rate-limits-container.show {
Expand Down
249 changes: 249 additions & 0 deletions static/js/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,134 @@ function initializeReleaseNotes() {
}

// Rate Limits Functions
function initializeRateLimits() {
const rateLimitsSection = document.querySelector('.rate-limits-section');
const container = document.getElementById('rate-limits-container');
let isDragging = false;
let startX, startY;
let lastX, lastY;

// Load saved position from localStorage
const savedPosition = localStorage.getItem('rateLimitsPosition');
if (savedPosition) {
try {
const { x, y } = JSON.parse(savedPosition);
rateLimitsSection.style.left = `${x}px`;
rateLimitsSection.style.top = `${y}px`;
} catch (e) {
console.error('Error loading saved position:', e);
}
}

function getPosition(element) {
const style = window.getComputedStyle(element);
return {
left: parseInt(style.left),
top: parseInt(style.top)
};
}

function handleDragStart(e) {
// Only start dragging from the toggle area
if (!e.target.closest('.rate-limits-toggle')) return;

isDragging = true;
const pos = getPosition(rateLimitsSection);

if (e.type === 'touchstart') {
startX = e.touches[0].clientX - pos.left;
startY = e.touches[0].clientY - pos.top;
} else {
startX = e.clientX - pos.left;
startY = e.clientY - pos.top;
}

// Add dragging class for visual feedback
rateLimitsSection.classList.add('dragging');
}

function handleDragMove(e) {
if (!isDragging) return;

e.preventDefault();

let clientX, clientY;
if (e.type === 'touchmove') {
clientX = e.touches[0].clientX;
clientY = e.touches[0].clientY;
} else {
clientX = e.clientX;
clientY = e.clientY;
}

// Calculate new position
let newX = clientX - startX;
let newY = clientY - startY;

// Get viewport and element dimensions
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const rect = rateLimitsSection.getBoundingClientRect();

// Constrain to viewport bounds with padding
const padding = 10;
newX = Math.max(padding, Math.min(newX, viewportWidth - rect.width - padding));
newY = Math.max(padding, Math.min(newY, viewportHeight - rect.height - padding));

// Update position
rateLimitsSection.style.left = `${newX}px`;
rateLimitsSection.style.top = `${newY}px`;

lastX = newX;
lastY = newY;
}

function handleDragEnd() {
if (!isDragging) return;

isDragging = false;
rateLimitsSection.classList.remove('dragging');

// Save final position
if (lastX !== undefined && lastY !== undefined) {
localStorage.setItem('rateLimitsPosition', JSON.stringify({
x: lastX,
y: lastY
}));
}
}

// Mouse events
rateLimitsSection.addEventListener('mousedown', handleDragStart);
document.addEventListener('mousemove', handleDragMove);
document.addEventListener('mouseup', handleDragEnd);

// Touch events
rateLimitsSection.addEventListener('touchstart', handleDragStart, { passive: false });
document.addEventListener('touchmove', handleDragMove, { passive: false });
document.addEventListener('touchend', handleDragEnd);

// Handle edge cases
window.addEventListener('resize', () => {
const pos = getPosition(rateLimitsSection);
const rect = rateLimitsSection.getBoundingClientRect();
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
const padding = 10;

let newX = Math.max(padding, Math.min(pos.left, viewportWidth - rect.width - padding));
let newY = Math.max(padding, Math.min(pos.top, viewportHeight - rect.height - padding));

rateLimitsSection.style.left = `${newX}px`;
rateLimitsSection.style.top = `${newY}px`;

localStorage.setItem('rateLimitsPosition', JSON.stringify({
x: newX,
y: newY
}));
});
}

function toggleRateLimits() {
const container = document.getElementById('rate-limits-container');
container.classList.toggle('show');
Expand Down Expand Up @@ -176,6 +304,124 @@ function fetchRateLimitInfo() {
window.toggleRateLimits = toggleRateLimits;
window.fetchRateLimitInfo = fetchRateLimitInfo;

function initializeTorrentActivity() {
const container = document.getElementById('torrentActivityContainer');
const toggle = document.getElementById('torrentActivityToggle');
const toggleIcon = toggle?.querySelector('i');
const list = document.getElementById('torrentActivityList');
const clearButton = document.getElementById('torrentActivityClear');

if (!container || !toggle || !list) return;

// Clear button functionality
if (clearButton) {
clearButton.addEventListener('click', async () => {
if (confirm('Are you sure you want to clear the recent torrent history?')) {
try {
const response = await fetch('/base/api/clear-torrent-activity', {
method: 'POST'
});
const data = await response.json();
if (data.success) {
list.innerHTML = '<div class="no-activity">No recent activity</div>';
}
} catch (error) {
console.error('Error clearing torrent activity:', error);
}
}
});
}

// Load initial state from localStorage
const isHidden = localStorage.getItem('torrentActivityHidden') === 'true';
if (isHidden) {
container.classList.add('hidden');
toggleIcon?.classList.remove('fa-chevron-right');
toggleIcon?.classList.add('fa-chevron-left');
}

// Toggle visibility
toggle.addEventListener('click', () => {
container.classList.toggle('hidden');
const isNowHidden = container.classList.contains('hidden');
localStorage.setItem('torrentActivityHidden', isNowHidden);

// Toggle icon direction
if (toggleIcon) {
if (isNowHidden) {
toggleIcon.classList.remove('fa-chevron-right');
toggleIcon.classList.add('fa-chevron-left');
} else {
toggleIcon.classList.remove('fa-chevron-left');
toggleIcon.classList.add('fa-chevron-right');
}
}
});

function formatTimeAgo(dateString) {
// Parse the ISO date string as UTC
const date = new Date(dateString + 'Z'); // Append Z to treat as UTC
const now = new Date();
const diffMs = now - date;
const seconds = Math.floor(diffMs / 1000);

// Return appropriate time string based on elapsed time
if (seconds < 30) return 'just now';
if (seconds < 60) return `${seconds}s ago`;

const minutes = Math.floor(seconds / 60);
if (minutes < 60) return `${minutes}m ago`;

const hours = Math.floor(minutes / 60);
if (hours < 24) return `${hours}h ago`;

const days = Math.floor(hours / 24);
if (days < 7) return `${days}d ago`;

// For older dates, return the formatted date
return date.toLocaleDateString(undefined, {
year: 'numeric',
month: 'short',
day: 'numeric'
});
}

function updateActivity() {
fetch('/base/api/torrent-activity')
.then(response => response.json())
.then(data => {
if (data.success) {
if (!data.activities || data.activities.length === 0) {
list.innerHTML = '<div class="no-activity">No recent activity</div>';
} else {
list.innerHTML = data.activities.map(activity => `
<div class="torrent-activity-item">
<div class="torrent-name">${activity.torrent_name}</div>
<div class="torrent-details">
${activity.media_title} (${activity.media_year})
${activity.season ? ` S${String(activity.season).padStart(2, '0')}` : ''}
${activity.episode ? `E${String(activity.episode).padStart(2, '0')}` : ''}
</div>
<div class="torrent-user">Added by ${activity.username}</div>
<div class="torrent-time">${formatTimeAgo(activity.added_at)}</div>
</div>
`).join('');
}
} else {
list.innerHTML = '<div class="error">Failed to load activity</div>';
}
})
.catch(error => {
console.error('Error fetching torrent activity:', error);
list.innerHTML = '<div class="error">Error loading activity</div>';
});
}

// Update activity every 30 seconds
updateActivity();
setInterval(updateActivity, 30000);
}

// Initialize everything when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
// Set updating content state to false before initializing tooltips
Expand Down Expand Up @@ -263,4 +509,7 @@ document.addEventListener('DOMContentLoaded', function() {
// Open GitHub repository in new tab
window.open('https://github.com/godver3/cli_debrid', '_blank');
});

initializeTorrentActivity();
initializeRateLimits();
});