AutoPercenty3/announcement_widget.py

267 lines
13 KiB
Python

# announcement_dialog.py
from PySide6.QtWidgets import (
QDialog, QVBoxLayout, QLabel, QPushButton, QScrollArea, QWidget,
QTextEdit, QHBoxLayout, QCheckBox, QMessageBox
)
from PySide6.QtCore import Qt, QTimer, QSettings
from PySide6.QtGui import QFont
from datetime import datetime, timezone
class AnnouncementDialog(QDialog):
def __init__(self, supabase_manager, user_info, parent=None):
super().__init__(parent)
self.supabase_manager = supabase_manager
self.user_info = user_info # dict, 예: {"email": "...", "name": "...", ...}
self.settings = QSettings("WhenRideMycar", "AutoPercenty3")
self.setWindowTitle("로그인 성공 - 공지사항 및 사용자 정보")
self.setMinimumSize(600, 500)
self.auto_close_timer = None # 자동 닫힘 타이머
self.remaining_seconds = 3 # 3초 카운트다운
self.init_ui()
self.load_announcements()
self.check_auto_close_condition()
self.handle_membership_validity()
def init_ui(self):
main_layout = QVBoxLayout(self)
main_layout.setContentsMargins(20, 20, 20, 20)
main_layout.setSpacing(15)
# 사용자 정보 패널
self.info_label = QLabel(self.format_user_info())
self.info_label.setStyleSheet("font-size: 16px; font-weight: bold; color: #333;")
main_layout.addWidget(self.info_label)
# 이벤트 안내 레이블 (나중에 get_membership_message의 결과를 표시)
self.event_label = QLabel("")
self.event_label.setStyleSheet("font-size: 14px; color: #d32f2f;")
main_layout.addWidget(self.event_label)
# 공지사항 헤더
header_label = QLabel("새 공지사항")
header_label.setStyleSheet("font-size: 16px; font-weight: bold;")
main_layout.addWidget(header_label)
# 스크롤 영역에 공지사항 목록
self.scroll_area = QScrollArea()
self.scroll_area.setWidgetResizable(True)
container = QWidget()
self.ann_layout = QVBoxLayout(container)
self.ann_layout.setSpacing(10)
container.setLayout(self.ann_layout)
self.scroll_area.setWidget(container)
main_layout.addWidget(self.scroll_area)
# "다음부터 공지사항 보지 않기" 체크박스
self.hide_checkbox = QCheckBox("다음부터 공지사항 보지 않기")
self.hide_checkbox.setToolTip("체크 시, 새로운 공지사항이 없으면 3초 후 자동으로 닫힙니다.")
main_layout.addWidget(self.hide_checkbox)
# 카운트다운 레이블 (초 단위)
self.countdown_label = QLabel("")
self.countdown_label.setAlignment(Qt.AlignRight)
self.countdown_label.setStyleSheet("font-size: 12px; color: #666;")
main_layout.addWidget(self.countdown_label)
# 닫기 버튼
self.close_btn = QPushButton("닫기")
self.close_btn.setStyleSheet("""
QPushButton {
background-color: #1877f2;
color: white;
border-radius: 4px;
padding: 8px;
font-size: 14px;
}
QPushButton:hover {
background-color: #166fe5;
}
""")
self.close_btn.clicked.connect(self.accept)
main_layout.addWidget(self.close_btn)
self.setLayout(main_layout)
def format_user_info(self):
# 환영 메시지: nickname이 있으면 사용, 없으면 이메일 사용
welcome = self.user_info.get("nickname") or self.user_info.get("email", "사용자")
email = self.user_info.get("email")
membership = self.user_info.get("membership_level", "free")
# 남은 사용 기간 계산 (payment_period_end와 현재 시간 비교)
payment_period_end_str = self.user_info.get("payment_period_end")
remaining_period_str = "N/A"
if payment_period_end_str:
try:
# payment_period_end_str에 타임존 정보가 없다면 UTC로 지정
payment_period_end = datetime.fromisoformat(payment_period_end_str)
if payment_period_end.tzinfo is None:
payment_period_end = payment_period_end.replace(tzinfo=timezone.utc)
now = datetime.now(timezone.utc)
if payment_period_end > now:
delta = payment_period_end - now
days = delta.days
hours, remainder = divmod(delta.seconds, 3600)
minutes, seconds = divmod(remainder, 60)
remaining_period_str = f"{days}{hours:02d}:{minutes:02d}:{seconds:02d}"
else:
remaining_period_str = "만료됨"
except Exception as e:
remaining_period_str = "N/A"
print(f"에러 : {e}")
# 마지막 로그인 시간 포맷 (YYYY-MM-DD HH:MM:SS)
last_login_str = self.user_info.get("last_login", "N/A")
formatted_last_login = last_login_str
try:
dt = datetime.fromisoformat(last_login_str)
formatted_last_login = dt.strftime("%Y-%m-%d %H:%M:%S")
except Exception:
pass
# banned_keyword_count: SupabaseManager에서 해당 사용자의 금지어 개수를 조회
user_id = self.user_info.get("id")
banned_keyword_count = self.supabase_manager.get_banned_keyword_count(user_id)
info_text = (
f"환영합니다, {welcome}님!\n"
f"회원이메일: {email} | 회원 등급: {membership} | 남은 사용 기간: {remaining_period_str}\n"
f"관리 중 금지키워드: {banned_keyword_count}개 | 마지막 로그인: {formatted_last_login}"
)
return info_text
def load_announcements(self):
"""공지사항 목록을 Supabase에서 로드하고, 읽은 항목은 QSettings에 기록된 대로 처리합니다."""
# 먼저, 기존 위젯 제거
for i in reversed(range(self.ann_layout.count())):
widget = self.ann_layout.itemAt(i).widget()
if widget:
widget.setParent(None)
try:
announcements = self.supabase_manager.get_announcements()
if not announcements:
self.ann_layout.addWidget(QLabel("새 공지사항이 없습니다."))
return
# QSettings에서 이미 읽은 공지사항 ID 목록을 불러옴
read_ids = self.settings.value("read_announcements", [])
if not isinstance(read_ids, list):
read_ids = []
# 정렬: position 기준 오름차순
announcements = sorted(announcements, key=lambda x: x["position"])
for ann in announcements:
# 만약 공지사항이 중요하지 않고, 이미 읽은 상태라면 표시하지 않음.
if (ann.get("id") in read_ids) and (not ann.get("important", False)):
continue
# 버튼 텍스트: 중요 공지라면 "[중요공지]" 접두어 추가
prefix = "[중요공지] " if ann.get("important", False) else ""
btn = QPushButton(f"{prefix}{ann['position']}. {ann['title']}")
btn.setStyleSheet("""
QPushButton {
background-color: #1877f2;
color: white;
border-radius: 5px;
padding: 8px;
text-align: left;
font-size: 14px;
}
QPushButton:hover {
background-color: #166fe5;
}
""")
# 버튼 클릭 시 상세 공지사항 팝업 표시
btn.clicked.connect(lambda checked, a=ann: self.show_announcement_detail(a))
self.ann_layout.addWidget(btn)
except Exception as e:
QMessageBox.warning(self, "오류", f"공지사항 로드 중 오류 발생: {e}")
def show_announcement_detail(self, announcement):
"""공지사항 버튼 클릭 시 상세 내용을 팝업으로 보여주고, 읽은 것으로 처리합니다."""
try:
dialog = QDialog(self)
dialog.setWindowTitle(f"{announcement['position']}. {announcement['title']}")
dialog.setMinimumSize(400, 300)
layout = QVBoxLayout(dialog)
text_edit = QTextEdit()
text_edit.setReadOnly(True)
text_edit.setHtml(announcement["content"])
layout.addWidget(text_edit)
close_btn = QPushButton("닫기")
close_btn.clicked.connect(dialog.accept)
layout.addWidget(close_btn)
dialog.setLayout(layout)
dialog.exec()
# 읽음 처리 (중요 공지가 아니면)
if not announcement.get("important", False):
read_ids = self.settings.value("read_announcements", [])
if not isinstance(read_ids, list):
read_ids = []
if announcement.get("id") not in read_ids:
read_ids.append(announcement.get("id"))
self.settings.setValue("read_announcements", read_ids)
self.settings.sync()
self.load_announcements()
except Exception as e:
QMessageBox.warning(self, "오류", f"공지사항 표시 중 오류 발생: {e}")
def handle_membership_validity(self):
"""
SupabaseManager의 check_membership_validity()를 호출하여 사용 기간이 유효한지 확인합니다.
- 유효하다면 get_membership_message()로 이벤트 메시지를 표시합니다.
- 만약 기간이 만료되었다면, 재결제 안내 메시지와 함께 다이얼로그를 종료(프로그램 사용 제한)합니다.
"""
valid = self.supabase_manager.check_membership_validity(self.user_info)
print(f"membership_validity : {valid}")
if valid:
membership_msg = self.supabase_manager.get_membership_message(self.user_info)
self.event_label.setText(membership_msg)
# 만약 "다음부터 공지사항 보지 않기" 체크되어 있고, 공지사항 목록이 비어있다면 자동 종료 조건을 시작
self.check_auto_close_condition()
else:
QMessageBox.warning(self, "사용 기간 만료", "사용 기간이 만료되었습니다. 재결제 후 이용해 주세요.")
# 재결제 페이지 호출(예: RePaymentDialog) 또는 프로그램 종료
# self.open_repayment_page()
self.reject()
def open_repayment_page(self):
"""
재결제 안내 및 재결제 페이지를 표시합니다.
실제 구현에서는 재결제 다이얼로그나 웹 페이지를 호출할 수 있습니다.
"""
QMessageBox.information(self, "재결제 안내", "사용 기간이 만료되었습니다. 재결제 페이지로 이동합니다.")
# 예: self.parent().open_repayment_dialog() 또는 웹 브라우저를 통해 특정 URL 오픈
def check_auto_close_condition(self):
"""
"다음부터 공지사항 보지 않기" 체크박스가 선택되어 있고,
현재 표시할 공지사항(읽지 않은, 중요 공지를 제외한)이 없다면 3초 후 자동으로 닫히도록 카운트다운 시작.
"""
# 조건: 체크박스가 체크되어 있고, announcements가 빈 경우
announcements = self.supabase_manager.get_announcements()
read_ids = self.settings.value("read_announcements", [])
if not isinstance(read_ids, list):
read_ids = []
# 필터: 중요 공지를 제외하고, 읽지 않은 항목만 계산
new_announcements = [ann for ann in announcements
if (ann.get("id") not in read_ids) and (not ann.get("important", False))]
if self.hide_checkbox.isChecked() and not new_announcements:
# 3초 카운트다운 시작
self.remaining_seconds = 3
self.countdown_label.setText(f"자동 종료까지: {self.remaining_seconds}")
self.auto_close_timer = QTimer(self)
self.auto_close_timer.timeout.connect(self.update_countdown)
self.auto_close_timer.start(1000)
def update_countdown(self):
self.remaining_seconds -= 1
if self.remaining_seconds > 0:
self.countdown_label.setText(f"자동 종료까지: {self.remaining_seconds}")
else:
self.auto_close_timer.stop()
self.accept() # 다이얼로그 자동 종료