diff --git a/.gitignore b/.gitignore
index a0403515..cf53e58f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,7 @@ tox.ini
# generated css
/common/static/css/boofilsic.min.css
/common/static/css/boofilsic.css
+/common/static/scss/neodb.css
# debug log file
/log
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index b9a6102f..61edcd73 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -10,3 +10,10 @@ repos:
hooks:
- id: black
language_version: python3.11
+repos:
+ - repo: https://github.com/Riverside-Healthcare/djLint
+ rev: v1.28.0
+ hooks:
+ - id: djlint-reformat-django
+ - id: djlint-django
+
diff --git a/boofilsic/settings.py b/boofilsic/settings.py
index 88ef1297..85800e71 100644
--- a/boofilsic/settings.py
+++ b/boofilsic/settings.py
@@ -12,6 +12,7 @@ https://docs.djangoproject.com/en/3.0/ref/settings/
import os
+PROJECT_ROOT = os.path.abspath(os.path.dirname(__name__))
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -52,6 +53,7 @@ INSTALLED_APPS = [
"django_rq",
"django_bleach",
"tz_detect",
+ "sass_processor",
"simple_history",
"markdownx",
"polymorphic",
@@ -210,6 +212,11 @@ STATIC_URL = "/static/"
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
STATICFILES_STORAGE = "django.contrib.staticfiles.storage.ManifestStaticFilesStorage"
+STATICFILES_FINDERS = [
+ "django.contrib.staticfiles.finders.FileSystemFinder",
+ "django.contrib.staticfiles.finders.AppDirectoriesFinder",
+ "sass_processor.finders.CssFinder",
+]
AUTH_USER_MODEL = "users.User"
@@ -221,9 +228,9 @@ SILENCED_SYSTEM_CHECKS = [
MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media/")
-PROJECT_ROOT = os.path.abspath(os.path.dirname(__name__))
SITE_INFO = {
"site_name": "NiceDB",
+ "site_url": "https://nicedb.org",
"support_link": "https://github.com/doubaniux/boofilsic/issues",
"social_link": "https://donotban.com/@testie",
"donation_link": "https://patreon.com/tertius",
@@ -237,6 +244,7 @@ CLIENT_NAME = os.environ.get("APP_NAME", "NiceDB")
SITE_INFO["site_name"] = os.environ.get("APP_NAME", "NiceDB")
APP_WEBSITE = os.environ.get("APP_URL", "https://nicedb.org")
REDIRECT_URIS = APP_WEBSITE + "/users/OAuth2_login/"
+SITE_INFO["site_url"] = APP_WEBSITE
# Path to save report related images, ends with slash
@@ -408,3 +416,5 @@ MAINTENANCE_MODE_IGNORE_ADMIN_SITE = True
MAINTENANCE_MODE_IGNORE_SUPERUSER = True
MAINTENANCE_MODE_IGNORE_ANONYMOUS_USER = True
MAINTENANCE_MODE_IGNORE_URLS = (r"^/users/connect/", r"^/users/OAuth2_login/")
+
+DISCORD_WEBHOOKS = {}
diff --git a/catalog/common/downloaders.py b/catalog/common/downloaders.py
index 69288186..8d7caa47 100644
--- a/catalog/common/downloaders.py
+++ b/catalog/common/downloaders.py
@@ -277,9 +277,9 @@ class MockResponse:
return json.load(StringIO(self.text))
def html(self):
- return html.fromstring(
+ return html.fromstring( # may throw exception unexpectedly due to OS bug, see https://github.com/neodb-social/neodb/issues/5
self.text
- ) # may throw exception unexpectedly due to OS bug, see https://github.com/neodb-social/neodb/issues/5
+ )
@property
def headers(self):
diff --git a/catalog/management/commands/discover.py b/catalog/management/commands/discover.py
index 02980c9f..d9bc0a2d 100644
--- a/catalog/management/commands/discover.py
+++ b/catalog/management/commands/discover.py
@@ -8,6 +8,9 @@ from django.utils import timezone
from django.db.models import Count
+MAX_GALLERY_ITEMS = 42
+
+
class Command(BaseCommand):
help = "catalog app utilities"
@@ -18,9 +21,21 @@ class Command(BaseCommand):
help="generate discover data",
)
+ def get_popular_item_ids(self, category, days):
+ self.stdout.write(f"Generating popular {category} items for {days} days...")
+ item_ids = [
+ m["item_id"]
+ for m in ShelfMember.objects.filter(query_item_category(category))
+ .filter(created_time__gt=timezone.now() - timedelta(days=days))
+ .values("item_id")
+ .annotate(num=Count("item_id"))
+ .order_by("-num")[:MAX_GALLERY_ITEMS]
+ ]
+ return item_ids
+
def handle(self, *args, **options):
if options["update"]:
- cache_key = "public_gallery_list"
+ cache_key = "public_gallery"
gallery_categories = [
ItemCategory.Book,
ItemCategory.Movie,
@@ -31,22 +46,19 @@ class Command(BaseCommand):
]
gallery_list = []
for category in gallery_categories:
- item_ids = [
- m["item_id"]
- for m in ShelfMember.objects.filter(query_item_category(category))
- .filter(created_time__gt=timezone.now() - timedelta(days=42))
- .values("item_id")
- .annotate(num=Count("item_id"))
- .order_by("-num")[:100]
- ]
+ days = 14
+ item_ids = []
+ while len(item_ids) < MAX_GALLERY_ITEMS / 2 and days < 150:
+ item_ids = self.get_popular_item_ids(category, days)
+ days *= 3
+ items = Item.objects.filter(id__in=item_ids)
gallery_list.append(
{
"name": "popular_" + category.value,
"title": "热门"
+ (category.label if category != ItemCategory.Book else "图书"),
- "item_ids": item_ids,
+ "items": items,
}
)
cache.set(cache_key, gallery_list, timeout=None)
-
self.stdout.write(self.style.SUCCESS(f"Done."))
diff --git a/catalog/music/models.py b/catalog/music/models.py
index 571e6929..9ffddbb0 100644
--- a/catalog/music/models.py
+++ b/catalog/music/models.py
@@ -75,6 +75,16 @@ class Album(Item):
bandcamp_album_id = jsondata.CharField(blank=True, default="", max_length=500)
disc_count = jsondata.IntegerField(_("碟片数"), blank=True, default="", max_length=500)
+ def get_embed_link(self):
+ for res in self.external_resources.all():
+ if res.id_type == IdType.Bandcamp.value and res.metadata.get(
+ "bandcamp_album_id"
+ ):
+ return f"https://bandcamp.com/EmbeddedPlayer/album={res.metadata.get('bandcamp_album_id')}/size=large/bgcol=ffffff/linkcol=19A2CA/artwork=small/transparent=true/"
+ if res.id_type == IdType.Spotify_Album.value:
+ return res.url.replace("open.spotify.com/", "open.spotify.com/embed/")
+ return None
+
@classmethod
def lookup_id_type_choices(cls):
id_types = [
diff --git a/catalog/search/external.py b/catalog/search/external.py
index 94e957b3..6b72e999 100644
--- a/catalog/search/external.py
+++ b/catalog/search/external.py
@@ -16,19 +16,22 @@ class SearchResultItem:
self, category, source_site, source_url, title, subtitle, brief, cover_url
):
self.category = category
+ self.external_resources = {
+ "all": [{"url": source_url, "site_name": {"label": source_site}}]
+ }
self.source_site = source_site
self.source_url = source_url
self.title = title
self.subtitle = subtitle
self.brief = brief
- self.cover_url = cover_url
+ self.cover_image_url = cover_url
@property
def verbose_category_name(self):
return self.category.label
@property
- def link(self):
+ def url(self):
return f"/search?q={quote_plus(self.source_url)}"
@property
@@ -272,7 +275,7 @@ class ExternalSources:
results = []
if c == "" or c is None:
c = "all"
- if c == "all" or c == "movie" or c == "tv":
+ if c == "all" or c == "movietv":
results.extend(TheMovieDatabase.search(q, page))
if c == "all" or c == "book":
results.extend(GoogleBooks.search(q, page))
diff --git a/catalog/search/typesense.py b/catalog/search/typesense.py
index 3187feb1..ed500fa5 100644
--- a/catalog/search/typesense.py
+++ b/catalog/search/typesense.py
@@ -236,7 +236,10 @@ class Indexer:
def search(cls, q, page=1, category=None, tag=None, sort=None):
f = []
if category:
- f.append("category:= " + category)
+ if category == "movietv":
+ f.append("category:= [movie,tv]")
+ else:
+ f.append("category:= " + category)
if tag:
f.append(f"tags:= '{tag}'")
filters = " && ".join(f)
@@ -254,6 +257,7 @@ class Indexer:
try:
r = cls.instance().collections[INDEX_NAME].documents.search(options)
+ print(r)
results.items = list(
[
x
diff --git a/catalog/search/views.py b/catalog/search/views.py
index 2ee372e1..a8b0e0a0 100644
--- a/catalog/search/views.py
+++ b/catalog/search/views.py
@@ -80,6 +80,7 @@ def fetch(request, url, is_refetch: bool = False, site: AbstractSite = None):
"fetch_pending.html",
{
"site": site,
+ "sites": SiteName.labels,
"job_id": job_id,
},
)
@@ -99,6 +100,7 @@ def search(request):
"search_results.html",
{
"items": None,
+ "sites": SiteName.labels,
},
)
@@ -150,7 +152,7 @@ def search(request):
),
"categories": ["book", "movie", "music", "game"],
"sites": SiteName.labels,
- "hide_category": category is not None,
+ "hide_category": category is not None and category != "movietv",
},
)
diff --git a/catalog/sites/bandcamp.py b/catalog/sites/bandcamp.py
index b820c862..33a00659 100644
--- a/catalog/sites/bandcamp.py
+++ b/catalog/sites/bandcamp.py
@@ -14,8 +14,8 @@ _logger = logging.getLogger(__name__)
class Bandcamp(AbstractSite):
SITE_NAME = SiteName.Bandcamp
ID_TYPE = IdType.Bandcamp
- URL_PATTERNS = [r"https://([a-z0-9\-]+.bandcamp.com/album/[^?#/]+)"]
- URL_PATTERN_FALLBACK = r"https://([a-z0-9\-\.]+/album/[^?#/]+)"
+ URL_PATTERNS = [r"https://([a-z0-9\-]+.bandcamp.com/album/[^?#/]+).*"]
+ URL_PATTERN_FALLBACK = r"https://([a-z0-9\-\.]+/album/[^?#/]+).*"
WIKI_PROPERTY_ID = ""
DEFAULT_MODEL = Album
diff --git a/catalog/sites/spotify.py b/catalog/sites/spotify.py
index 790fb376..8b2625d9 100644
--- a/catalog/sites/spotify.py
+++ b/catalog/sites/spotify.py
@@ -24,7 +24,7 @@ spotify_token_expire_time = time.time()
class Spotify(AbstractSite):
SITE_NAME = SiteName.Spotify
ID_TYPE = IdType.Spotify_Album
- URL_PATTERNS = [r"\w+://open\.spotify\.com/album/([a-zA-Z0-9]+)"]
+ URL_PATTERNS = [r"\w+://open\.spotify\.com/album/([a-zA-Z0-9]+).*"]
WIKI_PROPERTY_ID = "?"
DEFAULT_MODEL = Album
diff --git a/catalog/static/podcast.js b/catalog/static/podcast.js
new file mode 100644
index 00000000..0285860d
--- /dev/null
+++ b/catalog/static/podcast.js
@@ -0,0 +1,57 @@
+function create_player(audio) {
+ window.player = new Shikwasa.Player({
+ container: () => document.querySelector('.player'),
+ preload: 'metadata',
+ autoplay: true,
+ themeColor: getComputedStyle(document.documentElement).getPropertyValue('--pico-primary'),
+ fixed: {
+ type: 'fixed',
+ position: 'bottom'
+ },
+ audio: audio
+ });
+ // $('.shk-title').on('click', e=>{
+ // window.location = "#";
+ // });
+ $('.footer').attr('style', 'margin-bottom: 120px !important');
+}
+function podcast_init(context) {
+ $('.episode', context).on('click', e=>{
+ e.preventDefault();
+ var ele = e.currentTarget;
+ var album = $(ele).data('album');
+ var artist = $(ele).data('hosts');
+ var title = $(ele).data('title');
+ var cover_url = $(ele).data('cover');
+ var media_url = $(ele).data('media');
+ var position = $(ele).data('position');
+ if (!media_url) return;
+ window.current_item_uuid = $(ele).data('uuid');
+ if (!window.player) {
+ create_player({
+ title: title,
+ cover: cover_url,
+ src: media_url,
+ album: album,
+ artist: artist
+ })
+ } else {
+ window.player.update({
+ title: title,
+ cover: cover_url,
+ src: media_url,
+ album: album,
+ artist: artist
+ })
+ }
+ if (position) window.player._initSeek = position;
+ window.player.play()
+ });
+}
+
+$(function() {
+ document.body.addEventListener('htmx:load', function(evt) {
+ podcast_init(evt.detail.elt);
+ });
+ podcast_init(document.body);
+});
diff --git a/catalog/templates/_item_card.html b/catalog/templates/_item_card.html
new file mode 100644
index 00000000..5351f935
--- /dev/null
+++ b/catalog/templates/_item_card.html
@@ -0,0 +1,121 @@
+{% load humanize %}
+{% load i18n %}
+{% load highlight %}
+{% if item.get_embed_link %}
+
+{% else %}
+
+
+
+
+
+ {% comment %}
+
+ {% for res in item.external_resources.all %}
+ {{ res.site_name.label }}
+ {% endfor %}
+
+ {% endcomment %}
+
+
+ {% if item.subtitle %}{{ item.subtitle }}{% endif %}
+ {% if item.orig_title %}
+ {{ item.orig_title }}
+ {% if item.season_number %}Season {{ item.season_number }}{% endif %}
+ {% endif %}
+
+
+
+
+
+ {% if item.rating %}
+ {{ item.rating | floatformat:1 }}分
+ {% else %}
+ {% endif %}
+ {% include '_people.html' with people=item.author role='' max=2 %}
+ {% include '_people.html' with people=item.translator role='' max=2 %}
+ {% include '_people.html' with people=item.director role='' max=2 %}
+ {% include '_people.html' with people=item.hosts role='' max=2 %}
+ {% include '_people.html' with people=item.artist role='' max=2 %}
+ {% include '_people.html' with people=item.developer role='' max=2 %}
+ {% if item.pub_house %}/ {{ item.pub_house }}{% endif %}
+ {% if item.pub_year %}
+ / {{ item.pub_year }}{% trans '年' %}
+ {% if item.pub_month %}
+ {{ item.pub_month }}{% trans '月' %}
+ {% endif %}
+ {% endif %}
+ {% if item.release_date %}/ {{ item.release_date }}{% endif %}
+ {% include '_people.html' with people=item.genre role='' max=10 %}
+ {% include '_people.html' with people=item.platform role='' max=10 %}
+
+
+ {% if item.actor %}
+ {% include '_people.html' with people=item.actor role='主演' max=2 %}
+
+ {% endif %}
+ {% if item.other_title %}
+ {% include '_people.html' with people=item.other_title role='又名' max=2 %}
+
+ {% endif %}
+ {% if request.GET.q %}
+ {{ item.brief | linebreaksbr | highlight:request.GET.q }}
+ {% else %}
+ {{ item.brief | linebreaksbr }}
+ {% endif %}
+
+ {% if show_tags %}
+
+ {% for tag in item.tags %}
+ {% if forloop.counter <= 5 %}
+
+ {{ tag }}
+
+ {% endif %}
+ {% endfor %}
+
+ {% endif %}
+
+
+
+{% endif %}
diff --git a/catalog/templates/_people.html b/catalog/templates/_people.html
new file mode 100644
index 00000000..8463a0c0
--- /dev/null
+++ b/catalog/templates/_people.html
@@ -0,0 +1,11 @@
+{% if people %}
+ {% if role %}{{ role }}:{% endif %}
+ {% for p in people %}
+ {% if forloop.counter <= max %}
+ {% if not forloop.first %}、{% endif %}
+ {{ p }}
+ {% elif forloop.last %}
+ 等
+ {% endif %}
+ {% endfor %}
+{% endif %}
diff --git a/catalog/templates/album.html b/catalog/templates/album.html
index 88faa68e..b41dd6bf 100644
--- a/catalog/templates/album.html
+++ b/catalog/templates/album.html
@@ -10,114 +10,120 @@
{% load strip_scheme %}
{% load thumb %}
{% load duration %}
-
{% block details %}
-
-
- {% if item.rating and item.rating_count >= 5 %}
-
- {{ item.rating | floatformat:1 }}
- ({{ item.rating_count }}人评分)
- {% else %}
- {% trans '评分:评分人数不足' %}
- {% endif %}
-
-
{% if item.artist %}{% trans '艺术家:' %}
- {% for artist in item.artist %}
-
5 %}style="display: none;" {% endif %}>
- {{ artist }}
- {% if not forloop.last %} / {% endif %}
-
- {% endfor %}
- {% if item.artist|length > 5 %}
-
{% trans '更多' %}
-
- {% endif %}
- {% endif %}
-
{% if item.company %}{% trans '发行方:' %}
- {% for company in item.company %}
-
5 %}style="display: none;" {% endif %}>
- {{ company }}
- {% if not forloop.last %} / {% endif %}
-
- {% endfor %}
- {% if item.company|length > 5 %}
-
{% trans '更多' %}
-
+ {% endif %}
+ {% endif %}
+
+
+ {% if item.company %}
+ {% trans '发行方:' %}
+ {% for company in item.company %}
+
5 %}style="display: none;"{% endif %}>
+ {{ company }}
+ {% if not forloop.last %}/{% endif %}
+
+ {% endfor %}
+ {% if item.company|length > 5 %}
+
{% trans '更多' %}
+
- {% endif %}
- {% endif %}
-
{% if item.release_date %}
- {% trans '发行日期:' %}{{ item.release_date }}
- {% endif %}
-
-
{% if item.duration %}
- {% trans '时长:' %}{{ item.duration|duration_format:1000 }}
- {% endif %}
-
-
{% if item.genre %}
- {% trans '流派:' %}
- {% for genre in item.genre %}
- {{ genre }} {% if not forloop.last %} / {% endif %}
- {% endfor %}
- {% endif %}
-
-
{% if item.barcode %}
- {% trans '条形码:' %}{{ item.barcode }}
- {% endif %}
-
-
-
-
-
{% if item.other_title %}
- {% trans '又名:' %}
- {% for t in item.other_title %}
- {{ t }} {% if not forloop.last %} / {% endif %}
- {% endfor %}
- {% endif %}
-
-
{% if item.album_type %}
- {% trans '专辑类型:' %}{{ item.album_type }}
- {% endif %}
-
-
{% if item.media %}
- {% trans '介质:' %}{{ item.media }}
- {% endif %}
-
-
{% if item.disc_count %}
- {% trans '碟片数:' %}{{ item.disc_count }}
- {% endif %}
-
-
- {% if item.last_editor and item.last_editor.preference.show_last_edit %}
-
- {% endif %}
-
-
-
+
+
+ {% endif %}
+ {% endif %}
+
+
+ {% if item.release_date %}
+ {% trans '发行日期:' %}{{ item.release_date }}
+ {% endif %}
+
+
+ {% if item.duration %}
+ {% trans '时长:' %}{{ item.duration|duration_format:1000 }}
+ {% endif %}
+
+
+ {% if item.genre %}
+ {% trans '流派:' %}
+ {% for genre in item.genre %}
+ {{ genre }}
+ {% if not forloop.last %}/{% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+ {% if item.barcode %}
+ {% trans '条形码:' %}{{ item.barcode }}
+ {% endif %}
+
+
+ {% if item.other_title %}
+ {% trans '又名:' %}
+ {% for t in item.other_title %}
+ {{ t }}
+ {% if not forloop.last %}/{% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+ {% if item.album_type %}
+ {% trans '专辑类型:' %}{{ item.album_type }}
+ {% endif %}
+
+
+ {% if item.media %}
+ {% trans '介质:' %}{{ item.media }}
+ {% endif %}
+
+
+ {% if item.disc_count %}
+ {% trans '碟片数:' %}{{ item.disc_count }}
+ {% endif %}
+
{% endblock %}
-
-{% block sidebar %}
-{% if item.get_embed_link %}
-
-{% endif %}
+{% block content %}
+ {% if item.track_list %}
+
+ 曲目
+ {{ item.track_list | linebreaksbr }}
+
+ {% endif %}
+ {% if item.get_embed_link %}
+
+ {% endif %}
{% endblock %}
diff --git a/catalog/templates/catalog_edit.html b/catalog/templates/catalog_edit.html
index 30ed0a4d..54b21482 100644
--- a/catalog/templates/catalog_edit.html
+++ b/catalog/templates/catalog_edit.html
@@ -4,133 +4,148 @@
{% load mastodon %}
{% load oauth_token %}
{% load truncate %}
-
-
-
-
-
-
- {{ site_name }} - {% if form.instance.id %}{% trans '编辑' %} {{ form.instance.title }} {% else %}{% trans '添加' %}{% endif %}
- {% include "common_libs.html" with jquery=0 %}
-
-
-
-
-
- {% include "partial/_navbar.html" %}
-
-
-
-
- {% if item %}
-
- {% for res in form.instance.external_resources.all %}
-
-
-
-
-
+
+
+
+
+
{{ site_name }} -
+ {% if form.instance.id %}
+ {% trans '编辑' %} {{ form.instance.title }}
+ {% else %}
+ {% trans '添加' %}
+ {% endif %}
+
+ {% include "common_libs.html" with jquery=0 v2=1 %}
+
+
+
+ {% include "_header.html" %}
+
+
+
+ 进阶编辑选项
+ {% if item %}
+ {% for res in form.instance.external_resources.all %}
+
+
+ {% trans '源网站' %}: {{ res.site_name.label }}
+
+
+
{% if request.user.is_staff %}
-
-
{% endif %}
- {% endfor %}
-
-
- {% if item.class_name == "movie" or item.class_name == "tvshow" %}
-
-
-
{% trans '切换分类' %}
-
-
-
-
-
- {% endif %}
-
-
-
- {% if item.class_name == "tvseason" %}
- {% if not item.show or request.user.is_superuser %}
-
{% trans '将本季关联到电视剧' %}
-
-
-
+
+ {% endfor %}
+ {% if item.class_name == "movie" or item.class_name == "tvshow" %}
+
+ {% trans '切换分类' %}
+
-
-
+
+
{% endif %}
-
-
-
-
-
- {% include "partial/_footer.html" %}
-
-
-
-
-
+ {% if item.class_name == "tvseason" %}
+ {% if not item.show or request.user.is_superuser %}
+
+ {% trans '将本季关联到电视剧' %}
+
+ {% csrf_token %}
+
+
+
+
+
+ {% endif %}
+ {% endif %}
+ {% if item.is_deleted and not request.user.is_superuser %}
+ 条目已被删除
+ {% elif item.merged_to_item and not request.user.is_superuser %}
+ 条目已被合并
+ {% elif not item.deletable and not request.user.is_superuser %}
+ 条目已被用户标记过
+ {% else %}
+
+ {% trans '合并到另一条目' %}
+
+ {% csrf_token %}
+
+
+
+
+
+
+ {% trans '删除' %}
+
+ {% csrf_token %}
+
+
+
+ {% endif %}
+ {% endif %}
+
+
+ {% include "partial/_footer.html" %}
+
+
diff --git a/catalog/templates/common_libs.html b/catalog/templates/common_libs.html
index a0cdae34..fa52c296 100644
--- a/catalog/templates/common_libs.html
+++ b/catalog/templates/common_libs.html
@@ -1,42 +1,38 @@
{% load static %}
+{% load sass_tags %}
{% load tz_detect %}
-{% tz_detect %}
-{% if sentry_dsn %}
-
-
+{% if request.user.is_authenticated %}
+ {% tz_detect %}
{% endif %}
{% if jquery %}
-
+
{% else %}
-
+
{% endif %}
-
+
-
-
-
-
-
-
-
-
+
+
+
+
diff --git a/catalog/templates/discover.html b/catalog/templates/discover.html
index 1468e3f2..fb072518 100644
--- a/catalog/templates/discover.html
+++ b/catalog/templates/discover.html
@@ -6,118 +6,111 @@
{% load truncate %}
{% load thumb %}
-
-
-
-
-
- {{ site_name }} - {% trans '发现' %}
-
-
- {% include "common_libs.html" with jquery=0 %}
-
-
+
+
+
+
+
+
+
+
+
+
+ {{ site_name }} - {% trans '发现' %}
+
+ {% include "common_libs.html" with jquery=0 v2=1 %}
+
+
+
-
-
-
- {% include "partial/_navbar.html" with current="discover" %}
-
-
-
-
-
-
-
- {% for gallery in gallery_list %}
-
-
- {{ gallery.title }}
-
-
-
+ {% include "_header.html" with current="discover" %}
+
+
+
+ {% for gallery in gallery_list %}
+
-
- {% if user == request.user %}
-
-
-
-
- {% trans '编辑布局' %}
-
-
- {% trans '保存' %}
-
-
-
-
-
-
-
-
-
-
- {% trans '取消' %}
-
-
-
-
-
- {% trans '显示' %}
-
-
- {% trans '隐藏' %}
-
-
-
- {% csrf_token %}
-
-
-
-
-
- {% endif %}
-
- {{ layout|json_script:"layout-data" }}
-
-
+
+
+ {% endfor %}
+
+ {% if request.user.is_authenticated %}
+
+
+ {% trans '编辑布局' %}
+ {% trans '保存' %}
+
+
+
+
+
+
+
+
+ {% trans '取消' %}
-
- {% include "partial/_sidebar.html" %}
-
+
+ {% trans '显示' %}
+ {% trans '隐藏' %}
+
+
+ {% csrf_token %}
+
+
+
+
+
+ {{ layout|json_script:"layout-data" }}
+
+ {% endif %}
- {% include "partial/_footer.html" %}
-
-
- {% if request.user.unread_announcements %}
- {% include "partial/_announcement.html" %}
- {% endif %}
+ {% if request.user.is_authenticated %}
+ {% include "_sidebar.html" with show_progress=1 %}
+ {% else %}
+ {% include "partial/_sidebar_anonymous.html" %}
+ {% endif %}
+
+ {% include "partial/_footer.html" %}
diff --git a/catalog/templates/edition.html b/catalog/templates/edition.html
index cb814da7..e1dd90af 100644
--- a/catalog/templates/edition.html
+++ b/catalog/templates/edition.html
@@ -9,105 +9,153 @@
{% load truncate %}
{% load strip_scheme %}
{% load thumb %}
-
{% block head %}
-{% if item.author %}
-
-{% endif %}
-{% if item.isbn %}
-
-{% endif %}
+ {% if item.author %}
+
+ {% endif %}
+ {% if item.isbn %}
{% endif %}
{% endblock %}
-
{% block details %}
-
-
- {% if item.rating %}
-
- {{ item.rating | floatformat:1 }}
- ({{ item.rating_count }}人评分)
- {% else %}
- {% trans '评分:评分人数不足' %}
- {% endif %}
-
-
{% if item.subtitle %}{% trans '副标题:' %}{{ item.subtitle }}{% endif %}
-
{% if item.isbn %}{% trans 'ISBN:' %}{{ item.isbn }}{% endif %}
-
{% if item.author %}{% trans '作者:' %}
- {% for author in item.author %}
- {{ author }} {% if not forloop.last %} / {% endif %}
- {% endfor %}
- {% endif %}
-
{% if item.pub_house %}{% trans '出版社:' %}{{ item.pub_house }}{% endif %}
-
{% if item.orig_title %}{% trans '原作名:' %}{{ item.orig_title }}{% endif %}
-
{% if item.translator %}{% trans '译者:' %}
- {% for translator in item.translator %}
- {{ translator }} {% if not forloop.last %} / {% endif %}
- {% endfor %}
- {% endif %}
-
{%if item.pub_year %}{% trans '出版时间:' %}{{ item.pub_year }}{% trans '年' %}{% if item.pub_month %}{{ item.pub_month }}{% trans '月' %}{% endif %}{% endif %}
-
{% if item.series %}{% trans '丛书系列:' %}{{ item.series }}{% endif %}
-
-
-
{% if item.language %}{% trans '语言:' %}{{ item.language }}{% endif %}
-
{% if item.binding %}{% trans '装帧:' %}{{ item.binding }}{% endif %}
-
{% if item.price %}{% trans '定价:' %}{{ item.price }}{% endif %}
-
{% if item.pages %}{% trans '页数:' %}{{ item.pages }}{% endif %}
-
{% if item.imprint %}{% trans '出品方:' %}{{ item.imprint }}{% endif %}
- {% if item.other_info %}
- {% for k, v in item.other_info.items %}
-
- {{ k }}:{{ v | urlize }}
-
- {% endfor %}
- {% endif %}
-
-
- {% if item.last_editor and item.last_editor.preference.show_last_edit %}
-
- {% endif %}
-
-
-
+
+
+ {% if item.isbn %}
+ {% trans 'ISBN:' %}{{ item.isbn }}
+ {% endif %}
+
+
+ {% if item.author %}
+ {% trans '作者:' %}
+ {% for author in item.author %}
+ {{ author }}
+ {% if not forloop.last %}/{% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+ {% if item.translator %}
+ {% trans '译者:' %}
+ {% for translator in item.translator %}
+ {{ translator }}
+ {% if not forloop.last %}/{% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+ {% if item.pub_house %}
+ {% trans '出版社:' %}{{ item.pub_house }}
+ {% endif %}
+
+
+ {% if item.imprint %}
+ {% trans '出品方:' %}{{ item.imprint }}
+ {% endif %}
+
+
+ {% if item.pub_year %}
+ {% trans '出版时间:' %}{{ item.pub_year }}{% trans '年' %}
+ {% if item.pub_month %}
+ {{ item.pub_month }}{% trans '月' %}
+ {% endif %}
+ {% endif %}
+
+
+ {% if item.series %}
+ {% trans '丛书系列:' %}{{ item.series }}
+ {% endif %}
+
+
+ {% if item.language %}
+ {% trans '语言:' %}{{ item.language }}
+ {% endif %}
+
+
+ {% if item.binding %}
+ {% trans '装帧:' %}{{ item.binding }}
+ {% endif %}
+
+
+ {% if item.price %}
+ {% trans '定价:' %}{{ item.price }}
+ {% endif %}
+
+
+ {% if item.pages %}
+ {% trans '页数:' %}{{ item.pages }}
+ {% endif %}
+
+ {% if item.other_info %}
+ {% for k, v in item.other_info.items %}
{{ k }}:{{ v|urlizetrunc:24 }}
{% endfor %}
+ {% endif %}
+{% endblock %}
+{% block content %}
+ {% if item.contents %}
+
+ 目录
+ {{ item.contents | linebreaksbr }}
+
+ {% endif %}
{% endblock %}
-
-
{% block sidebar %}
-{% with related_books=item.get_related_books %}
-{% if related_books.count > 0 %}
-
-
-
{% trans '其它版本' %}
-
- {% for b in related_books %}
-
- {{ b.title }}
- ({{ b.pub_house | default:'' }} {{ b.pub_year | default:'' }})
- {% for res in b.external_resources.all %}
-
- {{ res.site_name.label }}
-
- {% endfor %}
-
- {% endfor %}
-
-
-
-{% endif %}
-{% endwith %}
-
-{% if item.isbn %}
-
-
-
{% trans '借阅或购买' %}
-
-
-
-{% endif %}
+ {% with related_books=item.get_related_books %}
+ {% if related_books.count > 0 %}
+
+ {% trans '其它版本' %}
+ {% for b in related_books %}
+
+ {{ b.title }}
+ ({{ b.pub_house | default:'' }} {{ b.pub_year | default:'' }})
+ {% comment %} {% for res in b.external_resources.all %}
+
+ {{ res.site_name.label }}
+
+ {% endfor %} {% endcomment %}
+
+ {% endfor %}
+
+ {% endif %}
+ {% endwith %}
+ {% if item.isbn %}
+
+
+
{% trans '借阅或购买' %}
+
+
+
+ {% endif %}
{% endblock %}
diff --git a/catalog/templates/embed_base.html b/catalog/templates/embed_base.html
index b3570479..e20d0154 100644
--- a/catalog/templates/embed_base.html
+++ b/catalog/templates/embed_base.html
@@ -8,17 +8,14 @@
{% load truncate %}
{% load strip_scheme %}
{% load thumb %}
-
-
-
-
-
- {% block head %}
- {% endblock %}
-
{{ site_name }} - {% trans item.category.label %} | {{ item.title }}
-
-
-
+
+
+
+
+ {% block head %}{% endblock %}
+
{{ site_name }} - {% trans item.category.label %} | {{ item.title }}
+
+
diff --git a/catalog/templates/embed_podcast.html b/catalog/templates/embed_podcast.html
index 19514e5d..fa720632 100644
--- a/catalog/templates/embed_podcast.html
+++ b/catalog/templates/embed_podcast.html
@@ -9,10 +9,10 @@
{% load truncate %}
{% load strip_scheme %}
{% load thumb %}
-
{% block head %}
-
-
+
+
-
-
- {% if item.rating and item.rating_count >= 5 %}
-
- {{ item.rating | floatformat:1 }}
- ({{ item.rating_count }}人评分)
- {% else %}
- {% trans '评分:评分人数不足' %}
- {% endif %}
-
-
-
- {% if item.genre %}{% trans '类型:' %}
- {% for genre in item.genre %}
- {{ genre }} {% if not forloop.last %} / {% endif %}
- {% endfor %}
- {% endif %}
-
-
-
{% if item.official_site %}
- {% trans '网站:' %}{{ item.official_site|urlizetrunc:42 }}
- {% endif %}
-
-
-
-
-
-
{% if item.hosts %}{% trans '主播:' %}
- {% for host in item.hosts %}
-
5 %}style="display: none;" {% endif %}>
- {{ host }}
- {% if not forloop.last %} / {% endif %}
-
- {% endfor %}
- {% if item.hosts|length > 5 %}
-
{% trans '更多' %}
-
- {% endif %}
- {% endif %}
-
- {% if item.last_editor and item.last_editor.preference.show_last_edit %}
-
- {% endif %}
-
-
-
-{% endblock %}
-
-
-{% block content %}
-
-
{% trans '近期节目' %}
-
-
-
-{% endblock %}
-
-
-{% block sidebar %}
-
-
-
- {% trans '用播客应用订阅' %}
-
+
+ {% endif %}
+ {% endif %}
-
-
-
-
+
+
{% endblock %}
diff --git a/catalog/templates/podcast_episode_data.html b/catalog/templates/podcast_episode_data.html
index fb9c3728..74695cfa 100644
--- a/catalog/templates/podcast_episode_data.html
+++ b/catalog/templates/podcast_episode_data.html
@@ -3,44 +3,39 @@
{% load l10n %}
{% load humanize %}
{% for ep in episodes %}
-
-
-
-
-
- {{ ep.title }}
- {{ ep.pub_date|date }}
-
-
- {{ ep.brief | linebreaksbr }}
-
-
- {% if request.user.is_authenticated %}
-
-
-
- {% endif %}
-
-
-
-
-
-
-{% if forloop.last %}
-
显示更多
-{% endif %}
-
+
+
+
+
+ {% if request.user.is_authenticated %}
+
+ {% endif %}
+
+ {{ ep.title }}
+ {{ ep.pub_date|date }}
+
+
{{ ep.brief | linebreaksbr }}
+
+ {% if forloop.last %}
+
显示更多
+ {% endif %}
{% empty %}
-
{% trans '目前没有更多内容了' %}
+
{% trans '目前没有更多内容了' %}
{% endfor %}
diff --git a/catalog/templates/podcastepisode.html b/catalog/templates/podcastepisode.html
index 42f6a26f..23f7ee23 100644
--- a/catalog/templates/podcastepisode.html
+++ b/catalog/templates/podcastepisode.html
@@ -9,15 +9,18 @@
{% load truncate %}
{% load strip_scheme %}
{% load thumb %}
-
{% block head %}
-
-
-
-{% if item.media_url %}
-
-
-
-
-{% endif %}
+
+
+
+ {% if item.media_url %}
+
+
+
+
+ {% endif %}
{% endblock %}
diff --git a/catalog/templates/search_results.html b/catalog/templates/search_results.html
index 2d27683e..72a86534 100644
--- a/catalog/templates/search_results.html
+++ b/catalog/templates/search_results.html
@@ -9,94 +9,70 @@
{% load highlight %}
{% load thumb %}
-
-
-
+
+
{{ site_name }} - {% trans '搜索结果' %}
- {% include "common_libs.html" with jquery=0 %}
-
-
-
-
-
- {% include 'partial/_navbar.html' %}
-
-
-
-
-
-
-
- {% if request.GET.q %}
-
“{{ request.GET.q }}” {% trans '的搜索结果' %}
- {% endif %}
-
- {% if request.GET.tag %}
-
{% trans '含有标签' %} “{{ request.GET.tag }}” {% trans '的结果' %}
- {% endif %}
-
-
- {% for item in items %}
- {% with "list_item_"|add:item.class_name|add:".html" as template %}
- {% include template %}
- {% endwith %}
- {% empty %}
-
- {% trans '无站内条目匹配' %}
-
- {% endfor %}
- {% if request.GET.q and user.is_authenticated %}
-
- {% trans '正在实时搜索站外条目' %}
-
-
- {% endif %}
-
-
-
-
-
-
-
- {% include "search_sidebar.html" %}
-
-
+ {% include "common_libs.html" with jquery=0 v2=1 %}
+
+
+ {% include '_header.html' %}
+
+
+
+
+ {% if request.GET.q %}
+
“{{ request.GET.q }}” {% trans '的搜索结果' %}
+ {% endif %}
+ {% if request.GET.tag %}
+
{% trans '含有标签' %} “{{ request.GET.tag }}” {% trans '的结果' %}
+ {% endif %}
+
+ {% for item in items %}
+ {% with "list_item_"|add:item.class_name|add:".html" as template %}
+ {% include template with show_tags=1 %}
+ {% endwith %}
+ {% empty %}
+ {% trans '无站内条目匹配' %}
+ {% endfor %}
+ {% if request.GET.q and user.is_authenticated %}
+
+ {% trans '正在实时搜索站外条目' %}
+
+
+ {% endif %}
+
+
+
- {% include 'partial/_footer.html' %}
-
-
-
-
-
-
-
-
+
+ {% include "search_sidebar.html" %}
+
+ {% include 'partial/_footer.html' %}
+
diff --git a/catalog/templates/search_sidebar.html b/catalog/templates/search_sidebar.html
index 4b543218..1bda5958 100644
--- a/catalog/templates/search_sidebar.html
+++ b/catalog/templates/search_sidebar.html
@@ -1,46 +1,42 @@
{% load static %}
{% load i18n %}
{% load l10n %}
-
-
+
+
diff --git a/catalog/templates/sidebar_item.html b/catalog/templates/sidebar_item.html
index ab52f96c..973c5e2f 100644
--- a/catalog/templates/sidebar_item.html
+++ b/catalog/templates/sidebar_item.html
@@ -2,30 +2,26 @@
{% load i18n %}
{% load truncate %}
{% load thumb %}
-
-
-
-
-
-
-
{{ item.title }}
- {% for res in item.external_resources.all %}
+
+
+
-
- {% if item.isbn %}
-
ISBN: {{ item.isbn }}
- {% endif %}
-
-
{% if item.pub_house %}{% trans '出版社:' %}{{ item.pub_house }}{% endif %}
- {% if item.rating %}
- {% trans '评分: ' %}
-
{{ item.rating | floatformat:1 }}
+ {% endfor %}
+
+ {% if item.isbn %}
ISBN: {{ item.isbn }}
{% endif %}
+
+ {% if item.pub_house %}
+ {% trans '出版社:' %}{{ item.pub_house }}
{% endif %}
-
-
-
\ No newline at end of file
+
+
diff --git a/catalog/templates/tvseason.html b/catalog/templates/tvseason.html
index 2a6221da..3ca6f887 100644
--- a/catalog/templates/tvseason.html
+++ b/catalog/templates/tvseason.html
@@ -9,178 +9,201 @@
{% load truncate %}
{% load strip_scheme %}
{% load thumb %}
-
{% block title %}
-
- {% if item.season_number %}
- {{ item.title }} {{ item.orig_title }} Season {{ item.season_number }}
-
- {% if item.year %}({{ item.year }}){% endif %}
-
- {% else %}
- {{ item.title }} {{ item.orig_title }}
-
- {% if item.year %}({{ item.year }}){% endif %}
-
- {% endif %}
-
- {% for res in item.external_resources.all %}
-
- {{ res.site_name.label }}
-
- {% endfor %}
-
+
+ {% if item.season_number %}
+ {{ item.title }} {{ item.orig_title }} Season {{ item.season_number }}
+
+ {% if item.year %}({{ item.year }}){% endif %}
+
+ {% else %}
+ {{ item.title }} {{ item.orig_title }}
+
+ {% if item.year %}({{ item.year }}){% endif %}
+
+ {% endif %}
+ {% for res in item.external_resources.all %}
+
+ {{ res.site_name.label }}
+
+ {% endfor %}
+
{% endblock %}
-
{% block details %}
-
-
- {% if item.rating and item.rating_count >= 5 %}
-
- {{ item.rating | floatformat:1 }}
- ({{ item.rating_count }}人评分)
- {% else %}
- {% trans '评分:评分人数不足' %}
- {% endif %}
-
-
-
{% if item.director %}{% trans '导演:' %}
- {% for director in item.director %}
-
5 %}style="display: none;" {% endif %}>
- {{ director }}
- {% if not forloop.last %} / {% endif %}
-
- {% endfor %}
- {% if item.director|length > 5 %}
-
{% trans '更多' %}
-
- {% endif %}
- {% endif %}
-
{% if item.playwright %}{% trans '编剧:' %}
- {% for playwright in item.playwright %}
-
5 %}style="display: none;" {% endif %}>
- {{ playwright }}
- {% if not forloop.last %} / {% endif %}
-
- {% endfor %}
- {% if item.playwright|length > 5 %}
-
{% trans '更多' %}
-
+ {% endif %}
+ {% endif %}
+
+
+ {% if item.playwright %}
+ {% trans '编剧:' %}
+ {% for playwright in item.playwright %}
+
5 %}style="display: none;"{% endif %}>
+ {{ playwright }}
+ {% if not forloop.last %}/{% endif %}
+
+ {% endfor %}
+ {% if item.playwright|length > 5 %}
+
{% trans '更多' %}
+
- {% endif %}
- {% endif %}
-
{% if item.actor %}{% trans '主演:' %}
- {% for actor in item.actor %}
-
5 %}style="display: none;"{% endif %}>
- {{ actor }}
- {% if not forloop.last %} / {% endif %}
-
- {% endfor %}
- {% if item.actor|length > 5 %}
-
{% trans '更多' %}
-
+ {% endif %}
+ {% endif %}
+
+
+ {% if item.actor %}
+ {% trans '主演:' %}
+ {% for actor in item.actor %}
+
5 %}style="display: none;"{% endif %}>
+ {{ actor }}
+ {% if not forloop.last %}/{% endif %}
+
+ {% endfor %}
+ {% if item.actor|length > 5 %}
+
{% trans '更多' %}
+
- {% endif %}
-
- {% endif %}
-
{% if item.genre %}{% trans '类型:' %}
- {% for genre in item.genre %}
- {{ genre }} {% if not forloop.last %} / {% endif %}
- {% endfor %}
- {% endif %}
-
{% if item.area %}{% trans '制片国家/地区:' %}
- {% for area in item.area %}
- {{ area }} {% if not forloop.last %} / {% endif %}
- {% endfor %}
- {% endif %}
-
{% if item.language %}{% trans '语言:' %}
- {% for language in item.language %}
- {{ language }} {% if not forloop.last %} / {% endif %}
- {% endfor %}
- {% endif %}
+
+ {% endif %}
+ {% endif %}
+
+
+ {% if item.genre %}
+ {% trans '类型:' %}
+ {% for genre in item.genre %}
+ {{ genre }}
+ {% if not forloop.last %}/{% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+ {% if item.area %}
+ {% trans '制片国家/地区:' %}
+ {% for area in item.area %}
+ {{ area }}
+ {% if not forloop.last %}/{% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+ {% if item.language %}
+ {% trans '语言:' %}
+ {% for language in item.language %}
+ {{ language }}
+ {% if not forloop.last %}/{% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+ {% if item.season_number %}
+ {% trans '本季序号:' %}{{ item.season_number }}
+ {% endif %}
+
+
+ {% if item.episode_count %}
+ {% trans '本季集数:' %}{{ item.episode_count }}
+ {% endif %}
+
+
+
-
-
{% if item.season_number %}{% trans '本季序号:' %}{{ item.season_number }}{% endif %}
-
{% if item.episode_count %}{% trans '本季集数:' %}{{ item.episode_count }}{% endif %}
-
-
-
{% if item.season_count %}{% trans '总季数:' %}{{ item.season_count }}{% endif %}
-
{% if item.single_episode_length %}{% trans '单集长度:' %}{{ item.single_episode_length }}{% endif %}
- {% with item.all_seasons as seasons %}
- {% if seasons %}
-
- {% trans '本剧所有季:' %}
- {% for s in seasons %}
-
- {{ s.season_number }}
-
+
+ {% if item.season_count %}
+ {% trans '总季数:' %}{{ item.season_count }}
+ {% endif %}
+
+
+ {% if item.single_episode_length %}
+ {% trans '单集长度:' %}{{ item.single_episode_length }}
+ {% endif %}
+
+{% with item.all_seasons as seasons %}
+ {% if seasons %}
+
+ {% endif %}
+{% endwith %}
+
+ {% if item.showtime %}
+ {% trans '上映时间:' %}
+ {% for showtime in item.showtime %}
+ {% for time, region in showtime.items %}
+ {{ time }}
+ {% if region != '' %}({{ region }}){% endif %}
+
+ {% endfor %}
+ {% if not forloop.last %}/{% endif %}
{% endfor %}
-
- {% endif %}
- {% endwith %}
-
-
{% if item.showtime %}{% trans '上映时间:' %}
- {% for showtime in item.showtime %}
- {% for time, region in showtime.items %}
- {{ time }}{% if region != '' %}({{ region }}){% endif %}
- {% endfor %}
- {% if not forloop.last %} / {% endif %}
- {% endfor %}
- {% endif %}
-
{% if item.other_title %}{% trans '又名:' %}
- {% for t in item.other_title %}
- {{ t }} {% if not forloop.last %} / {% endif %}
- {% endfor %}
- {% endif %}
-
- {% if item.other_info %}
- {% for k, v in item.other_info.items %}
-
- {{ k }}:{{ v | urlize }}
-
- {% endfor %}
- {% endif %}
-
-
- {% if item.last_editor and item.last_editor.preference.show_last_edit %}
-
- {% endif %}
-
-
+ {% endif %}
+
+ {% if item.other_title %}
+ {% trans '又名:' %}
+ {% for t in item.other_title %}
+ {{ t }}
+ {% if not forloop.last %}/{% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+{% if item.other_info %}
+ {% for k, v in item.other_info.items %}
{{ k }}:{{ v|urlizetrunc:24 }}
{% endfor %}
+{% endif %}
{% endblock %}
-
-{% block sidebar %}
-{% endblock %}
+{% block sidebar %}{% endblock %}
diff --git a/catalog/templates/tvshow.html b/catalog/templates/tvshow.html
index ea3e5250..12afc5ac 100644
--- a/catalog/templates/tvshow.html
+++ b/catalog/templates/tvshow.html
@@ -9,179 +9,167 @@
{% load truncate %}
{% load strip_scheme %}
{% load thumb %}
-
-{% block title %}
-
- {% if item.season_number %}
- {{ item.title }} {% trans '第' %}{{ item.season_number|apnumber }}{% trans '季' %} {{ item.orig_title }} Season {{ item.season_number }}
-
- {% if item.year %}({{ item.year }}){% endif %}
-
- {% else %}
- {{ item.title }} {{ item.orig_title }}
-
- {% if item.year %}({{ item.year }}){% endif %}
-
- {% endif %}
-
- {% for res in item.external_resources.all %}
-
- {{ res.site_name.label }}
-
- {% endfor %}
-
-{% endblock %}
-
{% block details %}
-
-
- {% if item.rating and item.rating_count >= 5 %}
-
- {{ item.rating | floatformat:1 }}
- ({{ item.rating_count }}人评分)
- {% else %}
- {% trans '评分:评分人数不足' %}
- {% endif %}
-
-
-
{% if item.director %}{% trans '导演:' %}
- {% for director in item.director %}
-
5 %}style="display: none;" {% endif %}>
- {{ director }}
- {% if not forloop.last %} / {% endif %}
-
- {% endfor %}
- {% if item.director|length > 5 %}
-
{% trans '更多' %}
-
- {% endif %}
- {% endif %}
-
{% if item.playwright %}{% trans '编剧:' %}
- {% for playwright in item.playwright %}
-
5 %}style="display: none;" {% endif %}>
- {{ playwright }}
- {% if not forloop.last %} / {% endif %}
-
- {% endfor %}
- {% if item.playwright|length > 5 %}
-
{% trans '更多' %}
-
+ {% endif %}
+ {% endif %}
+
+
+ {% if item.playwright %}
+ {% trans '编剧:' %}
+ {% for playwright in item.playwright %}
+
5 %}style="display: none;"{% endif %}>
+ {{ playwright }}
+ {% if not forloop.last %}/{% endif %}
+
+ {% endfor %}
+ {% if item.playwright|length > 5 %}
+
{% trans '更多' %}
+
- {% endif %}
- {% endif %}
-
{% if item.actor %}{% trans '主演:' %}
- {% for actor in item.actor %}
-
5 %}style="display: none;"{% endif %}>
- {{ actor }}
- {% if not forloop.last %} / {% endif %}
-
- {% endfor %}
- {% if item.actor|length > 5 %}
-
{% trans '更多' %}
-
+ {% endif %}
+ {% endif %}
+
+
+ {% if item.actor %}
+ {% trans '主演:' %}
+ {% for actor in item.actor %}
+
5 %}style="display: none;"{% endif %}>
+ {{ actor }}
+ {% if not forloop.last %}/{% endif %}
+
+ {% endfor %}
+ {% if item.actor|length > 5 %}
+
{% trans '更多' %}
+
- {% endif %}
- {% endif %}
-
{% if item.genre %}{% trans '类型:' %}
- {% for genre in item.genre %}
- {{ genre }} {% if not forloop.last %} / {% endif %}
- {% endfor %}
- {% endif %}
-
{% if item.area %}{% trans '制片国家/地区:' %}
- {% for area in item.area %}
- {{ area }} {% if not forloop.last %} / {% endif %}
- {% endfor %}
- {% endif %}
-
{% if item.language %}{% trans '语言:' %}
- {% for language in item.language %}
- {{ language }} {% if not forloop.last %} / {% endif %}
- {% endfor %}
- {% endif %}
-
-
-
-
{% if item.season_count %}{% trans '季数:' %}{{ item.season_count }}{% endif %}
-
{% if item.episode_count %}{% trans '集数:' %}{{ item.episode_count }}{% endif %}
-
{% if item.single_episode_length %}{% trans '单集长度:' %}{{ item.single_episode_length }}{% endif %}
-
- {% with item.all_seasons as seasons %}
- {% if seasons %}
-
- {% endif %}
- {% endwith %}
-
-
{% if item.showtime %}{% trans '播出时间:' %}
- {% for showtime in item.showtime %}
- {% for time, region in showtime.items %}
- {{ time }}{% if region != '' %}({{ region }}){% endif %}
- {% endfor %}
- {% if not forloop.last %} / {% endif %}
- {% endfor %}
- {% endif %}
-
{% if item.other_title %}{% trans '又名:' %}
- {% for t in item.other_title %}
- {{ t }} {% if not forloop.last %} / {% endif %}
- {% endfor %}
- {% endif %}
-
- {% if item.other_info %}
- {% for k, v in item.other_info.items %}
-
- {{ k }}:{{ v | urlize }}
-
- {% endfor %}
- {% endif %}
-
-
- {% if item.last_editor and item.last_editor.preference.show_last_edit %}
-
- {% endif %}
-
-
-
-{% endblock %}
-
-
-{% block sidebar %}
+
+ {% endif %}
+ {% endif %}
+
+
+ {% if item.genre %}
+ {% trans '类型:' %}
+ {% for genre in item.genre %}
+ {{ genre }}
+ {% if not forloop.last %}/{% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+ {% if item.area %}
+ {% trans '制片国家/地区:' %}
+ {% for area in item.area %}
+ {{ area }}
+ {% if not forloop.last %}/{% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+ {% if item.language %}
+ {% trans '语言:' %}
+ {% for language in item.language %}
+ {{ language }}
+ {% if not forloop.last %}/{% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+ {% if item.season_count %}
+ {% trans '季数:' %}{{ item.season_count }}
+ {% endif %}
+
+
+ {% if item.episode_count %}
+ {% trans '集数:' %}{{ item.episode_count }}
+ {% endif %}
+
+
+ {% if item.single_episode_length %}
+ {% trans '单集长度:' %}{{ item.single_episode_length }}
+ {% endif %}
+
+ {% with item.all_seasons as seasons %}
+ {% if seasons %}
+
+ {% endif %}
+ {% endwith %}
+
+ {% if item.showtime %}
+ {% trans '播出时间:' %}
+ {% for showtime in item.showtime %}
+ {% for time, region in showtime.items %}
+ {{ time }}
+ {% if region != '' %}({{ region }}){% endif %}
+
+ {% endfor %}
+ {% if not forloop.last %}/{% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+ {% if item.other_title %}
+ {% trans '又名:' %}
+ {% for t in item.other_title %}
+ {{ t }}
+ {% if not forloop.last %}/{% endif %}
+ {% endfor %}
+ {% endif %}
+
+
+ {% if item.other_info %}
+ {% for k, v in item.other_info.items %}
{{ k }}:{{ v|urlizetrunc:24 }}
{% endfor %}
+ {% endif %}
{% endblock %}
diff --git a/catalog/templates/work.html b/catalog/templates/work.html
index 16df8f0e..d354a06d 100644
--- a/catalog/templates/work.html
+++ b/catalog/templates/work.html
@@ -9,11 +9,7 @@
{% load truncate %}
{% load strip_scheme %}
{% load thumb %}
-
-{% block details %}
-{% endblock %}
-
+{% block details %}{% endblock %}
-{% block sidebar %}
-{% endblock %}
+{% block sidebar %}{% endblock %}
diff --git a/catalog/urls.py b/catalog/urls.py
index 63f4927a..468861cb 100644
--- a/catalog/urls.py
+++ b/catalog/urls.py
@@ -72,10 +72,24 @@ urlpatterns = [
recast,
name="recast",
),
+ re_path(
+ r"^(?P
"
+ + _get_all_url_paths()
+ + ")/(?P[A-Za-z0-9]{21,22})/comments",
+ comments,
+ name="comments",
+ ),
re_path(
r"^(?P"
+ _get_all_url_paths()
+ ")/(?P[A-Za-z0-9]{21,22})/reviews",
+ reviews,
+ name="reviews",
+ ),
+ re_path(
+ r"^(?P"
+ + _get_all_url_paths()
+ + ")/(?P[A-Za-z0-9]{21,22})/review_list",
review_list,
name="review_list",
),
diff --git a/catalog/views.py b/catalog/views.py
index c52cf8f1..94169dad 100644
--- a/catalog/views.py
+++ b/catalog/views.py
@@ -11,7 +11,7 @@ from django.core.paginator import Paginator
from catalog.common.models import ExternalResource, IdealIdTypes
from .models import *
from django.views.decorators.clickjacking import xframe_options_exempt
-from journal.models import Mark, ShelfMember, Review, query_item_category
+from journal.models import Mark, ShelfMember, Review, Comment, query_item_category
from journal.models import (
query_visible,
query_following,
@@ -85,32 +85,28 @@ def retrieve(request, item_path, item_uuid):
)
mark = None
review = None
- mark_list = None
- review_list = None
+ my_collections = []
collection_list = []
shelf_types = [(n[1], n[2]) for n in iter(ShelfTypeNames) if n[0] == item.category]
if request.user.is_authenticated:
visible = query_visible(request.user)
mark = Mark(request.user, item)
review = mark.review
+ my_collections = item.collections.all().filter(owner=request.user)
collection_list = (
item.collections.all()
+ .exclude(owner=request.user)
.filter(visible)
.annotate(like_counts=Count("likes"))
.order_by("-like_counts")
)
- mark_query = (
- ShelfMember.objects.filter(item=item)
- .filter(visible)
- .order_by("-created_time")
+ else:
+ collection_list = (
+ item.collections.all()
+ .filter(visibility=0)
+ .annotate(like_counts=Count("likes"))
+ .order_by("-like_counts")
)
- mark_list = [member.mark for member in mark_query[:NUM_REVIEWS_ON_ITEM_PAGE]]
- review_list = (
- Review.objects.filter(item=item)
- .filter(visible)
- .order_by("-created_time")[:NUM_REVIEWS_ON_ITEM_PAGE]
- )
-
return render(
request,
item.class_name + ".html",
@@ -119,8 +115,7 @@ def retrieve(request, item_path, item_uuid):
"focus_item": focus_item,
"mark": mark,
"review": review,
- "mark_list": mark_list,
- "review_list": review_list,
+ "my_collections": my_collections,
"collection_list": collection_list,
"shelf_types": shelf_types,
},
@@ -273,7 +268,7 @@ def episode_data(request, item_uuid):
if request.GET.get("last"):
qs = qs.filter(pub_date__lt=request.GET.get("last"))
return render(
- request, "podcast_episode_data.html", {"item": item, "episodes": qs[:10]}
+ request, "podcast_episode_data.html", {"item": item, "episodes": qs[:5]}
)
@@ -326,27 +321,62 @@ def review_list(request, item_path, item_uuid):
@login_required
+def comments(request, item_path, item_uuid):
+ item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
+ if not item:
+ raise Http404()
+ queryset = Comment.objects.filter(item=item).order_by("-created_time")
+ queryset = queryset.filter(query_visible(request.user))
+ before_time = request.GET.get("last")
+ if before_time:
+ queryset = queryset.filter(created_time__lte=before_time)
+ return render(
+ request,
+ "item_comments.html",
+ {
+ "comments": queryset[:11],
+ },
+ )
+
+
+@login_required
+def reviews(request, item_path, item_uuid):
+ item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
+ if not item:
+ raise Http404()
+ queryset = Review.objects.filter(item=item).order_by("-created_time")
+ queryset = queryset.filter(query_visible(request.user))
+ before_time = request.GET.get("last")
+ if before_time:
+ queryset = queryset.filter(created_time__lte=before_time)
+ return render(
+ request,
+ "item_reviews.html",
+ {
+ "reviews": queryset[:11],
+ },
+ )
+
+
def discover(request):
if request.method != "GET":
raise BadRequest()
user = request.user
if user.is_authenticated:
layout = user.get_preference().discover_layout
- top_tags = user.tag_manager.all_tags[:10]
else:
layout = []
- top_tags = []
- cache_key = "public_gallery_list"
+ cache_key = "public_gallery"
gallery_list = cache.get(cache_key, [])
- for gallery in gallery_list:
- ids = (
- random.sample(gallery["item_ids"], 10)
- if len(gallery["item_ids"]) > 10
- else gallery["item_ids"]
- )
- gallery["items"] = Item.objects.filter(id__in=ids)
+ # for gallery in gallery_list:
+ # ids = (
+ # random.sample(gallery["item_ids"], 10)
+ # if len(gallery["item_ids"]) > 10
+ # else gallery["item_ids"]
+ # )
+ # gallery["items"] = Item.objects.filter(id__in=ids)
if user.is_authenticated:
podcast_ids = [
@@ -355,41 +385,39 @@ def discover(request):
ShelfType.PROGRESS, ItemCategory.Podcast
)
]
- episodes = PodcastEpisode.objects.filter(program_id__in=podcast_ids).order_by(
- "-pub_date"
- )[:5]
- gallery_list.insert(
- 0,
- {
- "name": "my_recent_podcasts",
- "title": "在听播客的近期更新",
- "items": episodes,
- },
+ recent_podcast_episodes = PodcastEpisode.objects.filter(
+ program_id__in=podcast_ids
+ ).order_by("-pub_date")[:10]
+ books_in_progress = Edition.objects.filter(
+ id__in=[
+ p.item_id
+ for p in user.shelf_manager.get_members(
+ ShelfType.PROGRESS, ItemCategory.Book
+ ).order_by("-created_time")[:10]
+ ]
)
- # books = Edition.objects.filter(
- # id__in=[
- # p.item_id
- # for p in user.shelf_manager.get_members(
- # ShelfType.PROGRESS, ItemCategory.Book
- # ).order_by("-created_time")[:10]
- # ]
- # )
- # gallery_list.insert(
- # 0,
- # {
- # "name": "my_books_inprogress",
- # "title": "正在读的书",
- # "items": books,
- # },
- # )
+ tvshows_in_progress = Item.objects.filter(
+ id__in=[
+ p.item_id
+ for p in user.shelf_manager.get_members(
+ ShelfType.PROGRESS, ItemCategory.TV
+ ).order_by("-created_time")[:10]
+ ]
+ )
+ else:
+ recent_podcast_episodes = []
+ books_in_progress = []
+ tvshows_in_progress = []
return render(
request,
"discover.html",
{
"user": user,
- "top_tags": top_tags,
"gallery_list": gallery_list,
+ "recent_podcast_episodes": recent_podcast_episodes,
+ "books_in_progress": books_in_progress,
+ "tvshows_in_progress": tvshows_in_progress,
"layout": layout,
},
)
diff --git a/common/static/lib/css/calendar_yearview_blocks.css b/common/static/lib/css/calendar_yearview_blocks.css
index d5785d0e..81045f0a 100644
--- a/common/static/lib/css/calendar_yearview_blocks.css
+++ b/common/static/lib/css/calendar_yearview_blocks.css
@@ -26,6 +26,5 @@
.wday, .month {
font-variant: small-caps;
- color: #222222;
font-size: 12px;
}
diff --git a/common/static/lib/css/collection.css b/common/static/lib/css/collection.css
index 887addb3..12addddb 100644
--- a/common/static/lib/css/collection.css
+++ b/common/static/lib/css/collection.css
@@ -180,8 +180,8 @@ div.jsoneditor-menu {
}
.donut {
- margin-left: 20%;
- width: 60%;
+ margin-left: 10%;
+ width: 80%;
aspect-ratio : 1 / 1;
border-radius: 50%;
display: flex;
@@ -198,7 +198,7 @@ div.jsoneditor-menu {
justify-content: center;
}
-progress {
+/* progress {
border-radius: 7px;
width: 80%;
height: 10px;
@@ -209,7 +209,7 @@ progress {
}
::-webkit-progress-value {
background-color: #00a1cc;
-}
+} */
.markdownx-editor {
font-family: monospace;
@@ -266,6 +266,6 @@ summary::-webkit-details-marker {
transform: rotate(360deg);
}
-.fa-lock, .fa-spin {
+.action-bar .fa-lock, .fa-spin {
color: lightgray;
}
diff --git a/common/static/lib/js/calendar_yearview_blocks.js b/common/static/lib/js/calendar_yearview_blocks.js
index 6c7abf06..40826d1a 100644
--- a/common/static/lib/js/calendar_yearview_blocks.js
+++ b/common/static/lib/js/calendar_yearview_blocks.js
@@ -167,7 +167,7 @@
// Fixed size with width= 721 and height = 110
var wire_html =
- '' +
+ '' +
'' +
loop_html +
' ' + '"Your browser does not support inline SVG."' +
diff --git a/common/static/sass/_common.sass b/common/static/sass/_common.sass
new file mode 100644
index 00000000..8cc005a5
--- /dev/null
+++ b/common/static/sass/_common.sass
@@ -0,0 +1,15 @@
+
+@mixin clear
+ content: ' '
+ clear: both
+ display: table
+
+// Breakpoints
+// Small devices (landscape phones, 576px and up)
+$small-devices: 575.98px
+// Medium devices (tablets, 768px and up)
+$medium-devices: 767.98px
+// Large devices (desktops, 992px and up)
+$large-devices: 991.98px
+// Extra large devices (large desktops, 1200px and up)
+$x-large-devices: 1199.98px
diff --git a/common/static/scss/_card.scss b/common/static/scss/_card.scss
new file mode 100644
index 00000000..8929d285
--- /dev/null
+++ b/common/static/scss/_card.scss
@@ -0,0 +1,48 @@
+div.item {
+ display: grid;
+ grid-template-columns:
+ calc(4rem * var(--pico-line-height)) auto;
+ column-gap: var(--pico-block-spacing-horizontal);
+ align-items: top;
+ justify-content: left;
+ word-break: break-all;
+
+ &.player {
+ display: block;
+ }
+
+ hgroup {
+ margin: 0;
+ }
+
+ h5 small,
+ hgroup span {
+ font-weight: 400;
+ font-size: 80%;
+ color: var(--pico-muted-color);
+
+ &.category {
+ opacity: 0.5;
+ }
+
+ }
+
+ .metadata {
+ display: -webkit-box;
+ -webkit-line-clamp: 1;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ font-size: 80%;
+ }
+
+ .brief {
+ text-overflow: ellipsis;
+ word-wrap: break-word;
+ overflow: hidden;
+ -webkit-line-clamp: 4;
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ font-size: 80%;
+ }
+
+}
diff --git a/common/static/scss/_collection.scss b/common/static/scss/_collection.scss
new file mode 100644
index 00000000..cce0f745
--- /dev/null
+++ b/common/static/scss/_collection.scss
@@ -0,0 +1,25 @@
+#collection-page {
+ @media (max-width: 768px) {
+ main>article>header {
+ display: flex;
+ flex-flow: column nowrap;
+
+ #collection-cover {
+ img {
+ height: 20vw;
+ }
+
+ height: unset !important;
+ order: -1;
+ }
+
+ #collection-feature {
+ order: 99;
+
+ .donut {
+ display: none;
+ }
+ }
+ }
+ }
+}
diff --git a/common/static/scss/_common.scss b/common/static/scss/_common.scss
new file mode 100644
index 00000000..9f44418b
--- /dev/null
+++ b/common/static/scss/_common.scss
@@ -0,0 +1,95 @@
+body {
+ --pico-block-spacing-vertical: var(--pico-block-spacing-horizontal);
+}
+
+article.item-card {
+ padding: calc(var(--pico-block-spacing-horizontal));
+ margin-bottom: calc(var(--pico-block-spacing-vertical) / 2);
+
+ header,
+ footer {
+ padding: calc(var(--pico-block-spacing-horizontal) / 2);
+ }
+
+ section {
+ margin-bottom: calc(var(--pico-spacing) / 1);
+ }
+}
+
+article.item-card.external h5 {
+ font-style: italic;
+}
+
+details {
+ margin: 0;
+}
+
+section section {
+ margin-bottom: var(--pico-spacing) !important;
+}
+
+.empty {
+ font-style: italic;
+ font-size: 80%;
+ color: var(--pico-muted-color);
+}
+
+.oneline {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+}
+
+.handler {
+ color: var(--pico-muted-color);
+ word-break: break-all;
+ opacity: 0.5;
+}
+
+.loading {
+ color: var(--pico-muted-color);
+ opacity: 0.5;
+}
+
+.action {
+ width: max-content;
+ float: right;
+
+ &.inline {
+ float: none;
+ }
+
+ >span {
+ padding-left: calc(var(--pico-spacing)/2);
+ }
+
+ a {
+ cursor: pointer;
+ text-decoration: none;
+ }
+
+ a.hidden {
+ display: none;
+ }
+
+ a.disabled,
+ .timestamp {
+ cursor: unset;
+ color: var(--pico-muted-color);
+ opacity: 0.5;
+ }
+
+ a:not(:hover) {
+ color: var(--pico-muted-color);
+ opacity: 0.5;
+ }
+
+ a.activated:not(:hover) {
+ color: var(--pico-primary);
+ opacity: 1;
+ }
+}
+
+form img {
+ max-height: 20vh;
+}
diff --git a/common/static/scss/_dialog.scss b/common/static/scss/_dialog.scss
new file mode 100644
index 00000000..cfba0596
--- /dev/null
+++ b/common/static/scss/_dialog.scss
@@ -0,0 +1,103 @@
+dialog {
+ header {
+ margin-bottom: 16px;
+ padding: 8px !important;
+ }
+
+ &>article {
+ /* Animate when opening */
+ animation-name: zoomIn;
+ animation-duration: 150ms;
+ animation-timing-function: ease;
+ }
+
+ &.closing {
+ /* Animate when closing */
+ animation-name: fadeOut;
+ animation-duration: 150ms;
+ animation-timing-function: ease;
+ }
+
+ &.closing>article {
+ /* Aniate when closing */
+ animation-name: zoomOut;
+ animation-duration: 150ms;
+ animation-timing-function: ease;
+ }
+
+ @keyframes fadeIn {
+ 0% {
+ opacity: 0;
+ }
+
+ 100% {
+ opacity: 1;
+ }
+ }
+
+ @keyframes fadeOut {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+ }
+
+ @keyframes zoomIn {
+ 0% {
+ transform: scale(0.9);
+ }
+
+ 100% {
+ transform: scale(1);
+ }
+ }
+
+ @keyframes zoomOut {
+ 0% {
+ transform: scale(1);
+ }
+
+ 100% {
+ transform: scale(0.9);
+ }
+ }
+
+ .grid>div {
+ min-width: max-content;
+ }
+
+ .grid>div:nth-child(2) fieldset {
+ float: right;
+ }
+
+
+ @media (max-width: 768px) {
+ .grid>div:nth-child(2) fieldset {
+ float: unset;
+ }
+ }
+
+ fieldset {
+ margin: 0;
+ }
+
+ article {
+ padding-bottom: 1em;
+ }
+
+ input[type="date"] {
+ margin: 0px;
+ }
+
+ .tag-input {
+ margin-bottom: var(--pico-spacing);
+ }
+
+ .rating-editor {
+ font-size: 120%;
+ padding-bottom: 1em;
+ }
+}
diff --git a/common/static/scss/_feed.scss b/common/static/scss/_feed.scss
new file mode 100644
index 00000000..cc7ed93e
--- /dev/null
+++ b/common/static/scss/_feed.scss
@@ -0,0 +1,63 @@
+.feed-page {
+ .feed {
+ .avatar {
+ height: calc(1rem * var(--pico-line-height) + var(--pico-nav-link-spacing-vertical) * 4 - 8px);
+ width: calc(1rem * var(--pico-line-height) + var(--pico-nav-link-spacing-vertical) * 4 - 8px);
+
+ img {
+ height: 100%;
+ width: 100%;
+ }
+ }
+
+ section {
+ margin-bottom: 2vh;
+ display: grid;
+ grid-template-columns: auto 1fr;
+ grid-template-rows: auto;
+ grid-column-gap: 1em;
+ grid-row-gap: 0px;
+ // align-items: center;
+ // justify-content: left;
+
+ .nickname a {
+ word-break: break-all;
+ }
+
+ .time {
+ width: max-content;
+ float: right;
+ color: var(--pico-muted-color);
+ opacity: 0.5;
+ margin-left: 1em;
+ }
+
+ .rating-star {
+ margin: 0 0.5em;
+ }
+
+ .rating-star,
+ .rating-star>* {
+ color: var(--pico-primary);
+ }
+
+
+ article {
+ padding: calc(var(--pico-block-spacing-horizontal) / 2);
+
+ footer {
+ padding: calc(var(--pico-block-spacing-horizontal) / 2);
+ margin-left: calc(var(--pico-block-spacing-horizontal) * -0.5);
+ margin-right: calc(var(--pico-block-spacing-horizontal) * -0.5);
+ margin-top: 0;
+ }
+
+ margin-right: 4px;
+ }
+
+ .spacing {
+ margin-bottom: var(--pico-block-spacing-horizontal);
+ }
+ }
+ }
+}
diff --git a/common/static/scss/_gallery.scss b/common/static/scss/_gallery.scss
new file mode 100644
index 00000000..d3e8f0fb
--- /dev/null
+++ b/common/static/scss/_gallery.scss
@@ -0,0 +1,120 @@
+.calendar_view {
+ overflow-x: scroll;
+
+ svg {
+ height: 108px;
+ }
+}
+
+.shelf {
+ max-width: 1400px;
+ padding: 0;
+
+ * {
+ box-sizing: border-box;
+ padding: 0;
+ margin: 0;
+ }
+
+ .cards {
+ display: grid;
+ grid-auto-columns: 100%;
+ grid-column-gap: var(--pico-block-spacing-horizontal);
+ grid-auto-flow: column;
+ padding: var(--pico-nav-link-spacing-vertical) 0px;
+ list-style: none;
+ overflow-x: scroll;
+ scroll-snap-type: x mandatory;
+
+ &::-webkit-scrollbar {
+ height: var(--pico-nav-link-spacing-vertical);
+ }
+
+ &::-webkit-scrollbar-thumb,
+ &::-webkit-scrollbar-track {
+ border-radius: 92px;
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background: var(--pico-range-active-border-color);
+ }
+
+ &::-webkit-scrollbar-track {
+ background: var(--pico-range-border-color);
+ }
+
+
+ }
+
+ .card {
+ display: flex;
+ flex-direction: column;
+ padding: 0px;
+ // border-radius: 5px;
+ scroll-snap-align: start;
+ transition: all 0.2s;
+ text-align: center;
+ vertical-align: middle;
+
+ a {
+ margin-top: auto;
+ }
+
+ img {
+ background: var(--pico-background-color);
+ // border-radius: 5px;
+ width: 100%;
+ min-height: 3rem;
+ vertical-align: middle;
+ margin: auto;
+ font-size: 80%;
+ border: 2px solid transparent;
+ }
+
+ a:hover img {
+ border: 2px solid var(--pico-primary-hover);
+ }
+
+ a>div {
+ font-size: 80%;
+ text-overflow: ellipsis;
+ word-wrap: break-word;
+ overflow: hidden;
+ -webkit-line-clamp: 2;
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ min-height: 2.5rem;
+ }
+ }
+
+ @media (min-width: 200px) {
+ .cards {
+ grid-auto-columns: calc(25% - var(--pico-block-spacing-horizontal));
+ }
+ }
+
+ @media (min-width: 500px) {
+ .cards {
+ grid-auto-columns: calc(20% - var(--pico-block-spacing-horizontal));
+ }
+ }
+
+ @media (min-width: 769px) {
+ .cards {
+ grid-auto-columns: calc(calc(100% / 6) - var(--pico-block-spacing-horizontal));
+ }
+ }
+
+ @media (min-width: 1281px) {
+ .cards {
+ grid-auto-columns: calc(calc(100% / 8) - var(--pico-block-spacing-horizontal));
+ }
+ }
+}
+
+@media (min-width: 769px) {
+ .sidebar .shelf .cards {
+ grid-auto-columns: calc(34% - var(--pico-spacing)) !important;
+ gap: var(--pico-spacing) !important;
+ }
+}
diff --git a/common/static/scss/_header.scss b/common/static/scss/_header.scss
new file mode 100644
index 00000000..144c2611
--- /dev/null
+++ b/common/static/scss/_header.scss
@@ -0,0 +1,220 @@
+body>header {
+ padding: 0px !important;
+}
+
+.nav-logo {
+ margin-left: 8px !important;
+}
+
+.nav-dropdown {
+ margin-right: 8px !important;
+}
+
+.nav-dropdown ul li a {
+ padding: var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal) !important
+}
+
+nav form {
+ margin-bottom: 0px !important;
+}
+
+nav summary {
+ border: 0px !important;
+}
+
+nav ul {
+ min-width: -webkit-max-content;
+ min-width: -moz-max-content;
+}
+
+nav ul li a.current {
+ font-weight: bold;
+ color: var(--pico-primary);
+}
+
+.nav-search,
+.nav-search li {
+ width: 100%;
+}
+
+.nav-search select {
+ max-width: max-content;
+}
+
+.nav-search input[type="submit"] {
+ background-color: var(--pico-secondary-background);
+ border-color: var(--pico-secondary-background);
+ padding-left: calc(var(--pico-nav-link-spacing-horizontal)*2);
+ padding-right: calc(var(--pico-nav-link-spacing-horizontal)*2);
+}
+
+.nav-logo img {
+ max-height: 56px;
+}
+
+.unhide {
+ display: unset !important;
+}
+
+.nav-dropdown summary {
+ padding-top: 0px !important;
+ padding-bottom: 0px !important;
+}
+
+.nav-dropdown summary::after {
+ height: calc(1rem * var(--pico-line-height, 1.5) + 8px) !important;
+}
+
+.avatar {
+ line-height: 0;
+ /* remove line-height */
+ display: inline-block;
+ /* circle wraps image */
+ border: 4px solid lightgray;
+ border-radius: 50%;
+ /* relative value */
+ transition: linear 0.25s;
+}
+
+.avatar img {
+ border-radius: 50%;
+ height: calc(1rem * var(--pico-line-height) + var(--pico-nav-link-spacing-vertical) * 2 - 8px);
+ width: calc(1rem * var(--pico-line-height) + var(--pico-nav-link-spacing-vertical) * 2 - 8px);
+}
+
+.avatar:hover {
+ transition: ease-out 0.2s;
+ border: 4px solid var(--pico-primary);
+ -webkit-transition: ease-out 0.2s;
+}
+
+@media (max-width: 1200px) {
+ :root {
+ --pico-font-size: 80%;
+ }
+}
+
+@media (min-width: 769px) {
+ .small-only {
+ display: none;
+ }
+
+}
+
+@media (max-width: 768px) {
+ :root {
+ --pico-font-size: 100%;
+ }
+
+ .large-only {
+ display: none;
+ }
+
+ body>header {
+ position: sticky;
+ top: 0px;
+ background: var(--pico-background-color);
+ z-index: 999;
+ box-shadow: var(--pico-box-shadow);
+ }
+
+ nav {
+ display: flex;
+ flex-flow: row wrap;
+ }
+
+ .nav-logo img {
+ max-height: 28px;
+ }
+
+ .nav-search {
+ order: 999;
+ display: none;
+ }
+
+ .nav-search li {
+ padding: 0px;
+ padding-bottom: 4px;
+ }
+
+ .nav-search input[type="submit"] {
+ padding-left: calc(var(--pico-nav-link-spacing-horizontal)*1);
+ padding-right: calc(var(--pico-nav-link-spacing-horizontal)*1);
+ }
+
+ .nav-dropdown::after {
+ flex-basis: 100%;
+ width: 0;
+ }
+
+ .nav-dropdown {
+ margin-right: 0 !important;
+ }
+}
+
+/* Dark color scheme (Auto) */
+/* Automatically enabled if user has Dark mode enabled */
+@media only screen and (prefers-color-scheme: dark) {
+ .nav-logo img {
+ filter: brightness(100%) grayscale(100%) invert(40%);
+ }
+}
+
+/* Dark color scheme (Forced) */
+/* Enabled if forced with data-theme="dark" */
+.nav-logo img [data-theme="dark"] {
+ filter: brightness(100%) grayscale(100%) invert(40%);
+}
+
+.tldr {
+ overflow: hidden;
+ text-overflow: " ... [点击展开]";
+ display: -webkit-box;
+ -webkit-line-clamp: 10;
+ -webkit-box-orient: vertical;
+}
+
+.tldr-2 {
+ overflow: hidden;
+ text-overflow: " ... [点击展开]";
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+}
+
+.hidden {
+ display: none;
+}
+
+.invisible {
+ visibility: hidden;
+}
+
+a:not(:hover) {
+ text-decoration: none;
+}
+
+body>footer {
+ padding-top: 0.4em !important;
+ text-align: center;
+ margin-bottom: 4px !important;
+ width: 100%;
+
+ .footer__border {
+ width: 100%;
+ padding-top: 4px;
+ }
+
+ .footer__link {
+ margin: 0 12px;
+ white-space: nowrap;
+ min-width: 15vw;
+ }
+
+ @media (min-width: 769px) {
+ clear: both;
+ position: absolute !important;
+ left: 50%;
+ transform: translateX(-50%);
+ }
+}
diff --git a/common/static/scss/_item.scss b/common/static/scss/_item.scss
new file mode 100644
index 00000000..81f84870
--- /dev/null
+++ b/common/static/scss/_item.scss
@@ -0,0 +1,165 @@
+#item-page {
+ a:not(:hover) {
+ text-decoration: none;
+ }
+
+ .muted {
+ color: var(--pico-muted-color);
+ }
+
+ .muted a:not(:hover) {
+ color: var(--pico-muted-color);
+ }
+
+ .muted .fa-lock {
+ filter: brightness(50%) !important;
+ }
+
+ a>button {
+ padding: 2px 8px;
+ font-size: 75%;
+ }
+
+ main>div {
+ margin: 2vw;
+ }
+
+ main>div>section {
+ margin-bottom: 2vw;
+ }
+
+ #item-title {
+ margin-bottom: 0;
+ }
+
+ #item-title-more {
+ margin-top: 0;
+ }
+
+ #item-title h1 {
+ margin: 0;
+ }
+
+ h1 small {
+ font-size: 80%;
+ margin-left: 0.5vw;
+ font-weight: 400;
+ }
+
+
+
+ #item-cover {
+ align-items: stretch;
+ }
+
+ #item-cover img {
+ transition: transform 0.2s ease-in-out;
+ filter: drop-shadow(0 0.2rem 0.8rem rgba(0, 0, 0, 0.2));
+ max-width: 80%;
+ max-height: 50vh;
+ }
+
+ .item-edit,
+ .item-edit a {
+ color: var(--pico-muted-color);
+ }
+
+
+
+ .item-action {
+ display: flex;
+ flex-flow: row wrap;
+ justify-content: center;
+ align-items: center;
+ margin-top: 5%;
+ }
+
+ .item-action button {
+ margin-left: 4px;
+ margin-right: 4px;
+ }
+
+ .mark {
+ text-align: center;
+ }
+
+ #item-cover {
+ text-align: center;
+ }
+
+ main {
+ padding-top: 1vh !important;
+ }
+
+ @media (min-width: 769px) {
+ #item-title h1 {
+ display: inline;
+ }
+
+ .middle {
+ margin: auto;
+ width: 50%;
+ }
+
+ .left {
+ float: left;
+ clear: left;
+ width: 22%;
+ margin-left: 0;
+ margin-right: 3%;
+ }
+
+ .right {
+ float: right;
+ clear: right;
+ width: 22%;
+ margin-left: 3%;
+ margin-right: 0%;
+ }
+ }
+
+ @media (max-width: 768px) {
+ main {
+ display: flex;
+ flex-flow: column nowrap;
+ }
+
+ #item-title {
+ order: -5;
+ display: flex;
+ flex-flow: column nowrap;
+ }
+
+ #item-title .site-list {
+ order: -1;
+ }
+
+ #item-title-more {
+ order: -4;
+ }
+
+ #item-cover {
+ order: -3;
+ }
+
+ #item-primary-action {
+ order: -2;
+ }
+
+ #item-primary-mark {
+ order: -2;
+ }
+
+ #item-metadata {
+ order: -1;
+ }
+
+ #item-sidebar {
+ order: 99;
+ }
+
+ #item-cover img {
+ max-width: 60vw;
+ }
+ }
+}
diff --git a/common/static/scss/_layout.scss b/common/static/scss/_layout.scss
new file mode 100644
index 00000000..cc812871
--- /dev/null
+++ b/common/static/scss/_layout.scss
@@ -0,0 +1,144 @@
+.classic-page,
+.feed-page {
+ main {
+ width: 100vw;
+ display: grid;
+ grid-template-columns: 64% 32%;
+ grid-template-areas: "main aside";
+ gap: 4%;
+ padding: calc(3*var(--pico-spacing));
+
+ .grid__main {
+ grid-area: main;
+ }
+
+ .grid__aside {
+ grid-area: aside;
+ }
+ }
+
+ @media (max-width: 768px) {
+ main {
+ display: flex;
+ flex-flow: column nowrap;
+ padding: 0;
+ gap: 0;
+
+ .grid__main {
+ order: -1;
+ margin: var(--pico-spacing);
+ }
+
+ .grid__aside {
+ order: -2;
+ }
+
+ ul {
+ padding: 0;
+ }
+
+ aside.bottom {
+ order: 2 !important;
+ }
+
+
+ }
+ }
+}
+
+.content-page {
+ article {
+ margin: 0;
+ }
+
+ main {
+ display: grid;
+ grid-template-columns: auto 25%;
+ grid-template-areas: "main aside";
+ gap: calc(3*var(--pico-spacing));
+ padding: calc(3*var(--pico-spacing));
+
+ &>div {
+ grid-area: main;
+ }
+
+ &>aside {
+ grid-area: aside;
+
+ article>div {
+ text-align: center;
+ }
+ }
+ }
+
+ .owner-info {
+ height: max-content;
+ display: flex;
+ flex-direction: row;
+ gap: var(--pico-spacing);
+
+ .owner {
+ .avatar {
+ width: calc(2rem * var(--pico-line-height));
+ height: calc(2rem * var(--pico-line-height));
+
+ img {
+ width: 100%;
+ height: 100%;
+ }
+ }
+ }
+
+ .info {
+ width: 70%;
+ }
+
+ .more {
+ width: 20%;
+ }
+ }
+
+ @media (max-width: 768px) {
+ main {
+ display: flex;
+ max-width: 100%;
+ flex-flow: column nowrap;
+ padding: 0;
+ gap: 0;
+
+ >div {
+ order: 1;
+ // margin: var(--pico-spacing);
+ }
+
+ >aside {
+ order: 2;
+
+ article.item {
+ display: grid;
+ grid-template-columns: 25% auto;
+ grid-template-areas: "main aside";
+ margin: calc(var(--pico-spacing)/2);
+ padding: 0;
+ border: var(--pico-border-width) solid var(--pico-card-border-color);
+
+ >* {
+ margin: 0;
+ border: 0;
+ }
+
+ >div {
+ margin-left: var(--pico-spacing);
+ background-color: var(--pico-card-sectioning-background-color);
+ align-items: center;
+ display: flex;
+ }
+ }
+ }
+
+ ul {
+ padding: 0;
+ }
+ }
+ }
+}
diff --git a/common/static/scss/_legacy.sass b/common/static/scss/_legacy.sass
new file mode 100644
index 00000000..45093622
--- /dev/null
+++ b/common/static/scss/_legacy.sass
@@ -0,0 +1,1028 @@
+$color-initial: #fff !default
+$color-primary: #00a1cc !default
+$color-secondary: #606c76 !default
+$color-tertiary: #bbb !default
+$color-quaternary: #d5d5d5 !default
+$color-quinary: #e5e5e5 !default
+
+$color-light: #ccc
+$color-bright: rgb(247, 247, 247)
+
+
+@mixin clear
+ content: ' '
+ clear: both
+ display: table
+
+// Breakpoints
+// Small devices (landscape phones, 576px and up)
+$small-devices: 575.98px
+// Medium devices (tablets, 768px and up)
+$medium-devices: 767.98px
+// Large devices (desktops, 992px and up)
+$large-devices: 767.98px
+// Extra large devices (large desktops, 1200px and up)
+$x-large-devices: 1199.98px
+
+
+$main-section-padding: 32px 48px 32px 36px
+$main-section-padding-mobile: 32px 28px 28px 28px
+
+$section-title-margin: 20px
+$sub-sections-between-margin: 28px
+$sub-section-title-margin: 8px
+
+.main-section-wrapper
+
+ & input, & select
+ width: 100%
+
+// for search result etc
+.entity-list
+ li
+ list-style: none
+
+ & &__title
+ // font-size: large
+ margin-bottom: $section-title-margin
+
+ & &__entities
+
+ & &__entity
+ display: flex
+ margin-bottom: 36px
+ &::after
+ @include clear
+
+ & &__entity-img
+ // float: left
+ object-fit: contain
+ // height: 150px
+ $width: 130px
+ min-width: $width
+ max-width: $width
+
+ & &__entity-text
+ margin-left: 20px
+ overflow: hidden
+ width: 100%
+ // float: left
+ & .tag-collection
+ margin-left: -3px
+
+ & &__entity-link
+ font-size: 1.2em
+
+ & &__entity-title
+ display: block
+
+ & &__entity-category
+ color: $color-tertiary
+ margin-left: 5px
+ position: relative;
+ top: -1px;
+
+ & &__entity-info
+ // max-width: 73%
+ white-space: nowrap
+ overflow: hidden
+ display: inline-block
+ text-overflow: ellipsis
+ position: relative
+ top: 0.52em
+ &--full-length
+ // display: block
+ max-width: 100%
+ // margin-bottom: 12px
+
+ & &__entity-brief
+ margin-top: 8px
+ display: -webkit-box
+ -webkit-box-orient: vertical
+ -webkit-line-clamp: 4
+ overflow: hidden
+ margin-bottom: 0
+
+ $rating-info-gap-width: 5px
+ & &__rating
+ display: inline-block
+ margin: 0
+
+ & &__rating--empty
+ // width: $empty-rating-width
+ margin-right: $rating-info-gap-width
+
+ & &__rating-score
+ margin-right: $rating-info-gap-width
+ position: relative
+ top: 1px
+
+ & &__rating-star
+ display: inline
+ position: relative
+ top: 0.3em
+ left: -0.3em
+
+// detail page
+.entity-detail
+
+ & &__img
+ height: 210px
+ float: left
+ object-fit: contain
+ max-width: 150px
+ object-position: top
+
+ & &__img-origin
+ cursor: zoom-in
+
+ & &__info
+ float: left
+ margin-left: 20px
+ overflow: hidden
+ text-overflow: ellipsis
+ width: 70%
+
+ & &__title
+ font-weight: bold
+
+ & &__title--secondary
+ color: $color-tertiary
+
+ & &__fields
+ display: inline-block
+ vertical-align: top
+ width: 46%
+ margin-left: 2%
+ // margin-bottom: 5px
+ & div, & span
+ margin: 1px 0
+ & + .tag-collection
+ margin-top: 5px
+ margin-left: 6px
+
+ & &__rating
+ position: relative
+ top: -5px
+
+ & &__rating-star
+ position: relative
+ left: -4px
+ top: 3px
+
+ & &__rating-score
+ font-weight: bold
+
+ &::after
+ @include clear
+
+$mark-review-padding: 3px 0
+$mark-review-padding-wider: 6px 0
+
+// includes title and description of an entity
+// sub section
+.entity-desc
+ margin-bottom: $sub-sections-between-margin
+ & &__title
+ display: inline-block
+ margin-bottom: $sub-section-title-margin
+ & &__content
+ overflow: hidden
+ &--folded
+ max-height: 202px
+
+ & &__unfold-button
+ display: flex
+ color: $color-primary
+ background-color: transparent
+ justify-content: center
+ text-align: center
+ &--hidden
+ display: none
+
+ & &__empty
+
+// includes marks of an entity
+// sub section
+.entity-marks
+ margin-bottom: $sub-sections-between-margin
+ & &__title
+ margin-bottom: $sub-section-title-margin
+ display: inline-block
+ & > a
+ margin-right: 5px
+ &--stand-alone
+ margin-bottom: $section-title-margin
+
+ & &__more-link
+ margin-left: 5px
+ & &__mark-list
+
+ & &__mark
+ margin: 0
+ padding: $mark-review-padding
+ border-bottom: 1px dashed $color-quinary
+ &:last-child
+ border: none
+ &--wider
+ padding: $mark-review-padding-wider
+
+ & &__mark-content
+ margin-bottom: 0
+
+ & &__mark-time
+ color: $color-light
+ margin-left: 2px
+
+ & &__owner-link
+
+ & &__rating-star
+ position: relative
+ top: 4px
+
+ & &__empty
+
+// includes reviews of an entity
+// sub section
+.entity-reviews
+ // when used alone
+ &:first-child
+ margin-bottom: $sub-sections-between-margin
+ & &__title
+ display: inline-block
+ margin-bottom: $sub-section-title-margin
+ & > a
+ margin-right: 5px
+ &--stand-alone
+ margin-bottom: $section-title-margin
+ & &__more-link
+ margin-left: 5px
+ & &__review-list
+
+ & &__review
+ margin: 0
+ padding: $mark-review-padding
+ border-bottom: 1px dashed $color-quinary
+ &:last-child
+ border: none
+ &--wider
+ padding: $mark-review-padding-wider
+
+ & &__review-time
+ color: $color-light
+ margin-left: 2px
+ & &__review-title
+
+ & &__owner-link
+
+ & &__empty
+
+.dividing-line
+ height: 0
+ width: 100%
+ margin: 40px 0 24px 0
+ border-top: solid 1px $color-light
+ &.dividing-line--dashed
+ margin: 0
+ margin-top: 10px
+ margin-bottom: 2px
+ border-top: 1px dashed $color-quinary;
+
+// on home page
+.entity-sort
+ position: relative
+ margin-bottom: 30px
+ & &__label
+ font-size: large
+ display: inline-block
+ margin-bottom: $section-title-margin
+
+ & &__more-link
+ margin-left: 8px
+
+ & &__count
+ // font-size: large
+ // letter-spacing: -1px
+ color: $color-tertiary
+ &::before
+ content: '('
+ &::after
+ content: ')'
+
+ & &__entity-list
+ display: flex
+ justify-content: flex-start
+ // padding-left: 24px
+ flex-wrap: wrap
+
+ & &__entity
+ padding: 0 10px
+ flex-basis: 20%
+ text-align: center
+ display: inline-block
+ color: $color-secondary
+ &:hover
+ color: $color-primary
+ & > a
+ color: inherit
+
+ & &__entity-img
+ height: 110px
+
+ & &__entity-name
+ text-overflow: ellipsis
+ overflow: hidden
+ display: -webkit-box
+ -webkit-box-orient: vertical
+ -webkit-line-clamp: 2
+
+ & &__empty
+
+ // for drag and sort purpose
+ &--placeholder
+ border: dashed $color-tertiary 4px
+ &--hover
+ padding: 10px
+ border: dashed $color-primary 2px !important
+ border-radius: 3px
+ &--sortable
+ padding: 10px
+ margin: 10px 0
+ border: dashed $color-tertiary 2px
+ cursor: all-scroll
+ &--hidden
+ opacity: 0.4
+
+.entity-sort-control
+ display: flex
+ justify-content: flex-end
+
+ &__button
+ margin-top: 5px
+ margin-left: 12px
+ padding: 0 2px
+ cursor: pointer
+ color: $color-tertiary
+ &:hover
+ color: $color-primary
+ & > .icon-save svg, & > .icon-edit svg
+ fill: $color-primary
+
+ &--float-right
+ position: absolute
+ top: 4px
+ right: 10px
+
+ &__text
+
+
+// follower/following list page
+.related-user-list
+ & &__title
+ margin-bottom: $section-title-margin
+
+ & &__user
+ display: flex
+ justify-content: flex-start
+ margin-bottom: 20px
+
+ & &__user-info
+ margin-left: 15px
+ overflow: auto
+
+ & &__user-name
+
+ & &__user-bio
+
+ & &__user-avatar
+ max-height: 72px
+ min-width: 72px
+
+// review detail page
+.review-head
+ & &__title
+ display: inline-block
+ font-weight: bold
+
+ & &__body
+ margin-bottom: 10px
+ &::after
+ @include clear
+
+ & &__info
+ float: left
+
+ & &__owner-link
+ color: $color-light
+ &:hover
+ color: $color-primary
+
+ & &__time
+ color: $color-light
+
+ & &__rating-star
+ position: relative
+ top: 3px
+ left: -1px
+
+ & &__actions
+ float: right
+
+ & &__action-link
+ &:not(:first-child)
+ margin-left: 5px
+
+// tag list
+.tag-collection
+ margin-left: -9px
+ & &__tag
+ position: relative;
+ display: block;
+ float: left;
+ color: white;
+ background: $color-light;
+ padding: 5px;
+ // margin: 4px;
+ border-radius: .3rem;
+ line-height: 1.2em;
+ font-size: 80%
+ margin: 3px
+ & a
+ color: white
+ &:hover
+ color: $color-primary
+
+// Small devices (landscape phones, 576px and up)
+@media (max-width: $small-devices)
+ .entity-list
+ & &__entity
+ // flex-direction: column
+ // margin-bottom: 30px
+ & &__entity-text
+ // margin-left: 0
+ & &__entity-img-wrapper
+ // margin-bottom: 8px
+ & &__entity-img
+ max-width: 25vw
+ min-width: 25vw
+ & &__entity-info
+ // max-width: unset
+ white-space: unset
+ & &__rating--empty + &__entity-info
+ // max-width: 70%
+ & &__entity-brief
+ -webkit-line-clamp: 5
+
+ .action-bar
+ display: flex
+ flex-direction: column
+
+ .entity-detail
+ flex-direction: column
+ & &__title
+ margin-bottom: 5px
+ & &__info
+ margin-left: 0
+ float: none
+ display: flex
+ flex-direction: column
+ width: 100%
+ & &__img
+ margin-bottom: 24px
+ float: none
+ height: unset
+ max-width: 170px;
+ & &__fields
+ width: unset
+ margin-left: unset
+ & + .tag-collection
+ margin-left: -3px
+
+ .entity-marks
+ & &__mark-list
+
+ & &__mark
+
+ & &__rating-star
+
+ & &__rating-star + &__mark-time
+ // display: block
+
+ .dividing-line
+ margin-top: 24px
+
+ .entity-sort
+ & &__entity
+ flex-basis: 50%
+ // margin-left: 10px
+ // margin-right: 10px
+ & &__entity-img
+ height: 130px
+
+ .review-head
+ & &__info
+ float: unset
+ & &__actions
+ float: unset
+
+ .track-carousel
+ &__content
+ padding-bottom: 10px
+ &__track
+ min-width: 31%
+ max-width: 31%
+ margin-right: 4.5%
+
+
+// Medium devices (tablets, 768px and up)
+@media (max-width: $medium-devices)
+ .calendar_view
+ overflow-x: scroll
+
+// Large devices (desktops, 992px and up)
+@media (max-width: $large-devices)
+ .main-section-wrapper
+ padding: $main-section-padding-mobile
+
+ .entity-detail
+ display: flex
+ & &__info
+ // display: flex
+// Extra large devices (large desktops, 1200px and up)
+@media (max-width: $x-large-devices)
+ pass
+
+$aside-section-padding: 28px 25px 12px 25px
+$aside-section-padding-mobile: 24px 25px 10px 25px
+
+.aside-section-wrapper
+ display: flex
+ flex: 1
+ flex-direction: column
+ // align-items: center
+ width: 100%
+ padding: $aside-section-padding
+ background-color: $color-bright
+ margin-bottom: 30px
+ overflow: auto
+
+ &--transparent
+ background-color: unset
+ &--collapse
+ padding: unset
+
+.add-entity-entries
+
+ & &__entry
+ margin-bottom: 10px
+
+ & &__label
+ font-size: 1.2em
+ margin-bottom: 8px
+
+ & &__button
+ line-height: unset;
+ height: unset;
+ padding: 4px 15px;
+ margin: 5px;
+
+.action-panel
+ margin-bottom: 20px
+
+ & &__label
+ // font-size: 1.2em
+ font-weight: bold
+ margin-bottom: 12px
+
+ & &__button-group
+ display: flex
+ justify-content: space-between
+ &--center
+ justify-content: center
+
+ & &__button
+ line-height: unset
+ height: unset
+ padding: 4px 15px
+ margin: 0 5px
+ // width: 100%
+
+.mark-panel
+ margin-bottom: 20px
+
+ & &__status
+ font-weight: bold
+
+ & &__rating-star
+ position: relative
+ top: 2px
+
+ & &__actions
+ float: right
+ & form
+ display: inline
+
+ & &__time
+ color: $color-light
+ margin-bottom: 10px
+
+ & &__content
+
+ & &__clear
+ @include clear
+
+.review-panel
+ & &__label
+ font-weight: bold
+ & &__actions
+ float: right
+ & &__time
+ color: $color-light
+ margin-bottom: 10px
+
+ & &__review-title
+ display: block
+ margin-bottom: 15px
+ font-weight: bold
+
+ & &__clear
+ @include clear
+
+.user-profile
+ & &__header
+ display: flex
+ align-items: flex-start
+ margin-bottom: 15px
+
+ & &__avatar
+ width: 72px
+
+ & &__username
+ font-size: large
+ margin-left: 10px
+ margin-bottom: 0
+
+ & &__bio
+
+ & &__report-link
+ color: $color-light
+
+.user-relation
+ & &__label
+ display: inline-block
+ font-size: large
+ margin-bottom: 10px
+
+ & &__more-link
+ margin-left: 5px
+
+ & &__related-user-list
+ display: flex
+ justify-content: flex-start
+ &:last-of-type
+ margin-bottom: 0
+
+ & &__related-user
+ flex-basis: 25%
+ padding: 0px 3px
+ text-align: center
+ display: inline-block
+ overflow: hidden
+ & > a
+ &:hover
+ color: $color-secondary
+
+ & &__related-user-avatar
+ background-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")
+ width: 48px
+ height: 48px
+ @media (min-width: $small-devices) and (max-width: $large-devices)
+ height: unset
+ width: 60%
+ max-width: 96px
+
+
+ & &__related-user-name
+ color: inherit
+ overflow: hidden
+ text-overflow: ellipsis
+ // display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+
+$panel-padding : 0
+
+.report-panel
+ & &__label
+ display: inline-block
+ margin-bottom: 10px
+
+ & &__body
+ padding-left: $panel-padding
+
+ & &__report-list
+
+ & &__report
+ margin: 2px 0
+
+ & &__user-link
+ margin: 0 2px
+
+ & &__all-link
+ margin-left: 5px
+
+.import-panel
+ overflow-x: hidden
+
+ & &__label
+ display: inline-block
+ margin-bottom: 10px
+
+ & &__body
+ padding-left: $panel-padding
+ border: 2px dashed #00a1cc
+ padding: 6px 9px
+ form
+ margin: 0
+
+ @media (max-width: $large-devices)
+ border: unset
+ padding-left: 0
+
+ & &__help
+ background-color: $color-quinary
+ border-radius: 100000px
+ display: inline-block
+ width: 16px
+ height: 16px
+ text-align: center
+ font-size: 12px
+ cursor: help
+
+ & &__checkbox
+ display: inline-block
+ margin-right: 10px
+ label
+ display: inline
+ input[type="checkbox"]
+ margin: 0
+ position: relative
+ top: 2px
+ &--last
+ margin-right: 0
+
+ & &__file-input
+ margin-top: 10px
+
+ & &__button
+ line-height: unset
+ height: unset
+ padding: 4px 15px
+
+ & &__progress
+ padding-top: 10px
+ // padding-top: 4px
+ &:not(:first-child)
+ border-top: $color-tertiary 1px dashed
+ label
+ display: inline
+ progress
+ background-color: $color-quaternary
+ border-radius: 0
+ height: 10px
+ width: 54%
+
+ progress::-webkit-progress-bar
+ background-color: $color-quaternary
+
+ progress::-webkit-progress-value
+ background-color: $color-primary
+
+ progress::-moz-progress-bar
+ background-color: $color-quaternary
+
+ & &__last-task
+ &:not(:first-child)
+ padding-top: 4px
+ border-top: $color-tertiary 1px dashed
+ .index:not(:last-of-type)
+ margin-right: 8px
+
+ & &__fail-urls
+ margin-top: 10px
+ li
+ word-break: break-all
+ ul
+ // padding: 4px
+ max-height: 100px
+ overflow-y: auto
+
+
+.relation-dropdown
+ & &__button
+ display: none
+
+.entity-card
+ display: flex
+ margin-bottom: 10px
+ flex-direction: column
+ &--horizontal
+ flex-direction: row
+
+ & &__img
+ height: 150px
+
+ & &__rating-star
+ position: relative
+ top: 4px
+ left: -3px
+
+ & &__rating-score
+ position: relative
+ top: 1px
+ margin-left: 2px
+
+ & &__title
+ margin-bottom: 10px
+ margin-top: 5px
+
+ & &__info-wrapper
+ &--horizontal
+ margin-left: 20px
+
+ & &__img-wrapper
+ flex-basis: 100px
+
+
+// Small devices (landscape phones, 576px and up)
+@media (max-width: $small-devices)
+ .add-entity-entries
+ display: block !important
+ & &__button
+ width: 100%
+ margin: 5px 0 5px 0;
+
+ .aside-section-wrapper
+ &:first-child
+ margin-right: 0 !important
+ margin-bottom: 0 !important
+ &--singular:first-child
+ margin-bottom: 20px !important
+ // &--no-padding
+ // padding: $aside-section-padding-mobile !important
+ // margin-top: 0 !important
+ .action-panel
+ flex-direction: column !important
+
+ .entity-card
+ &--horizontal
+ flex-direction: column !important
+ // flex-direction: column !important
+ & &__info-wrapper
+ margin-left: 10px !important
+ &--horizontal
+ margin-left: 0 !important
+// Medium devices (tablets, 768px and up)
+@media (max-width: $medium-devices)
+ pass
+// Large devices (desktops, 992px and up)
+@media (max-width: $large-devices)
+ .add-entity-entries
+ display: flex
+ justify-content: space-around
+
+ .aside-section-wrapper
+ padding: $aside-section-padding-mobile
+
+ margin-top: 20px
+ // &:not(:first-child)
+ &:not(:last-child)
+ margin-right: 20px
+ &--collapse
+ padding: $aside-section-padding-mobile !important
+ margin-top: 0
+ margin-bottom: 0
+ &:first-child
+ margin-right: 0
+ &--no-margin
+ margin: 0
+
+ .action-panel
+ flex-direction: row
+ & &__button-group
+ justify-content: space-evenly
+
+ .relation-dropdown
+ margin-bottom: 20px
+ & &__button
+ padding-bottom: 10px
+ background-color: $color-bright
+ width: 100%
+ display: flex
+ justify-content: center
+ align-items: center
+ cursor: pointer
+ transition: transform 0.3s
+ &:focus
+ background-color: red
+
+ & &__button > .icon-arrow
+ transition: transform 0.3s
+
+ & &__button:hover > .icon-arrow > svg
+ fill: $color-primary
+
+ & &__button > .icon-arrow--expand
+ transform: rotate(-180deg)
+
+ & &__body
+ background-color: $color-bright
+ max-height: 0
+ transition: max-height 1s ease-out
+ overflow: hidden
+
+ & &__body--expand
+ max-height: 2000px
+ transition: max-height 1s ease-in
+
+ .entity-card
+ flex-direction: row
+ & &__info-wrapper
+ margin-left: 30px
+
+.entity-list ul
+ padding: 0
+ list-style: none
+
+.pagination
+ // position: absolute
+ // bottom: 30px
+ // left: 50%
+ // transform: translateX(-50%)
+ text-align: center
+ width: 100%
+
+ & &__page-link
+ font-weight: normal
+ margin: 0 5px
+
+ &--current
+ font-weight: bold
+ font-size: 1.2em
+ // text-decoration: underline
+ color: $color-secondary
+
+ & &__nav-link
+ font-size: 1.4em
+ margin: 0 2px
+
+ $nav-link-edge-margin-width: 18px
+
+ &--right-margin
+ margin-right: $nav-link-edge-margin-width
+
+ &--left-margin
+ margin-left: $nav-link-edge-margin-width
+
+ &--hidden
+ display: none
+
+
+// Small devices (landscape phones, 576px and up)
+@media (max-width: $small-devices)
+ .pagination
+ & &__page-link
+ margin: 0 3px
+
+ & &__nav-link
+ font-size: 1.4em
+ margin: 0 2px
+
+ $nav-link-edge-margin-width: 10px
+
+ &--right-margin
+ margin-right: $nav-link-edge-margin-width
+
+ &--left-margin
+ margin-left: $nav-link-edge-margin-width
+// Medium devices (tablets, 768px and up)
+@media (max-width: $medium-devices)
+ pass
+// Large devices (desktops, 992px and up)
+@media (max-width: $large-devices)
+ pass
+// Extra large devices (large desktops, 1200px and up)
+@media (max-width: $x-large-devices)
+ pass
+
+img.emoji
+ height: 14px
+ box-sizing: border-box
+ object-fit: contain
+ position: relative
+ top: 3px
+
+ &--large
+ height: 20px
+ position: relative
+ top: 2px
diff --git a/common/static/scss/_legacy2.scss b/common/static/scss/_legacy2.scss
new file mode 100644
index 00000000..7eedb997
--- /dev/null
+++ b/common/static/scss/_legacy2.scss
@@ -0,0 +1,191 @@
+/*** django-jsoneditor ***/
+div.jsoneditor {
+ border-color: #ccc !important;
+}
+
+div.jsoneditor-menu {
+ background-color: #606c76 !important;
+ border-color: #606c76 !important;
+}
+
+/***** MODAL DIALOG ****/
+#modal {
+ /* Underlay covers entire screen. */
+ position: fixed;
+ top: 0px;
+ bottom: 0px;
+ left: 0px;
+ right: 0px;
+ background-color: rgba(0, 0, 0, 0.5);
+ z-index: 1000;
+
+ /* Flexbox centers the .modal-content vertically and horizontally */
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+
+ /* Animate when opening */
+ animation-name: fadeIn;
+ animation-duration: 150ms;
+ animation-timing-function: ease;
+}
+
+#modal>.modal-underlay {
+ /* underlay takes up the entire viewport. This is only
+ required if you want to click to dismiss the popup */
+ position: absolute;
+ z-index: -1;
+}
+
+#modal>.modal-content {
+ /* Position visible dialog near the top of the window */
+ margin-top: 10vh;
+
+ /* Sizing for visible dialog */
+ width: 80%;
+ max-width: 600px;
+
+ /* Display properties for visible dialog*/
+ background-color: #f7f7f7;
+ padding: 20px 20px 10px 20px;
+ color: #606c76;
+
+ /* Animate when opening */
+ animation-name: zoomIn;
+ animation-duration: 150ms;
+ animation-timing-function: ease;
+}
+
+#modal.closing {
+ /* Animate when closing */
+ animation-name: fadeOut;
+ animation-duration: 150ms;
+ animation-timing-function: ease;
+}
+
+#modal.closing>.modal-content {
+ /* Aniate when closing */
+ animation-name: zoomOut;
+ animation-duration: 150ms;
+ animation-timing-function: ease;
+}
+
+@keyframes fadeIn {
+ 0% {
+ opacity: 0;
+ }
+
+ 100% {
+ opacity: 1;
+ }
+}
+
+@keyframes fadeOut {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+}
+
+@keyframes zoomIn {
+ 0% {
+ transform: scale(0.9);
+ }
+
+ 100% {
+ transform: scale(1);
+ }
+}
+
+@keyframes zoomOut {
+ 0% {
+ transform: scale(1);
+ }
+
+ 100% {
+ transform: scale(0.9);
+ }
+}
+
+#modal .add-to-list-modal__head {
+ margin-bottom: 20px;
+}
+
+#modal .add-to-list-modal__head::after {
+ content: ' ';
+ clear: both;
+ display: table;
+}
+
+#modal .add-to-list-modal__title {
+ font-weight: bold;
+ font-size: 1.2em;
+ float: left;
+}
+
+#modal .add-to-list-modal__close-button {
+ float: right;
+ cursor: pointer;
+}
+
+#modal .add-to-list-modal__confirm-button {
+ float: right;
+}
+
+#modal li,
+#modal ul,
+#modal label {
+ display: inline;
+}
+
+.donut {
+ margin-left: 10%;
+ width: 80%;
+ aspect-ratio: 1 / 1;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.donut .hole {
+ width: 80%;
+ aspect-ratio: 1 / 1;
+ border-radius: 50%;
+ background: #f7f7f7;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+input:invalid#position {
+ border: red dashed 1px;
+}
+
+.gg-play-button-o {
+ box-sizing: border-box;
+ position: relative;
+ display: inline-block;
+ transform: scale(var(--ggs, 1));
+ width: 22px;
+ height: 22px;
+ border: 2px solid;
+ border-radius: 20px
+}
+
+.gg-play-button-o::before {
+ content: "";
+ display: inline-block;
+ box-sizing: border-box;
+ position: absolute;
+ width: 0;
+ height: 10px;
+ border-top: 5px solid transparent;
+ border-bottom: 5px solid transparent;
+ border-left: 6px solid;
+ top: 4px;
+ left: 7px
+}
diff --git a/common/static/scss/_login.scss b/common/static/scss/_login.scss
new file mode 100644
index 00000000..fbc82c0f
--- /dev/null
+++ b/common/static/scss/_login.scss
@@ -0,0 +1,72 @@
+.login-page {
+ input:invalid {
+ border: var(--pico-primary) dashed 2px;
+ }
+
+ mark {
+ padding: 0;
+ }
+
+ .autoComplete_wrapper>ul {
+ padding: 0;
+ }
+
+ .autoComplete_wrapper>ul li {
+ list-style: none;
+ padding: 0 var(--pico-spacing);
+ }
+
+ .autoComplete_wrapper>ul>li:hover {
+ cursor: pointer;
+ background-color: var(--pico-text-selection-color);
+ }
+
+ .autoComplete_wrapper>ul>li[aria-selected="true"] {
+ background-color: var(--pico-text-selection-color);
+ }
+
+ article {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ min-width: max-content;
+ width: 50%;
+ height: max-content;
+
+ p {
+ max-width: 50vw;
+ }
+ }
+
+ header img {
+ padding: 10%;
+ }
+
+ html,
+ body {
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ @media (max-width: 768px) {
+ article {
+ width: 96%;
+
+ p {
+ max-width: 96vw;
+ }
+ }
+ }
+
+ body>footer {
+ color: lightgrey;
+ text-align: center;
+ bottom: 0px;
+ position: fixed;
+ width: 100%;
+ padding: 0;
+ }
+}
diff --git a/common/static/scss/_mark.scss b/common/static/scss/_mark.scss
new file mode 100644
index 00000000..9f12e3c2
--- /dev/null
+++ b/common/static/scss/_mark.scss
@@ -0,0 +1,68 @@
+#mark_date {
+ width: unset !important;
+}
+
+.tag-input {
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-pack: start;
+ -ms-flex-pack: start;
+ justify-content: flex-start;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ border-bottom: 0.1rem solid #ccc;
+ padding-bottom: 2px;
+}
+
+.tag-input__tag {
+ position: relative;
+ display: block;
+ float: left;
+ color: white;
+ background: #d5d5d5;
+ padding: 5px 20px 5px 6px;
+ margin: 4px;
+ border-radius: .4rem;
+ line-height: 1em;
+ -webkit-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
+ height: min-content;
+}
+
+.tag-input__tag.tag-input__tag--highlight {
+ color: white;
+ background: #00a1cc;
+}
+
+.tag-input__close {
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 14px;
+ height: 100%;
+ cursor: pointer;
+ border-radius: 0 2px 2px 0;
+ -webkit-transition: background 0.2s;
+ transition: background 0.2s;
+}
+
+.tag-input__close:after {
+ position: absolute;
+ content: "×";
+ top: 5px;
+ left: 2px;
+}
+
+.tag-input__close:hover {
+ color: #606c76;
+}
+
+.tag-input input {
+ border: 0;
+ margin: 4px;
+ padding: 3px 7px 3px 0;
+ width: auto;
+ outline: none;
+ height: unset;
+}
diff --git a/common/static/scss/_markdown.scss b/common/static/scss/_markdown.scss
new file mode 100644
index 00000000..455b43d5
--- /dev/null
+++ b/common/static/scss/_markdown.scss
@@ -0,0 +1,31 @@
+.spoiler {
+ background: grey;
+ filter: blur(2px);
+ cursor: pointer;
+
+ &.revealed {
+ background: unset;
+ filter: unset;
+ color: inherit;
+ }
+}
+
+.markdown-content {
+
+ h1 {
+ font-size: 1.5rem
+ }
+
+ h2 {
+ font-size: 1.25rem
+ }
+
+ h3 {
+ font-size: 1.1rem;
+ }
+}
+
+
+.markdownx-editor {
+ font-family: monospace;
+}
diff --git a/common/static/scss/_rating.scss b/common/static/scss/_rating.scss
new file mode 100644
index 00000000..e8f3cbc5
--- /dev/null
+++ b/common/static/scss/_rating.scss
@@ -0,0 +1,133 @@
+.rating {
+ margin-top: 2vh;
+ margin-bottom: 2vh;
+ position: relative;
+}
+
+.rating * {
+ margin: 0;
+ padding: 0;
+}
+
+.rating h3 small {
+ font-weight: 200;
+}
+
+.rating .display {
+ display: flex;
+ flex: row;
+ vertical-align: bottom;
+}
+
+.rating .display div {
+ align-items: center;
+ justify-content: center;
+ display: flex;
+}
+
+.rating .display div:nth-child(1) {
+ width: max-content;
+ padding-right: 12px;
+}
+
+.rating .display div:nth-child(2) {
+ flex-grow: 100;
+}
+
+.rating .chart {
+ flex-grow: 100;
+ display: table;
+ table-layout: fixed;
+ height: max-content;
+}
+
+.rating .chart li {
+ position: relative;
+ display: table-cell;
+ vertical-align: bottom;
+ height: 6vh;
+}
+
+.rating .chart span {
+ margin: 2%;
+ display: block;
+ background: var(--pico-muted-color);
+ border-radius: 0.5vh 0.5vh 0 0;
+}
+
+.rating .undisplay {
+ position: absolute;
+ display: none;
+ align-items: center;
+ justify-content: center;
+ inset: 0;
+}
+
+.rating.unavailable .undisplay {
+ display: flex;
+}
+
+.rating.unavailable .display {
+ filter: blur(0.5vh);
+}
+
+.rating.unavailable .chart li:nth-child(1) span {
+ height: 10% !important;
+}
+
+.rating.unavailable .chart li:nth-child(2) span {
+ height: 20% !important;
+}
+
+.rating.unavailable .chart li:nth-child(3) span {
+ height: 30% !important;
+}
+
+.rating.unavailable .chart li:nth-child(4) span {
+ height: 40% !important;
+}
+
+.rating.unavailable .chart li:nth-child(5) span {
+ height: 50% !important;
+}
+
+// Rating Star with Font Awesome
+.rating-star {
+ position: relative;
+ vertical-align: middle;
+ font-family: FontAwesome;
+ display: inline-block;
+}
+
+.rating-star:before {
+ content: "\f006 \f006 \f006 \f006 \f006";
+ text-underline-offset: 0.125em;
+ line-height: 1;
+ vertical-align: middle;
+}
+
+.rating-star>div {
+ position: absolute;
+ left: 0;
+ top: 0;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+.rating-star>div:before {
+ content: "\f005 \f005 \f005 \f005 \f005";
+ text-underline-offset: 0.125em;
+ line-height: 1;
+ vertical-align: middle;
+}
+
+.rating-star .yellow {
+ color: #F68127;
+ text-shadow: 0 0 1px #FFE205, 0 0 2px #FFE205, 0 0 6px #EDD205;
+ // color: #FFE205;
+}
+
+.rating-editor {
+ cursor: pointer !important;
+ border: none !important;
+}
diff --git a/common/static/scss/_sidebar.scss b/common/static/scss/_sidebar.scss
new file mode 100644
index 00000000..beb1cc05
--- /dev/null
+++ b/common/static/scss/_sidebar.scss
@@ -0,0 +1,84 @@
+.sidebar {
+ article {
+ padding: calc(var(--pico-block-spacing-horizontal));
+ }
+
+ details {
+ margin-bottom: 0;
+ }
+
+ hgroup {
+ margin: 0;
+ margin-right: 1rem;
+ }
+
+ section.announcement {
+ p {
+ font-size: 80%;
+ }
+
+ // details[open]>summary {
+ // margin: 0;
+ // }
+
+ details {
+ margin-bottom: var(--pico-spacing);
+ }
+ }
+
+ section.profile {
+ summary>div {
+ display: grid;
+ grid-template-columns: auto 1fr;
+ grid-template-rows: auto;
+ grid-column-gap: 0.5em;
+ grid-row-gap: 0px;
+ // align-items: center;
+ // justify-content: left;
+ }
+
+ summary::after {
+ position: relative;
+ bottom: calc(2rem * var(--pico-line-height));
+ }
+
+ a:hover {
+ text-decoration: none;
+ }
+
+ .avatar {
+ height: calc(1rem * var(--pico-line-height) + var(--pico-nav-link-spacing-vertical) * 4);
+ width: calc(1rem * var(--pico-line-height) + var(--pico-nav-link-spacing-vertical) * 4);
+
+ img {
+ height: 100%;
+ width: 100%;
+ }
+ }
+
+ .nickname {
+ word-break: break-all;
+ }
+
+
+ .relation {
+ width: max-content;
+ float: right;
+ color: var(--pico-muted-color);
+ opacity: 0.5;
+ margin-left: 1em;
+ }
+
+
+ }
+
+
+ @media (max-width: 768px) {
+
+ section,
+ article,
+ details {
+ margin-bottom: 0;
+ }
+ }
+}
diff --git a/common/static/scss/_sitelabel.scss b/common/static/scss/_sitelabel.scss
new file mode 100644
index 00000000..b7599f3a
--- /dev/null
+++ b/common/static/scss/_sitelabel.scss
@@ -0,0 +1,115 @@
+.site-list {
+ a {
+ display: inline;
+ background: transparent;
+ padding: calc(var(--pico-spacing)/8);
+ border-radius: calc(var(--pico-spacing)/4);
+ border-style: solid;
+ border-width: 1px;
+ font-size: 80%;
+ margin: 3px;
+ // padding: 1px 3px;
+ // padding-top: 2px;
+ font-weight: lighter;
+ word-break: keep-all;
+ opacity: 1;
+ font-weight: 400;
+ text-decoration: none;
+ white-space: nowrap;
+ }
+
+ .douban {
+ border: none;
+ color: white;
+ background-color: #319840;
+ }
+
+ .spotify {
+ background-color: #1ed760;
+ color: black;
+ border: none;
+ font-weight: bold;
+ }
+
+ .imdb {
+ background-color: #F5C518;
+ color: #121212;
+ border: none;
+ font-weight: bold;
+ }
+
+ .igdb {
+ background-color: #323A44;
+ color: #DFE1E2;
+ border: none;
+ font-weight: bold;
+ }
+
+ .steam {
+ background: linear-gradient(30deg, #1387b8, #111d2e);
+ color: white;
+ border: none;
+ // font-weight: 600
+ // padding-top: 2px
+ }
+
+ .bangumi {
+ background: #F09199;
+ color: #FCFCFC;
+ font-weight: 600;
+ }
+
+ .goodreads {
+ background: #F4F1EA;
+ color: #372213;
+ font-weight: lighter;
+ }
+
+ .bookstw {
+ background: #7FBA19;
+ color: white;
+ font-weight: lighter;
+ }
+
+ .tmdb {
+ background: linear-gradient(90deg, #91CCA3, #1FB4E2);
+ color: white;
+ border: none;
+ font-weight: lighter;
+ padding-top: 2px;
+ }
+
+ .googlebooks {
+ color: white;
+ background-color: #4285F4;
+ border-color: #4285F4;
+ }
+
+ .rss {
+ color: white;
+ background-color: #E1A02F;
+ border-color: #E1A02F;
+ }
+
+ .bandcamp {
+ color: white;
+ background-color: #28A0C1;
+ }
+
+ // .bandcamp {
+ // color: white
+ // background-color: #28A0C1
+ // // transform: skewX(-30deg)
+ // display: inline-block
+ // }
+ // .bandcamp span
+ // // transform: skewX(30deg)
+ // display: inline-block
+ // margin: 0 4px
+
+ .discogs {
+ color: white;
+ background-color: #262626;
+ border-color: #262626;
+ }
+}
diff --git a/common/static/scss/_tag.scss b/common/static/scss/_tag.scss
new file mode 100644
index 00000000..0c2c13e1
--- /dev/null
+++ b/common/static/scss/_tag.scss
@@ -0,0 +1,20 @@
+.tag-list {
+ // margin-bottom: var(--pico-spacing);
+
+ span {
+ // margin: 3px;
+
+ a {
+ color: white;
+ background: var(--pico-muted-color);
+ padding: calc(var(--pico-spacing)/4);
+ border-radius: calc(var(--pico-spacing)/4);
+ line-height: 1.2em;
+ font-size: 80%;
+ margin: 3px !important;
+ text-decoration: none;
+ white-space: nowrap;
+ display: inline-block;
+ }
+ }
+}
diff --git a/common/static/scss/neodb.scss b/common/static/scss/neodb.scss
new file mode 100644
index 00000000..3af02899
--- /dev/null
+++ b/common/static/scss/neodb.scss
@@ -0,0 +1,18 @@
+@import '_header.scss';
+@import '_dialog.scss';
+@import '_rating.scss';
+@import '_mark.scss';
+@import '_item.scss';
+@import '_layout.scss';
+@import '_sitelabel.scss';
+@import '_tag.scss';
+@import '_markdown.scss';
+@import '_legacy.sass';
+@import '_legacy2.scss';
+@import '_collection.scss';
+@import '_feed.scss';
+@import '_card.scss';
+@import '_gallery.scss';
+@import '_sidebar.scss';
+@import '_common.scss';
+@import '_login.scss';
diff --git a/common/templates/400.html b/common/templates/400.html
index 6c5047e6..3996b689 100644
--- a/common/templates/400.html
+++ b/common/templates/400.html
@@ -8,33 +8,23 @@
{% load thumb %}
-
-
-
-
- {{ site_name }} - {% trans '无效请求' %}
- {% include "common_libs.html" with jquery=0 %}
-
-
-
-
-
-
- {% include "partial/_navbar.html" %}
-
-
-
-
-
- 无效的请求
-
-
- {{ exception }}
- 您可能提交了无效的数据,或者相关内容已被作者删除。如果您确信这是我们的错误,欢迎通过页面底部的链接联系。
-
-
-
+
+
+
+
{{ site_name }} - {% trans '无效请求' %}
+ {% include "common_libs.html" with jquery=0 v2=1 %}
+
+
+ {% include "_header.html" %}
+
+
+
+ {{ exception }}
+ 您可能提交了无效的数据,或者相关内容已被作者删除。如果您确信这是我们的错误,请通过页面底部的链接联系我们。
+
+
{% include "partial/_footer.html" %}
-
-
+
diff --git a/common/templates/403.html b/common/templates/403.html
index 7a18a32b..b3633a8c 100644
--- a/common/templates/403.html
+++ b/common/templates/403.html
@@ -8,32 +8,23 @@
{% load thumb %}
-
-
-
-
- {{ site_name }} - {% trans '权限不符' %}
- {% include "common_libs.html" with jquery=0 %}
-
-
-
-
-
-
- {% include "partial/_navbar.html" %}
-
-
-
-
-
- 权限不符
-
-
- 您可能访问了错误的网址,或者相关内容已被作者隐藏。如果您确信这是我们的错误,欢迎通过页面底部的链接联系。
-
-
-
+
+
+
+
{{ site_name }} - {% trans '权限不符' %}
+ {% include "common_libs.html" with jquery=0 v2=1 %}
+
+
+ {% include "_header.html" %}
+
+
+
+ {{ exception }}
+ 您可能访问了错误的网址,或者相关内容已被作者删除。如果您确信这是我们的错误,请通过页面底部的链接联系我们。
+
+
{% include "partial/_footer.html" %}
-
-
+
diff --git a/common/templates/404.html b/common/templates/404.html
index 46d3ea92..e1bb5901 100644
--- a/common/templates/404.html
+++ b/common/templates/404.html
@@ -8,32 +8,22 @@
{% load thumb %}
-
-
-
-
- {{ site_name }} - {% trans '未找到' %}
- {% include "common_libs.html" with jquery=0 %}
-
-
-
-
-
-
- {% include "partial/_navbar.html" %}
-
-
-
-
-
- 请求条目未找到
-
-
- 您可能输入了一个无效的网址,或者相关内容已被作者删除。如果您确信这是我们的错误,欢迎通过页面底部的链接联系。
-
-
-
+
+
+
+
{{ site_name }} - {% trans '未找到' %}
+ {% include "common_libs.html" with jquery=0 v2=1 %}
+
+
+ {% include "_header.html" %}
+
+
+
+ 您可能输入了一个无效的网址,或者相关内容已被作者删除。如果您确信这是我们的错误,请通过页面底部的链接联系我们。
+
+
{% include "partial/_footer.html" %}
-
-
+
diff --git a/common/templates/500.html b/common/templates/500.html
index 7213d7de..41d63a12 100644
--- a/common/templates/500.html
+++ b/common/templates/500.html
@@ -8,32 +8,22 @@
{% load thumb %}
-
-
-
-
- {{ site_name }} - {% trans '系统错误' %}
- {% include "common_libs.html" with jquery=0 %}
-
-
-
-
-
-
- {% include "partial/_navbar.html" %}
-
-
-
-
-
- 系统内部错误
-
-
- 发生了一个内部错误,如果这个错误多次出现,后台会记录并会由人工处理。如果您有紧急情况或疑问,欢迎通过页面底部的链接联系。
-
-
-
+
+
+
+
{{ site_name }} - {% trans '系统错误' %}
+ {% include "common_libs.html" with jquery=0 v2=1 %}
+
+
+ {% include "_header.html" %}
+
+
+
+ 发生了一个内部错误,如果这个错误多次出现,后台会记录并会由人工处理。如果您有紧急情况或任何疑问,请通过页面底部的链接联系我们。
+
+
{% include "partial/_footer.html" %}
-
-
+
diff --git a/common/templates/503.html b/common/templates/503.html
index 069b11f4..302115a6 100644
--- a/common/templates/503.html
+++ b/common/templates/503.html
@@ -1,22 +1,25 @@
-
-
-
-
-☃ NeoDB 升级中...
-
-
-
-
-
-
-
\ No newline at end of file
+
+
+
+
diff --git a/common/templates/_header.html b/common/templates/_header.html
new file mode 100644
index 00000000..282a5eca
--- /dev/null
+++ b/common/templates/_header.html
@@ -0,0 +1,103 @@
+{% load admin_url %}
+{% load static %}
+{% load i18n %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% if request.user.is_authenticated %}
+
+ 个人主页
+
+
+ 数据
+
+
+ 设置
+
+
+ 登出
+
+ {% if request.user.is_superuser %}
+
+ 后台
+
+ {% endif %}
+ {% else %}
+
+ 登录
+
+ {% endif %}
+
+
+
+
+
+
+{% if messages %}
+
+
+ {% for message in messages %}
+ {{ message }}
+ {% endfor %}
+
+
+{% endif %}
diff --git a/common/templates/_sidebar.html b/common/templates/_sidebar.html
new file mode 100644
index 00000000..e7c62461
--- /dev/null
+++ b/common/templates/_sidebar.html
@@ -0,0 +1,215 @@
+{% load static %}
+{% load i18n %}
+{% load admin_url %}
+{% load mastodon %}
+{% load oauth_token %}
+{% load truncate %}
+{% load thumb %}
+{% load collection %}
+{% load bleach_tags %}
+
+
diff --git a/common/templates/api_doc.html b/common/templates/api_doc.html
index 301a1656..885fc004 100644
--- a/common/templates/api_doc.html
+++ b/common/templates/api_doc.html
@@ -1,24 +1,28 @@
-
-
-
-{{ api.title }} API Documentation
-{% include "common_libs.html" with jquery=0 %}
-
-
-
-
- {% include "partial/_navbar.html" %}
-
-
-
-
-
-
+
-
-
+
+
diff --git a/common/templates/common/error.html b/common/templates/common/error.html
index 7e29db79..fec60e03 100644
--- a/common/templates/common/error.html
+++ b/common/templates/common/error.html
@@ -1,32 +1,29 @@
-{% load i18n %}
{% load static %}
+{% load i18n %}
+{% load l10n %}
+{% load admin_url %}
+{% load mastodon %}
+{% load oauth_token %}
+{% load truncate %}
+{% load thumb %}
-
+
-
-
-
-
- {% trans '错误' %}
-
-
-
-
-
-
-
-
- {{ msg }}
-
-
- {% if secondary_msg %}
- {{ secondary_msg }}
- {% endif %}
-
-
-
-
-
-
\ No newline at end of file
+ {{ site_name }} - {% trans '错误' %}
+ {% include "common_libs.html" with jquery=0 v2=1 %}
+
+
+ {% include "_header.html" %}
+
+
+
+ {{ secondary_msg|default:"" }}
+
+
+ {% include "partial/_footer.html" %}
+
+