|
|
{% extends 'base.html' %} |
|
|
|
|
|
{% block title %}/{{ board.slug }}/ - {{ board.name }}{% endblock %} |
|
|
|
|
|
{% block content %} |
|
|
<div class="mb-6 px-4 pt-4"> |
|
|
<h1 class="text-2xl font-bold text-gray-900 tracking-tight">/{{ board.slug }}/ - {{ board.name }}</h1> |
|
|
<p class="text-gray-500 text-sm mt-1">{{ board.description }}</p> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="space-y-4 px-2 pb-24"> |
|
|
{% for thread in threads %} |
|
|
<div class="bg-white p-4 rounded-xl border border-gray-100 shadow-sm" id="post-{{ thread.id }}"> |
|
|
<div class="flex gap-4"> |
|
|
|
|
|
{% if thread.files %} |
|
|
<div class="flex-shrink-0 flex flex-col gap-2"> |
|
|
{% for file in thread.files[:1] %} |
|
|
{% if file.filename.endswith(('.mp4', '.webm')) %} |
|
|
<video controls class="w-24 h-24 sm:w-32 sm:h-32 object-cover rounded-lg bg-black"> |
|
|
<source src="{{ url_for('static', filename='uploads/' + file.filename) }}" type="video/{{ file.filename.split('.')[-1] }}"> |
|
|
</video> |
|
|
{% else %} |
|
|
<img |
|
|
src="{{ url_for('static', filename='uploads/' + file.filename) }}" |
|
|
data-thumb="{{ url_for('static', filename='uploads/' + file.filename) }}" |
|
|
data-full="{{ url_for('static', filename='uploads/' + file.filename) }}" |
|
|
alt="Post image" |
|
|
class="post-image w-24 h-24 sm:w-32 sm:h-32 object-cover rounded-lg cursor-pointer hover:opacity-90 transition-opacity bg-gray-100" |
|
|
> |
|
|
{% endif %} |
|
|
{% endfor %} |
|
|
{% if thread.files|length > 1 %} |
|
|
<span class="text-xs text-gray-500 text-center">+ {{ thread.files|length - 1 }} autres</span> |
|
|
{% endif %} |
|
|
</div> |
|
|
{% endif %} |
|
|
|
|
|
<div class="flex-grow min-w-0"> |
|
|
<div class="flex items-center text-xs text-gray-500 mb-2 space-x-2"> |
|
|
<span class="font-bold text-gray-900">{{ thread.nickname }}</span> |
|
|
<span>{{ thread.created_at.strftime('%d/%m %H:%M') }}</span> |
|
|
<a href="{{ url_for('thread', thread_id=thread.id) }}" class="text-blue-500 hover:underline">N° {{ thread.id }}</a> |
|
|
</div> |
|
|
|
|
|
<div class="text-gray-800 text-base leading-relaxed break-words line-clamp-4 mb-3"> |
|
|
{{ thread.content|default('', true)|truncate(300)|format_post(post_context) }} |
|
|
</div> |
|
|
|
|
|
<div class="flex items-center justify-between"> |
|
|
<a href="{{ url_for('thread', thread_id=thread.id) }}" class="text-sm font-medium text-blue-600 hover:text-blue-800 bg-blue-50 px-3 py-1 rounded-full"> |
|
|
Voir le fil |
|
|
{% if thread.replies|length > 0 %} |
|
|
<span class="ml-1 text-blue-800">({{ thread.replies|length }})</span> |
|
|
{% endif %} |
|
|
</a> |
|
|
<button class="text-green-600 hover:text-green-800 p-2 rounded-full hover:bg-green-50" onclick="sharePost('{{ thread.id }}', '{{ url_for('thread', thread_id=thread.id, _external=True) }}')" title="Partager"> |
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> |
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z" /> |
|
|
</svg> |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
|
|
|
{% set recent_replies = thread.replies[-2:] %} |
|
|
{% if recent_replies %} |
|
|
<div class="mt-4 space-y-2 border-l-2 border-gray-100 pl-3"> |
|
|
{% for reply in recent_replies %} |
|
|
<div class="text-sm text-gray-600"> |
|
|
<span class="text-xs text-gray-400">>> {{ reply.created_at.strftime('%H:%M') }}</span> |
|
|
<span class="line-clamp-1">{{ reply.content|truncate(100) }}</span> |
|
|
</div> |
|
|
{% endfor %} |
|
|
</div> |
|
|
{% endif %} |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
{% else %} |
|
|
<div class="text-center text-gray-400 py-12 bg-white rounded-xl border border-gray-100 border-dashed"> |
|
|
<p>C'est calme ici. Lancez une conversation.</p> |
|
|
</div> |
|
|
{% endfor %} |
|
|
</div> |
|
|
|
|
|
|
|
|
<button id="fab-button" onclick="togglePostForm()" class="fixed bottom-6 right-6 bg-blue-600 hover:bg-blue-700 text-white rounded-full p-4 shadow-lg transition-transform hover:scale-105 active:scale-95 z-40 flex items-center justify-center w-14 h-14"> |
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor"> |
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" /> |
|
|
</svg> |
|
|
</button> |
|
|
|
|
|
|
|
|
<div id="post-form-container" class="hidden fixed inset-0 z-50 flex items-end sm:items-center justify-center bg-gray-900 bg-opacity-40 backdrop-blur-sm p-0 sm:p-4"> |
|
|
|
|
|
<div class="absolute inset-0" onclick="togglePostForm()"></div> |
|
|
|
|
|
<div class="bg-white w-full max-w-lg rounded-t-2xl sm:rounded-2xl shadow-2xl transform transition-all relative z-10 flex flex-col max-h-[90vh]"> |
|
|
<div class="flex justify-between items-center p-4 border-b border-gray-100"> |
|
|
<h3 class="text-lg font-bold text-gray-900">Nouveau fil</h3> |
|
|
<button onclick="togglePostForm()" class="text-gray-400 hover:text-gray-600 p-2 rounded-full hover:bg-gray-100"> |
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> |
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> |
|
|
</svg> |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<div class="p-6 overflow-y-auto"> |
|
|
<form method="POST" enctype="multipart/form-data" class="space-y-5"> |
|
|
{{ form.hidden_tag() }} |
|
|
<div class="hidden"> |
|
|
{{ form.honeypot() }} |
|
|
</div> |
|
|
<div id="nickname-field"> |
|
|
<label class="block text-gray-700 text-sm font-semibold mb-2" for="nickname">Pseudo</label> |
|
|
{{ form.nickname(class="w-full bg-gray-50 text-gray-900 border border-gray-200 rounded-lg py-3 px-4 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent", placeholder="Pseudo (Optionnel)") }} |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-gray-700 text-sm font-semibold mb-2" for="content">Message</label> |
|
|
{{ form.content(class="w-full bg-gray-50 text-gray-900 border border-gray-200 rounded-lg py-3 px-4 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent h-40 resize-none text-base", placeholder="Écrivez quelque chose...") }} |
|
|
{% for error in form.content.errors %} |
|
|
<p class="text-red-500 text-xs mt-1">{{ error }}</p> |
|
|
{% endfor %} |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-gray-700 text-sm font-semibold mb-2" for="files">Images ou Vidéos</label> |
|
|
<div class="flex items-center justify-center w-full"> |
|
|
<label for="files" class="flex flex-col items-center justify-center w-full h-32 border-2 border-gray-200 border-dashed rounded-lg cursor-pointer bg-gray-50 hover:bg-gray-100"> |
|
|
<div class="flex flex-col items-center justify-center pt-5 pb-6"> |
|
|
<svg class="w-8 h-8 mb-4 text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 16"> |
|
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0 0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2"/> |
|
|
</svg> |
|
|
<p class="text-sm text-gray-500"><span class="font-semibold">Cliquez pour uploader</span></p> |
|
|
</div> |
|
|
{{ form.files(class="hidden", multiple=True, accept="image/*,video/*", onchange="previewFiles(this, 'preview-board')") }} |
|
|
</label> |
|
|
</div> |
|
|
<div id="preview-board" class="flex flex-wrap gap-2 mt-2"></div> |
|
|
{% for error in form.files.errors %} |
|
|
<p class="text-red-500 text-xs mt-1">{{ error }}</p> |
|
|
{% endfor %} |
|
|
</div> |
|
|
<div class="pt-2"> |
|
|
{{ form.submit(class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-6 rounded-lg cursor-pointer shadow-md active:scale-95 transition-transform") }} |
|
|
</div> |
|
|
</form> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
{% endblock %} |
|
|
|