ui change to improve tag list load
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
<!-- /app/templates/_tag_cards.html -->
|
||||
{% for tag in tag_data %}
|
||||
<div class="tag-card" data-tag-id="{{ tag.id }}" data-tag-name="{{ tag.tag }}" data-tag-kind="{{ tag.kind }}">
|
||||
<a href="{{ url_for('main.gallery', tag=tag.tag) }}" class="tag-card-link">
|
||||
<div class="tag-preview">
|
||||
{% for image in tag.images %}
|
||||
<img class="tag-thumb"
|
||||
src="{{ url_for('main.serve_image', filename=(image.thumb_path or image.filepath) | replace('/images/', '') ) }}"
|
||||
loading="lazy"
|
||||
alt="">
|
||||
{% endfor %}
|
||||
{% if tag.images|length == 0 %}
|
||||
<div class="tag-thumb tag-thumb-empty"></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="tag-card-info">
|
||||
<span class="tag-icon">
|
||||
{%- if tag.kind == 'artist' -%}🎨
|
||||
{%- elif tag.kind == 'character' -%}👤
|
||||
{%- elif tag.kind == 'series' -%}📺
|
||||
{%- elif tag.kind == 'fandom' -%}🎭
|
||||
{%- elif tag.kind == 'rating' -%}⚠️
|
||||
{%- elif tag.kind == 'archive' -%}🗜️
|
||||
{%- elif tag.kind == 'source' -%}🌐
|
||||
{%- elif tag.kind == 'post' -%}📌
|
||||
{%- else -%}#{%- endif -%}
|
||||
</span>
|
||||
<span class="tag-name" title="{{ tag.name }}">{{ tag.name }}</span>
|
||||
<span class="tag-count">{{ tag.count }}</span>
|
||||
</div>
|
||||
</a>
|
||||
<button class="tag-edit-btn" title="Edit tag" data-tag-id="{{ tag.id }}">✏️</button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
+106
-34
@@ -70,41 +70,27 @@
|
||||
</div>
|
||||
|
||||
{% if tag_data %}
|
||||
<div class="tag-grid">
|
||||
{% for tag in tag_data %}
|
||||
<div class="tag-card" data-tag-id="{{ tag.id }}" data-tag-name="{{ tag.tag }}" data-tag-kind="{{ tag.kind }}">
|
||||
<a href="{{ url_for('main.gallery', tag=tag.tag) }}" class="tag-card-link">
|
||||
<div class="tag-preview">
|
||||
{% for image in tag.images %}
|
||||
<img class="tag-thumb"
|
||||
src="{{ url_for('main.serve_image', filename=(image.thumb_path or image.filepath) | replace('/images/', '') ) }}"
|
||||
loading="lazy"
|
||||
alt="">
|
||||
{% endfor %}
|
||||
{% if tag.images|length == 0 %}
|
||||
<div class="tag-thumb tag-thumb-empty"></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="tag-card-info">
|
||||
<span class="tag-icon">
|
||||
{%- if tag.kind == 'artist' -%}🎨
|
||||
{%- elif tag.kind == 'character' -%}👤
|
||||
{%- elif tag.kind == 'series' -%}📺
|
||||
{%- elif tag.kind == 'fandom' -%}🎭
|
||||
{%- elif tag.kind == 'rating' -%}⚠️
|
||||
{%- elif tag.kind == 'archive' -%}🗜️
|
||||
{%- elif tag.kind == 'source' -%}🌐
|
||||
{%- elif tag.kind == 'post' -%}📌
|
||||
{%- else -%}#{%- endif -%}
|
||||
</span>
|
||||
<span class="tag-name" title="{{ tag.name }}">{{ tag.name }}</span>
|
||||
<span class="tag-count">{{ tag.count }}</span>
|
||||
</div>
|
||||
</a>
|
||||
<button class="tag-edit-btn" title="Edit tag" data-tag-id="{{ tag.id }}">✏️</button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="tag-grid" id="tagGrid">
|
||||
{% include '_tag_cards.html' %}
|
||||
</div>
|
||||
|
||||
<!-- Infinite scroll sentinel and loading indicator -->
|
||||
<div id="tagScrollSentinel" class="scroll-sentinel" {% if not has_more %}style="display: none;"{% endif %}></div>
|
||||
<div id="tagLoadingIndicator" class="loading-indicator" style="display: none;">
|
||||
<span class="spinner"></span> Loading more tags...
|
||||
</div>
|
||||
|
||||
<!-- Infinite scroll state -->
|
||||
<script>
|
||||
window.tagListState = {
|
||||
kind: {{ (active_kind | tojson) if active_kind else 'null' }},
|
||||
offset: {{ tag_data | length }},
|
||||
pageSize: {{ page_size }},
|
||||
hasMore: {{ 'true' if has_more else 'false' }},
|
||||
loading: false,
|
||||
total: {{ total_count }}
|
||||
};
|
||||
</script>
|
||||
{% else %}
|
||||
<p class="empty-state">No tags found in this category.</p>
|
||||
{% endif %}
|
||||
@@ -146,4 +132,90 @@
|
||||
</div>
|
||||
|
||||
<script src="{{ url_for('static', filename='js/tag-editor.js') }}"></script>
|
||||
<script>
|
||||
// Tag list infinite scroll
|
||||
(function() {
|
||||
const state = window.tagListState;
|
||||
if (!state) return;
|
||||
|
||||
const grid = document.getElementById('tagGrid');
|
||||
const sentinel = document.getElementById('tagScrollSentinel');
|
||||
const loading = document.getElementById('tagLoadingIndicator');
|
||||
if (!grid || !sentinel) return;
|
||||
|
||||
async function loadMoreTags() {
|
||||
if (state.loading || !state.hasMore) return;
|
||||
|
||||
state.loading = true;
|
||||
loading.style.display = 'flex';
|
||||
|
||||
try {
|
||||
const params = new URLSearchParams({
|
||||
offset: state.offset,
|
||||
limit: state.pageSize
|
||||
});
|
||||
if (state.kind) {
|
||||
params.set('kind', state.kind);
|
||||
}
|
||||
|
||||
const response = await fetch(`/api/tags/list?${params}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.ok && data.html) {
|
||||
// Append new cards to grid
|
||||
grid.insertAdjacentHTML('beforeend', data.html);
|
||||
|
||||
// Update state
|
||||
state.offset = data.offset;
|
||||
state.hasMore = data.has_more;
|
||||
|
||||
// Re-attach edit button listeners for new cards
|
||||
attachEditListeners();
|
||||
|
||||
// Hide sentinel if no more data
|
||||
if (!state.hasMore) {
|
||||
sentinel.style.display = 'none';
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load more tags:', error);
|
||||
} finally {
|
||||
state.loading = false;
|
||||
loading.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// Re-attach edit button event listeners after loading new cards
|
||||
function attachEditListeners() {
|
||||
// The tag-editor.js handles this via event delegation on document,
|
||||
// so new buttons should work automatically
|
||||
}
|
||||
|
||||
// Set up IntersectionObserver for infinite scroll
|
||||
if ('IntersectionObserver' in window) {
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
if (entries[0].isIntersecting && state.hasMore && !state.loading) {
|
||||
loadMoreTags();
|
||||
}
|
||||
},
|
||||
{ rootMargin: '200px' }
|
||||
);
|
||||
observer.observe(sentinel);
|
||||
} else {
|
||||
// Fallback for older browsers: use scroll event
|
||||
let scrollTimeout;
|
||||
window.addEventListener('scroll', () => {
|
||||
clearTimeout(scrollTimeout);
|
||||
scrollTimeout = setTimeout(() => {
|
||||
const scrollY = window.scrollY + window.innerHeight;
|
||||
const docHeight = document.documentElement.scrollHeight;
|
||||
if (scrollY >= docHeight - 500 && state.hasMore && !state.loading) {
|
||||
loadMoreTags();
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user