Skip to content
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
22 changes: 17 additions & 5 deletions src/chigame/chat/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 4.2.20 on 2025-05-01 15:28
# Generated by Django 4.2.20 on 2025-05-19 02:36

from django.conf import settings
from django.db import migrations, models
Expand All @@ -21,19 +21,19 @@ class Migration(migrations.Migration):
],
),
migrations.CreateModel(
name="LiveChatUser",
name="LiveChatMessage",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("sent_at", models.DateTimeField(auto_now_add=True)),
("content", models.TextField()),
("live_chat", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="chat.livechat")),
("user", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name="LiveChatMessage",
name="LiveChatUser",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("sent_at", models.DateTimeField(auto_now_add=True)),
("content", models.TextField()),
("live_chat", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="chat.livechat")),
("user", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
Expand All @@ -45,4 +45,16 @@ class Migration(migrations.Migration):
related_name="live_chats", through="chat.LiveChatUser", to=settings.AUTH_USER_MODEL
),
),
migrations.CreateModel(
name="LiveChatMessageReaction",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("content", models.CharField(max_length=10)),
("message", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="chat.livechatmessage")),
("user", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
"unique_together": {("user", "message", "content")},
},
),
]
Empty file.
1 change: 1 addition & 0 deletions src/chigame/chat/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class LiveChat(models.Model):

name = models.TextField(null=False)
users: models.ManyToManyField = models.ManyToManyField(User, through="LiveChatUser", related_name="live_chats")
background_image = models.ImageField(upload_to="chat_backrounds/", null=True, blank=True)

def __str__(self):
return f"LiveChat with name:'{self.name}'"
Expand Down
5 changes: 5 additions & 0 deletions src/chigame/chat/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,9 @@ def chat(request, chat_id):
chat = get_object_or_404(LiveChat, id=chat_id)
messages = LiveChatMessage.objects.filter(live_chat=chat).order_by("sent_at")

# Handle background image upload
if request.method == "POST" and "background" in request.FILES:
if request.user in chat.users.all():
chat.background_image = request.FILES["background"]
chat.save()
return render(request, "chat/index.html", {"chat": chat, "messages": messages})
7 changes: 7 additions & 0 deletions src/config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,13 @@
"can_download_file",
]

# Updated project to allow media uploads such as for
# background images in chat

MEDIA_URL = "/media/"

MEDIA_ROOT = Path.home() / "Downloads"

# CHANNELS
# ------------------------------------------------------------------------------
# https://channels.readthedocs.io/en/stable/topics/channel_layers.html
Expand Down
5 changes: 5 additions & 0 deletions src/config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
if settings.DEBUG:
# This allows the error pages to be debugged during development, just visit
# these url in browser to see how these error pages look like.
urlpatterns += static(
settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT,
)

urlpatterns += [
path(
"400/",
Expand Down
5 changes: 1 addition & 4 deletions src/sandbox/markdown-demo/md_testing/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,4 @@
from django.urls import path
from md_app.views import markdown_content_view

urlpatterns = [
path("", markdown_content_view, name="markdown"),
path("admin/", admin.site.urls),
]
urlpatterns = [path("", markdown_content_view, name="markdown"), path("admin/", admin.site.urls)]
41 changes: 41 additions & 0 deletions src/static/js/chat-menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
document.addEventListener('DOMContentLoaded', () => {
const menu = document.getElementById('message-menu');
let clickCount = 0;
let clickTimer = null;

// Helper to reset our click counter
function resetClicks() {
clickCount = 0;
if (clickTimer) clearTimeout(clickTimer);
}

// Show the menu just under the clicked message
function showMenu(msgEl) {
const rect = msgEl.getBoundingClientRect();
menu.style.top = `${rect.bottom + window.scrollY}px`;
menu.style.left = `${rect.left + window.scrollX}px`;
menu.style.display = 'block';
}

// Attach to every message
document.querySelectorAll('.chat-message').forEach(msgEl => {
msgEl.addEventListener('click', e => {
clickCount++;
if (clickCount === 3) {
showMenu(msgEl);
resetClicks();
} else {
// if no third click within 600ms, reset
if (clickTimer) clearTimeout(clickTimer);
clickTimer = setTimeout(resetClicks, 600);
}
});
});

// Clicking anywhere else hides the menu
document.addEventListener('click', e => {
if (!menu.contains(e.target) && !e.target.closest('.chat-message')) {
menu.style.display = 'none';
}
});
});
18 changes: 17 additions & 1 deletion src/templates/chat/components/_header.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@
<div class="bg-danger text-white p-3 rounded-top">
<div class="bg-danger text-white p-3 rounded-top position-relative">
<h2 class="chigame-text mb-0">{{ chat.name }}</h2>
<div class="small mt-1 opacity-75">Welcome, {{ request.user.username }}</div>
<!-- background‐upload button -->
<button id="bg-button"
class="btn btn-sm btn-light"
style="position:absolute;
top:8px;
right:8px;
z-index:10"
title="Change chat background">🖼️</button>
<!-- hidden file‐upload form -->
<form id="bg-form"
method="POST"
enctype="multipart/form-data"
style="display:none">
{% csrf_token %}
<input type="file" id="bg-input" name="background" accept="image/*" />
</form>
</div>
10 changes: 10 additions & 0 deletions src/templates/chat/components/_script.html
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,14 @@
container.scrollTop = container.scrollHeight;
});
};

// Handle background upload
const bgBtn = document.getElementById("bg-button");
const bgInput = document.getElementById("bg-input");
const bgForm = document.getElementById("bg-form");

bgBtn.addEventListener("click", () => bgInput.click());
bgInput.addEventListener("change", () => {
if (bgInput.files.length) bgForm.submit();
});
</script>
41 changes: 40 additions & 1 deletion src/templates/chat/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{% extends "base.html" %}
{% load static %}

{% block content %}
<style>
Expand All @@ -11,11 +12,49 @@
height: 100%;
min-height: 0;
}

.message-menu {
position: absolute;
background: #fff;
border: 1px solid #ccc;
border-radius: 4px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
z-index: 1000;
}

.message-menu ul {
margin: 0;
padding: 0;
list-style: none;
}

.message-menu li {
padding: 8px 12px;
cursor: pointer;
}

.message-menu li:hover {
background: #f0f0f0;
}
</style>
<div class="chat-container h-100 d-flex flex-column">

<div id="chat-container"
class="chat-container h-100 d-flex flex-column position-relative"
{% if chat.background_image %} style="background-image: url('{{ chat.background_image.url }}'); background-size: cover; background-position: center;" {% else %} style="background-size: cover; background-position: center;" {% endif %}>
{% include "chat/components/_header.html" %}
{% include "chat/components/_messages.html" %}
{% include "chat/components/_input.html" %}
{% include "chat/components/_script.html" %}

{# Blank triple-click menu #}
<div id="message-menu" class="message-menu" style="display: none;">
<ul>
<li data-action="copy">Copy Message</li>
<li data-action="delete">Delete Message</li>
<li data-action="pin">Pin Message</li>
</ul>
</div>
</div>

<script src="{% static 'js/chat-menu.js' %}"></script>
{% endblock content %}