mirror of
https://github.com/rangermix/TwitchDropsMiner.git
synced 2026-05-26 07:08:04 +00:00
Merge pull request #7 from rangermix/feat/inventory-benefit-display
feat: Display benefits as individual lines with icon, name, and type
This commit is contained in:
@@ -40,6 +40,7 @@ COPY web/ ./web/
|
||||
|
||||
# Create data directory for persistent storage
|
||||
RUN mkdir -p /app/data && chmod 777 /app/data
|
||||
RUN mkdir -p /app/logs && chmod 777 /app/logs
|
||||
|
||||
# Expose web port
|
||||
EXPOSE 8080
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
@@ -9,6 +9,7 @@ from datetime import timedelta
|
||||
from enum import Enum, auto
|
||||
from typing import TYPE_CHECKING, Any, Literal, NewType
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections import abc # noqa
|
||||
from typing import TypeAlias
|
||||
@@ -57,7 +58,6 @@ ONLINE_DELAY = timedelta(seconds=120)
|
||||
WATCH_INTERVAL = timedelta(seconds=59)
|
||||
|
||||
|
||||
|
||||
class State(Enum):
|
||||
"""Application state machine states."""
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
|
||||
@@ -12,6 +13,9 @@ if TYPE_CHECKING:
|
||||
from src.web.managers.cache import ImageCache
|
||||
|
||||
|
||||
logger = logging.getLogger("TwitchDrops")
|
||||
|
||||
|
||||
class InventoryManager:
|
||||
"""Manages drop campaign inventory display in the web interface.
|
||||
|
||||
@@ -41,6 +45,16 @@ class InventoryManager:
|
||||
|
||||
drops_data = []
|
||||
for drop in campaign.drops:
|
||||
# Collect full benefit data (filter out benefits without images)
|
||||
benefits_data = [
|
||||
{
|
||||
"name": benefit.name,
|
||||
"type": benefit.type.name,
|
||||
"image_url": str(benefit.image_url),
|
||||
}
|
||||
for benefit in drop.benefits
|
||||
if benefit.image_url
|
||||
]
|
||||
drops_data.append(
|
||||
{
|
||||
"id": drop.id,
|
||||
@@ -50,7 +64,7 @@ class InventoryManager:
|
||||
"progress": drop.progress,
|
||||
"is_claimed": drop.is_claimed,
|
||||
"can_claim": drop.can_claim,
|
||||
"rewards": drop.rewards_text(),
|
||||
"benefits": benefits_data,
|
||||
"starts_at": drop.starts_at.isoformat(),
|
||||
"ends_at": drop.ends_at.isoformat(),
|
||||
}
|
||||
|
||||
@@ -494,14 +494,36 @@ function renderInventory() {
|
||||
}
|
||||
|
||||
const claimedText = t.gui?.inventory?.status?.claimed || 'Claimed';
|
||||
const dropsHtml = campaign.drops.map(drop => `
|
||||
<div class="drop-item ${drop.is_claimed ? 'claimed' : ''} ${drop.can_claim ? 'active' : ''}">
|
||||
<div><strong>${drop.name}</strong></div>
|
||||
<div>${drop.rewards}</div>
|
||||
<div>${drop.current_minutes} / ${drop.required_minutes} minutes (${Math.round(drop.progress * 100)}%)</div>
|
||||
${drop.is_claimed ? `<div>✓ ${claimedText}</div>` : ''}
|
||||
</div>
|
||||
`).join('');
|
||||
const dropsHtml = campaign.drops.map(drop => {
|
||||
// Generate HTML for each benefit as its own line
|
||||
let benefitsHtml = '';
|
||||
if (drop.benefits && drop.benefits.length > 0) {
|
||||
benefitsHtml = drop.benefits.map(benefit =>
|
||||
`<div class="benefit-item">
|
||||
<img src="${benefit.image_url}" alt="${benefit.name}" class="benefit-icon" onerror="this.style.display='none'">
|
||||
<div class="benefit-info">
|
||||
<span class="benefit-name">${benefit.name}</span>
|
||||
<span class="benefit-type">(${benefit.type})</span>
|
||||
</div>
|
||||
</div>`
|
||||
).join('');
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="drop-item ${drop.is_claimed ? 'claimed' : ''} ${drop.can_claim ? 'active' : ''}">
|
||||
<div class="drop-item-header">
|
||||
<div class="drop-item-info">
|
||||
<div><strong>${drop.name}</strong></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="benefits-list">
|
||||
${benefitsHtml}
|
||||
</div>
|
||||
<div>${drop.current_minutes} / ${drop.required_minutes} minutes (${Math.round(drop.progress * 100)}%)</div>
|
||||
${drop.is_claimed ? `<div>✓ ${claimedText}</div>` : ''}
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
// Make campaign name clickable if link_url is available
|
||||
const campaignNameHtml = campaign.link_url
|
||||
|
||||
@@ -500,6 +500,66 @@ header h1 {
|
||||
border-left-color: var(--accent-color);
|
||||
}
|
||||
|
||||
.drop-item-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.drop-item-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.benefits-list {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.benefit-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 8px;
|
||||
background: var(--bg-panel);
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.benefit-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 4px;
|
||||
object-fit: cover;
|
||||
border: 2px solid var(--border-color);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.benefit-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.benefit-name {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.benefit-type {
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Settings */
|
||||
.settings-container {
|
||||
max-width: 800px;
|
||||
|
||||
Reference in New Issue
Block a user