diff --git a/tildes/scss/modules/_topic.scss b/tildes/scss/modules/_topic.scss
index 0956c66..a87e2f0 100644
--- a/tildes/scss/modules/_topic.scss
+++ b/tildes/scss/modules/_topic.scss
@@ -81,6 +81,13 @@
white-space: nowrap;
}
+// This puts a bullet in front of the topic-content-type only when there's something
+// else in front of it (group name and/or tags)
+.topic-content-type:not(:first-child)::before {
+ content: "•";
+ margin-right: 0.4rem;
+}
+
.topic-group {
margin-right: 0.4rem;
}
diff --git a/tildes/tildes/enums.py b/tildes/tildes/enums.py
index 9b53fe0..e9dcef2 100644
--- a/tildes/tildes/enums.py
+++ b/tildes/tildes/enums.py
@@ -115,6 +115,27 @@ class ScraperType(enum.Enum):
YOUTUBE = enum.auto()
+class TopicContentType(enum.Enum):
+ """Enum for the different types of content for topics."""
+
+ ARTICLE = enum.auto()
+ ASK = enum.auto()
+ IMAGE = enum.auto()
+ PDF = enum.auto()
+ TEXT = enum.auto()
+ TWEET = enum.auto()
+ VIDEO = enum.auto()
+
+ @property
+ def display_name(self) -> str:
+ """Return the content type's name in a format more suitable for display."""
+
+ if self.name == "PDF":
+ return self.name
+
+ return self.name.capitalize()
+
+
class TopicSortOption(enum.Enum):
"""Enum for the different methods topics can be sorted by.
diff --git a/tildes/tildes/models/topic/topic.py b/tildes/tildes/models/topic/topic.py
index 1eeb933..ab19b97 100644
--- a/tildes/tildes/models/topic/topic.py
+++ b/tildes/tildes/models/topic/topic.py
@@ -5,7 +5,9 @@
from datetime import datetime, timedelta
from itertools import chain
+from pathlib import PurePosixPath
from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple, TYPE_CHECKING
+from urllib.parse import urlparse
from pyramid.security import Allow, Authenticated, Deny, DENY_ALL, Everyone
from sqlalchemy import (
@@ -24,7 +26,7 @@ from sqlalchemy.orm import deferred, relationship
from sqlalchemy.sql.expression import text
from titlecase import titlecase
-from tildes.enums import TopicType
+from tildes.enums import TopicContentType, TopicType
from tildes.lib.database import TagList
from tildes.lib.datetime import utc_from_timestamp, utc_now
from tildes.lib.id import id_to_id36
@@ -394,7 +396,46 @@ class Topic(DatabaseModel):
@property
def is_spoiler(self) -> bool:
"""Return whether the topic is marked as a spoiler."""
- return "spoiler" in self.tags
+ return self.has_tag("spoiler")
+
+ def has_tag(self, check_tag: str) -> bool:
+ """Return whether the topic has a tag or any sub-tag of it."""
+ if check_tag in self.tags:
+ return True
+
+ if any(tag.startswith(f"{check_tag}.") for tag in self.tags):
+ return True
+
+ return False
+
+ @property
+ def content_type(self) -> TopicContentType:
+ """Return the content's type based on the topic's attributes."""
+ if self.is_text_type:
+ if self.has_tag("ask"):
+ return TopicContentType.ASK
+
+ return TopicContentType.TEXT
+ else:
+ parsed_url = urlparse(self.link) # type: ignore
+ url_path = PurePosixPath(parsed_url.path)
+
+ if url_path.suffix.lower() == ".pdf":
+ return TopicContentType.PDF
+ elif url_path.suffix.lower() in (".gif", ".jpeg", ".jpg", ".png"):
+ return TopicContentType.IMAGE
+
+ # individual sites should be handled in a more general manner; fine for now
+ if self.link_domain == "youtube.com" and parsed_url.path == "/watch":
+ return TopicContentType.VIDEO
+
+ try:
+ if self.link_domain == "twitter.com" and url_path.parts[2] == "status":
+ return TopicContentType.TWEET
+ except IndexError:
+ pass
+
+ return TopicContentType.ARTICLE
def get_content_metadata(self, key: str) -> Any:
"""Get a piece of content metadata "safely".
diff --git a/tildes/tildes/templates/macros/topics.jinja2 b/tildes/tildes/templates/macros/topics.jinja2
index b05b95d..90cc985 100644
--- a/tildes/tildes/templates/macros/topics.jinja2
+++ b/tildes/tildes/templates/macros/topics.jinja2
@@ -46,8 +46,10 @@
{% endif %}
+ {{ topic.content_type.display_name }}
+
{% if topic.content_metadata_for_display %}
- {{ topic.content_metadata_for_display }}
+ : {{ topic.content_metadata_for_display }}
{% endif %}
{% if topic.unimportant_tags and request.user and request.user.show_tags_in_listings %}