Spaces:
Runtime error
Runtime error
Claude commited on
fix: 공감 동점 시 같은 등수 뱃지 + 3초 쿨다운 추가
Browse files- 같은 공감 수 = 같은 뱃지 (dense rank, 예: 동점 1위 둘 다 🥇)
- 공감 클릭 후 3초간 모든 공감 버튼 비활성화
https://claude.ai/code/session_016DE3uHLsVmA5Uv7gZGGaKT
- components/chat_board.py +15 -4
components/chat_board.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
import plotly.express as px
|
| 3 |
from collections import Counter
|
|
@@ -9,6 +10,7 @@ from config import DASHBOARD_FETCH_LIMIT, LIVE_BOARD_FETCH_LIMIT, LIVE_REFRESH_I
|
|
| 9 |
|
| 10 |
|
| 11 |
_RANK_BADGES = {1: "🥇", 2: "🥈", 3: "🥉"}
|
|
|
|
| 12 |
|
| 13 |
|
| 14 |
def _anon_ip(raw_ip: str) -> str:
|
|
@@ -62,6 +64,7 @@ def _live_chat_board_core(supabase, room_name, user_role, teacher_auth, student_
|
|
| 62 |
my_likes = set()
|
| 63 |
badge_map = {}
|
| 64 |
my_anon_ip = ""
|
|
|
|
| 65 |
if use_likes:
|
| 66 |
likes_data = fetch_room_likes(supabase, room_name)
|
| 67 |
likes_count = Counter(item['opinion_id'] for item in likes_data)
|
|
@@ -69,9 +72,16 @@ def _live_chat_board_core(supabase, room_name, user_role, teacher_auth, student_
|
|
| 69 |
raw_client_ip = get_client_ip()
|
| 70 |
if raw_client_ip:
|
| 71 |
my_anon_ip = _anon_ip(raw_client_ip)
|
| 72 |
-
#
|
| 73 |
-
|
| 74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
|
| 76 |
def delete_chat_msg(msg_id):
|
| 77 |
try:
|
|
@@ -86,6 +96,7 @@ def _live_chat_board_core(supabase, room_name, user_role, teacher_auth, student_
|
|
| 86 |
def do_toggle_like(msg_id):
|
| 87 |
toggle_like(supabase, msg_id, room_name, student_name)
|
| 88 |
fetch_room_likes.clear()
|
|
|
|
| 89 |
|
| 90 |
def render_msg(row):
|
| 91 |
formatted_timestamp = format_kst_datetime(row.get("timestamp", ""))
|
|
@@ -97,7 +108,7 @@ def _live_chat_board_core(supabase, room_name, user_role, teacher_auth, student_
|
|
| 97 |
# 셀프 공감 차단: 의견 작성자 IP와 현재 사용자 IP 비교
|
| 98 |
row_ip = str(row.get("ip_address", "")).strip() if hasattr(row, "get") else ""
|
| 99 |
is_self = bool(my_anon_ip and row_ip and my_anon_ip == row_ip)
|
| 100 |
-
like_disabled = is_self or not use_likes
|
| 101 |
like_label = f"👍 {count}" if count > 0 else "👍"
|
| 102 |
like_type = "primary" if is_liked else "secondary"
|
| 103 |
|
|
|
|
| 1 |
+
import time
|
| 2 |
import streamlit as st
|
| 3 |
import plotly.express as px
|
| 4 |
from collections import Counter
|
|
|
|
| 10 |
|
| 11 |
|
| 12 |
_RANK_BADGES = {1: "🥇", 2: "🥈", 3: "🥉"}
|
| 13 |
+
_LIKE_COOLDOWN = 3
|
| 14 |
|
| 15 |
|
| 16 |
def _anon_ip(raw_ip: str) -> str:
|
|
|
|
| 64 |
my_likes = set()
|
| 65 |
badge_map = {}
|
| 66 |
my_anon_ip = ""
|
| 67 |
+
on_like_cooldown = False
|
| 68 |
if use_likes:
|
| 69 |
likes_data = fetch_room_likes(supabase, room_name)
|
| 70 |
likes_count = Counter(item['opinion_id'] for item in likes_data)
|
|
|
|
| 72 |
raw_client_ip = get_client_ip()
|
| 73 |
if raw_client_ip:
|
| 74 |
my_anon_ip = _anon_ip(raw_client_ip)
|
| 75 |
+
# 동점 처리: 같은 공감 수 = 같은 등수 (dense rank)
|
| 76 |
+
distinct_counts = sorted({c for c in likes_count.values() if c > 0}, reverse=True)[:3]
|
| 77 |
+
count_to_rank = {c: rank for rank, c in enumerate(distinct_counts, 1)}
|
| 78 |
+
badge_map = {
|
| 79 |
+
oid: _RANK_BADGES[count_to_rank[cnt]]
|
| 80 |
+
for oid, cnt in likes_count.items()
|
| 81 |
+
if cnt > 0 and cnt in count_to_rank
|
| 82 |
+
}
|
| 83 |
+
# 쿨다운 확인
|
| 84 |
+
on_like_cooldown = (time.time() - st.session_state.get('_last_like_ts', 0)) < _LIKE_COOLDOWN
|
| 85 |
|
| 86 |
def delete_chat_msg(msg_id):
|
| 87 |
try:
|
|
|
|
| 96 |
def do_toggle_like(msg_id):
|
| 97 |
toggle_like(supabase, msg_id, room_name, student_name)
|
| 98 |
fetch_room_likes.clear()
|
| 99 |
+
st.session_state['_last_like_ts'] = time.time()
|
| 100 |
|
| 101 |
def render_msg(row):
|
| 102 |
formatted_timestamp = format_kst_datetime(row.get("timestamp", ""))
|
|
|
|
| 108 |
# 셀프 공감 차단: 의견 작성자 IP와 현재 사용자 IP 비교
|
| 109 |
row_ip = str(row.get("ip_address", "")).strip() if hasattr(row, "get") else ""
|
| 110 |
is_self = bool(my_anon_ip and row_ip and my_anon_ip == row_ip)
|
| 111 |
+
like_disabled = is_self or not use_likes or on_like_cooldown
|
| 112 |
like_label = f"👍 {count}" if count > 0 else "👍"
|
| 113 |
like_type = "primary" if is_liked else "secondary"
|
| 114 |
|