feat(showcase): add temporary language switcher for visitors with cookie persistence
This commit is contained in:
@@ -815,6 +815,16 @@ def public_showcase(request: Request, username: str, db: Session = Depends(get_d
|
|||||||
user = db.query(User).filter(User.username == username).first()
|
user = db.query(User).filter(User.username == username).first()
|
||||||
if not user:
|
if not user:
|
||||||
return HTMLResponse("用户不存在", status_code=404)
|
return HTMLResponse("用户不存在", status_code=404)
|
||||||
|
|
||||||
|
# Language: query param > cookie > owner's default
|
||||||
|
from app.i18n import get_languages
|
||||||
|
_avail = get_languages()
|
||||||
|
visitor_lang = request.query_params.get("lang")
|
||||||
|
if not visitor_lang or visitor_lang not in _avail:
|
||||||
|
visitor_lang = request.cookies.get("showcase_lang")
|
||||||
|
if not visitor_lang or visitor_lang not in _avail:
|
||||||
|
visitor_lang = user.language
|
||||||
|
|
||||||
profiles = db.query(Profile).filter(Profile.user_id == user.id, Profile.showcase_enabled == True).all()
|
profiles = db.query(Profile).filter(Profile.user_id == user.id, Profile.showcase_enabled == True).all()
|
||||||
sent_all, received_all, profile_groups = [], [], []
|
sent_all, received_all, profile_groups = [], [], []
|
||||||
for p in profiles:
|
for p in profiles:
|
||||||
@@ -837,11 +847,13 @@ def public_showcase(request: Request, username: str, db: Session = Depends(get_d
|
|||||||
profile_groups.append({"profile": p, "sent": p_sent, "received": p_received})
|
profile_groups.append({"profile": p, "sent": p_sent, "received": p_received})
|
||||||
sent_all.sort(key=lambda c: c.send_time or c.created_at, reverse=True)
|
sent_all.sort(key=lambda c: c.send_time or c.created_at, reverse=True)
|
||||||
received_all.sort(key=lambda c: c.receive_time or c.created_at, reverse=True)
|
received_all.sort(key=lambda c: c.receive_time or c.created_at, reverse=True)
|
||||||
return templates.TemplateResponse("showcase.html", {
|
resp = templates.TemplateResponse("showcase.html", {
|
||||||
"request": request, "profile_user": user, "mode": user.showcase_mode,
|
"request": request, "profile_user": user, "mode": user.showcase_mode,
|
||||||
"profile_groups": profile_groups, "sent_all": sent_all, "received_all": received_all,
|
"profile_groups": profile_groups, "sent_all": sent_all, "received_all": received_all,
|
||||||
"lang": user.language,
|
"lang": visitor_lang, "available_languages": _avail,
|
||||||
})
|
})
|
||||||
|
resp.set_cookie("showcase_lang", visitor_lang, max_age=365 * 24 * 3600, httponly=True, samesite="lax")
|
||||||
|
return resp
|
||||||
|
|
||||||
|
|
||||||
# ---------- API Keys ----------
|
# ---------- API Keys ----------
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ a:hover { text-decoration: underline; }
|
|||||||
.lang-option { display: block; width: 100%; padding: .35rem .75rem; border: none; background: none; text-align: left; font-size: .8rem; color: var(--text); cursor: pointer; white-space: nowrap; }
|
.lang-option { display: block; width: 100%; padding: .35rem .75rem; border: none; background: none; text-align: left; font-size: .8rem; color: var(--text); cursor: pointer; white-space: nowrap; }
|
||||||
.lang-option:hover { background: var(--bg); color: var(--primary); }
|
.lang-option:hover { background: var(--bg); color: var(--primary); }
|
||||||
.lang-option.active { color: var(--primary); font-weight: 600; }
|
.lang-option.active { color: var(--primary); font-weight: 600; }
|
||||||
|
.showcase-lang-bar { position: fixed; top: 1rem; right: 1.5rem; z-index: 100; }
|
||||||
|
|
||||||
/* Container */
|
/* Container */
|
||||||
.container { max-width: 960px; margin: 2rem auto; padding: 0 1.5rem; }
|
.container { max-width: 960px; margin: 2rem auto; padding: 0 1.5rem; }
|
||||||
|
|||||||
@@ -2,6 +2,16 @@
|
|||||||
{% block title %}{{ profile_user.username }} {{ 'showcase.title'|t(lang) }}{% endblock %}
|
{% block title %}{{ profile_user.username }} {{ 'showcase.title'|t(lang) }}{% endblock %}
|
||||||
{% block nav %}{% endblock %}
|
{% block nav %}{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<div class="showcase-lang-bar">
|
||||||
|
<div class="lang-switcher" id="langSwitcher">
|
||||||
|
<button type="button" class="lang-globe" onclick="document.getElementById('langDropdown').classList.toggle('show')">🌐</button>
|
||||||
|
<div class="lang-dropdown" id="langDropdown">
|
||||||
|
{% for code, name in available_languages.items() %}
|
||||||
|
<a href="?lang={{ code }}" class="lang-option {% if lang == code %}active{% endif %}">{{ name }}</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h1>📮 {{ profile_user.username }} {{ 'showcase.title'|t(lang) }}</h1>
|
<h1>📮 {{ profile_user.username }} {{ 'showcase.title'|t(lang) }}</h1>
|
||||||
</div>
|
</div>
|
||||||
@@ -184,5 +194,13 @@ document.addEventListener('keydown', function(e) {
|
|||||||
else if (e.key === 'ArrowLeft') navLightbox(-1);
|
else if (e.key === 'ArrowLeft') navLightbox(-1);
|
||||||
else if (e.key === 'ArrowRight') navLightbox(1);
|
else if (e.key === 'ArrowRight') navLightbox(1);
|
||||||
});
|
});
|
||||||
|
// Close language dropdown when clicking outside
|
||||||
|
document.addEventListener('click', function(e) {
|
||||||
|
var sw = document.getElementById('langSwitcher');
|
||||||
|
var dd = document.getElementById('langDropdown');
|
||||||
|
if (sw && dd && !sw.contains(e.target)) {
|
||||||
|
dd.classList.remove('show');
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user