add file info button

This commit is contained in:
2026-05-23 12:19:51 +02:00
parent ca9ccab697
commit 0e04e9f49f
9 changed files with 249 additions and 10 deletions

View File

@@ -14508,4 +14508,56 @@ body.scroller-active #gchat-reopen-bubble {
to {
text-shadow: 0 0 6px #f1c40f, 0 0 10px #f1c40f;
}
}
/* =============================================
POST & FILE INFO MODAL STYLES
============================================= */
.info-table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
.info-table th {
text-align: left;
padding: 8px 10px 8px 0;
font-weight: bold;
color: #fff;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
font-size: 0.9em;
width: 35%;
}
.info-table td {
text-align: left;
padding: 8px 0;
color: #ccc;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
font-size: 0.9em;
}
.info-table td a {
color: var(--accent, #9f0);
text-decoration: none;
font-weight: 600;
}
.info-table td a:hover {
text-decoration: underline;
}
.info-table td code {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
padding: 2px 6px;
border-radius: 4px;
font-family: monospace;
font-size: 0.85em;
color: #eee;
}
.info-table tr:last-child th,
.info-table tr:last-child td {
border-bottom: none;
}

View File

@@ -529,7 +529,7 @@ window.cancelAnimFrame = (function () {
'login-modal', 'register-modal', 'forgot-modal', 'reset-modal',
'report-modal', 'halls-modal', 'metadata-modal', 'warning-modal',
'shortcuts-modal', 'upload-drag-modal', 'excluded-tags-overlay',
'content-warning-modal', 'gchat-img-modal', 'image-modal'
'content-warning-modal', 'gchat-img-modal', 'image-modal', 'info-modal'
];
modalIds.forEach(id => {
const el = document.getElementById(id);
@@ -910,6 +910,8 @@ window.cancelAnimFrame = (function () {
closeModal(loginModal);
closeModal(registerModal);
closeModal(shortcutsModal);
const infoModal = document.getElementById('info-modal');
if (infoModal) closeModal(infoModal);
}
});
@@ -8695,6 +8697,39 @@ if (navigator.vibrate) {
}
}
});
// Post & File Info Modal Logic
document.addEventListener('click', (e) => {
const infoBtn = e.target.closest('#a_info');
if (infoBtn) {
e.preventDefault();
const modal = document.getElementById('info-modal');
if (modal) {
modal.style.display = 'flex';
document.body.classList.add('modal-open');
}
return;
}
const closeBtn = e.target.closest('#info-modal-close');
if (closeBtn) {
e.preventDefault();
const modal = document.getElementById('info-modal');
if (modal) {
modal.style.display = 'none';
document.body.classList.remove('modal-open');
}
return;
}
// Close when clicking outside modal content
const infoModal = document.getElementById('info-modal');
if (infoModal && e.target === infoModal) {
infoModal.style.display = 'none';
document.body.classList.remove('modal-open');
}
});
// Ensure any navigation event restores the scroll state
window.addEventListener('pjax:start', () => {
if (window.resetGlobalScrollState) window.resetGlobalScrollState();

View File

@@ -518,6 +518,23 @@
"found": "Gefundene Metadaten:",
"no_results": "Keine weiteren Metadaten in dieser Datei gefunden."
},
"info_modal": {
"title": "Post- & Datei-Informationen",
"button_title": "Post- & Datei-Informationen",
"id": "Post-ID",
"source": "Quelle",
"uploader": "Hochgeladen von",
"uploaded_at": "Hochgeladen am",
"file_size": "Dateigröße",
"mime_type": "MIME-Typ",
"rating": "Bewertung",
"oc": "Originaler Inhalt (OC)",
"no": "Nein",
"direct_url": "Direkt-Link",
"view_file": "Datei anzeigen",
"metadata": "Metadaten",
"sha256": "SHA-256-Hash"
},
"meme": {
"add_text_layer": "Textebene hinzufügen",
"tags_label": "Tags (kommagetrennt)",

View File

@@ -522,6 +522,23 @@
"found": "Found in metadata:",
"no_results": "No additional metadata fields found in this file."
},
"info_modal": {
"title": "Post & File Information",
"button_title": "Post & File Info",
"id": "Post ID",
"source": "Source",
"uploader": "Uploader",
"uploaded_at": "Uploaded At",
"file_size": "File Size",
"mime_type": "MIME Type",
"rating": "Rating",
"oc": "Original Content (OC)",
"no": "No",
"direct_url": "Direct URL",
"view_file": "View File",
"metadata": "Metadata",
"sha256": "SHA-256 Hash"
},
"meme": {
"add_text_layer": "Add Text Layer",
"tags_label": "Tags (comma separated)",

View File

@@ -518,6 +518,23 @@
"found": "Gevonden in metadata:",
"no_results": "Geen extra metadata-velden gevonden in dit bestand."
},
"info_modal": {
"title": "Post- & Bestandsinformatie",
"button_title": "Post- & Bestandsinformatie",
"id": "Post-ID",
"source": "Bron",
"uploader": "Uploader",
"uploaded_at": "Geüpload op",
"file_size": "Bestandsgrootte",
"mime_type": "MIME-type",
"rating": "Beoordeling",
"oc": "Originele Content (OC)",
"no": "Nee",
"direct_url": "Directe URL",
"view_file": "Bestand bekijken",
"metadata": "Metadata",
"sha256": "SHA-256 Hash"
},
"meme": {
"add_text_layer": "Tekstlaag Toevoegen",
"tags_label": "Tags (gescheiden door komma's)",

View File

@@ -521,6 +521,23 @@
"found": "In den Metadaten gefunden:",
"no_results": "Keine weiteren Metadatenfelder in dieser Datei gefunden."
},
"info_modal": {
"title": "Pfosten- & Datei-Informationen",
"button_title": "Pfosten- & Datei-Informationen",
"id": "Pfosten-ID",
"source": "Quelle",
"uploader": "Hochgeladen von",
"uploaded_at": "Hochgeladen am",
"file_size": "Dateigröße",
"mime_type": "MIME-Typ",
"rating": "Bewertung",
"oc": "Originaler Inhalt (OC)",
"no": "Nein",
"direct_url": "Direkte Weltnetzadresse",
"view_file": "Datei betrachten",
"metadata": "Metadaten",
"sha256": "SHA-256-Streuwert"
},
"meme": {
"add_text_layer": "Textebene hinzufügen",
"tags_label": "Etiketten (kommagetrennt)",

View File

@@ -636,6 +636,7 @@ export default {
dest: actitem.mime === 'video/youtube' ? actitem.dest : `${cfg.websrv.paths.images}/${actitem.dest}`,
mime: actitem.mime,
size: lib.formatSize(actitem.size),
checksum: actitem.checksum,
timestamp: {
timeago: lib.timeAgo(new Date(actitem.stamp * 1e3).toISOString(), lang),
timefull: new Date(actitem.stamp * 1e3).toISOString()

View File

@@ -95,9 +95,8 @@
<span class="badge badge-dark">
<a href="/{{ item.id }}" class="id-link" @if(user_alternative_infobox)style="display:none"@endif>{{ item.id }}</a>
@if(item.src.short)@if(!user_alternative_infobox) — @endif<a href="{{ item.src.long }}" target="_blank">{{ item.src.short }}</a>@endif
@if(session && !user_alternative_infobox) — [<a id="a_username" data-username="{{ item.username || '' }}" data-author-id="{{ item.author_id || '' }}" href="/user/{{ (item.username || '').toLowerCase() }}" @if(item.author_id) tooltip="ID: {{ item.author_id }}" @endif @if(item.author_color) style="color: {{ item.author_color }}" @endif>{!! item.author_display_name || item.username || 'unknown' !!}</a>] @endif
@if(item.is_oc)@if(!user_alternative_infobox || item.src.short) — @endif<span class="oc-badge" tooltip="Original Content">OC</span>@endif
@if(item.is_oc)@if(!user_alternative_infobox) — @endif<span class="oc-badge" tooltip="Original Content">OC</span>@endif
</span>
@if(!user_alternative_infobox) — <span class="badge badge-dark"><time class="timeago" tooltip="{{ item.timestamp.timefull }}">{{item.timestamp.timeago }}</time></span> — @endif
@if(halls_enabled && item.primaryHall)
@@ -107,14 +106,14 @@
@endif
@if(session)
<div class="gapRight">
@if(session)
@if(user_has_favorited)
<i class="iconset fa-solid fa-heart" id="a_favo" title="Favorite"></i>
@else
<i class="iconset fa-regular fa-heart" id="a_favo" title="Favorite"></i>
@endif
<i class="iconset fa-solid fa-circle-info" id="a_info" data-item-id="{{ item.id }}" title="{{ t('info_modal.button_title') || 'Post & File Info' }}"></i>
<i class="iconset {{ isSubscribed ? 'fa-solid' : 'fa-regular' }} fa-bell" id="subscribe-btn" data-item-id="{{ item.id }}" title="{{ isSubscribed ? 'Subscribed' : 'Subscribe' }}"></i>
<i class="iconset fa-solid fa-triangle-exclamation report-item-btn" data-item-id="{{ item.id }}" title="Report this post"></i>
@if(halls_enabled)
@@ -133,8 +132,10 @@
<i class="iconset fa-solid fa-thumbtack{{ item.is_pinned ? ' active' : '' }}" id="a_pin" data-pinned="{{ item.is_pinned }}" title="{{ item.is_pinned ? 'Unpin from main' : 'Pin to main' }}"></i>
<i class="iconset fa-solid fa-xmark" id="a_delete" title="Delete"></i>
@endif
@else
<i class="iconset fa-solid fa-circle-info" id="a_info" data-item-id="{{ item.id }}" title="{{ t('info_modal.button_title') || 'Post & File Info' }}"></i>
@endif
</div>
@endif
<span class="badge badge-dark" id="tags">
<span class="tags-inner">
@if(typeof item.tags !== "undefined")
@@ -192,3 +193,43 @@
{{-- RIGHT SIDEBAR: recent activity (fixed to viewport) --}}
</div>
<div id="info-modal" class="modal-overlay" style="display: none;">
<div class="modal-content" style="max-width: 500px;">
<div class="modal-body" style="padding: 20px 0; text-align: left;">
<table class="info-table">
<tr>
<th>{{ t('info_modal.file_size') || 'File Size' }}</th>
<td>{{ item.size }}</td>
</tr>
<tr>
<th>{{ t('info_modal.mime_type') || 'MIME Type' }}</th>
<td><code>{{ item.mime }}</code></td>
</tr>
@if(item.checksum)
<tr>
<th>{{ t('info_modal.sha256') || 'SHA-256 Hash' }}</th>
<td><code style="word-break: break-all;">{{ item.checksum.split('_bypass_')[0] }}</code></td>
</tr>
@endif
<tr>
<th>{{ t('info_modal.direct_url') || 'Direct URL' }}</th>
<td>
<a href="{{ item.mime === 'video/youtube' ? 'https://www.youtube.com/watch?v=' + item.dest.replace('yt:', '') : item.dest }}" target="_blank">
{{ t('info_modal.view_file') || 'View File' }}
</a>
</td>
</tr>
@if(item.src.short)
<tr>
<th>{{ t('info_modal.source') || 'Source' }}</th>
<td><a href="{{ item.src.long }}" target="_blank">{{ item.src.short }}</a></td>
</tr>
@endif
</table>
</div>
<div class="modal-actions" style="display: flex; justify-content: flex-end; gap: 10px;">
<button class="btn-secondary" id="info-modal-close">{{ t('common.close') || 'Close' }}</button>
</div>
</div>
</div>

View File

@@ -107,7 +107,7 @@
</div>
<div class="blahlol">
<span class="badge badge-dark">
<a href="/{{ item.id }}" class="id-link">{{ item.id }}</a>@if(item.src.short) — <a href="{{ item.src.long }}" target="_blank">{{ item.src.short }}</a>@endif @if(session) — [<a id="a_username" data-username="{{ item.username || '' }}" data-author-id="{{ item.author_id || '' }}" href="/user/{{ item_username_lower }}" @if(item.author_id) tooltip="ID: {{ item.author_id }}" @endif @if(item.author_color) style="color: {{ item.author_color }}" @endif>{!! item.author_display_name || item.username || 'unknown' !!}</a>] @endif @if(item.is_oc) — <span class="oc-badge" tooltip="Original Content">OC</span>@endif
<a href="/{{ item.id }}" class="id-link">{{ item.id }}</a>@if(session) — [<a id="a_username" data-username="{{ item.username || '' }}" data-author-id="{{ item.author_id || '' }}" href="/user/{{ item_username_lower }}" @if(item.author_id) tooltip="ID: {{ item.author_id }}" @endif @if(item.author_color) style="color: {{ item.author_color }}" @endif>{!! item.author_display_name || item.username || 'unknown' !!}</a>] @endif @if(item.is_oc) — <span class="oc-badge" tooltip="Original Content">OC</span>@endif
</span>
@if(halls_enabled && item.primaryHall)
<span class="badge hall-badge-wrap">
@@ -115,13 +115,14 @@
</span>
@endif
<span class="badge badge-dark"><time class="timeago" tooltip="{{ item.timestamp.timefull }}">{{item.timestamp.timeago }}</time></span>
@if(session)
<div class="gapRight">
@if(session)
@if(user_has_favorited)
<i class="iconset fa-solid fa-heart" id="a_favo" title="Favorite"></i>
@else
<i class="iconset fa-regular fa-heart" id="a_favo" title="Favorite"></i>
@endif
<i class="iconset fa-solid fa-circle-info" id="a_info" data-item-id="{{ item.id }}" title="{{ t('info_modal.button_title') || 'Post & File Info' }}"></i>
<i class="iconset {{ isSubscribed ? 'fa-solid' : 'fa-regular' }} fa-bell" id="subscribe-btn" data-item-id="{{ item.id }}" title="{{ isSubscribed ? 'Subscribed' : 'Subscribe' }}"></i>
<i class="iconset fa-solid fa-triangle-exclamation report-item-btn" data-item-id="{{ item.id }}" title="Report this post"></i>
@if(can_manage_item)
@@ -138,9 +139,10 @@
<i class="iconset fa-solid fa-thumbtack{{ item.is_pinned ? ' active' : '' }}" id="a_pin" data-pinned="{{ item.is_pinned }}" title="{{ item.is_pinned ? 'Unpin from main' : 'Pin to main' }}"></i>
<i class="iconset fa-solid fa-xmark" id="a_delete" title="Delete"></i>
@endif
@else
<i class="iconset fa-solid fa-circle-info" id="a_info" data-item-id="{{ item.id }}" title="{{ t('info_modal.button_title') || 'Post & File Info' }}"></i>
@endif
</div>
@endif
<span id="favs" @if(!item.favorites.length) hidden@endif style="margin-top: 5px; display: block;">
@each(item.favorites as fav)
<a href="/user/{{ fav.user.toLowerCase() }}" tooltip="{!! fav.display_name || fav.user !!}" flow="up"><img src="@if(fav.avatar_file)/a/{{ fav.avatar_file }}@elseif(fav.avatar)/t/{{ fav.avatar }}.webp@else/a/default.png@endif" style="height: 32px; width: 32px@if(fav.username_color); border-color: {{ fav.username_color }}@endif" loading="lazy" /></a>
@@ -162,4 +164,44 @@
{{-- RIGHT SIDEBAR: recent activity --}}
</div>
<div id="info-modal" class="modal-overlay" style="display: none;">
<div class="modal-content" style="max-width: 500px;">
<div class="modal-body" style="padding: 20px 0; text-align: left;">
<table class="info-table">
<tr>
<th>{{ t('info_modal.file_size') || 'File Size' }}</th>
<td>{{ item.size }}</td>
</tr>
<tr>
<th>{{ t('info_modal.mime_type') || 'MIME Type' }}</th>
<td><code>{{ item.mime }}</code></td>
</tr>
@if(item.checksum)
<tr>
<th>{{ t('info_modal.sha256') || 'SHA-256 Hash' }}</th>
<td><code style="word-break: break-all;">{{ item.checksum.split('_bypass_')[0] }}</code></td>
</tr>
@endif
<tr>
<th>{{ t('info_modal.direct_url') || 'Direct URL' }}</th>
<td>
<a href="{{ item.mime === 'video/youtube' ? 'https://www.youtube.com/watch?v=' + item.dest.replace('yt:', '') : item.dest }}" target="_blank">
{{ t('info_modal.view_file') || 'View File' }}
</a>
</td>
</tr>
@if(item.src.short)
<tr>
<th>{{ t('info_modal.source') || 'Source' }}</th>
<td><a href="{{ item.src.long }}" target="_blank">{{ item.src.short }}</a></td>
</tr>
@endif
</table>
</div>
<div class="modal-actions" style="display: flex; justify-content: flex-end; gap: 10px;">
<button class="btn-secondary" id="info-modal-close">{{ t('common.close') || 'Close' }}</button>
</div>
</div>
</div>