pin a hashtag
This commit is contained in:
parent
a20779ce31
commit
432b435083
13 changed files with 313 additions and 154 deletions
|
@ -78,11 +78,11 @@ dialog {
|
|||
.grid>div:nth-child(2) fieldset {
|
||||
float: unset;
|
||||
}
|
||||
.grid {
|
||||
grid-row-gap: 0;
|
||||
}
|
||||
}
|
||||
|
||||
fieldset {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
article {
|
||||
padding-bottom: 1em;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
h1, h2, h3, h4, h5, h6 {
|
||||
h1, h2, h3, h4, h5 {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
<img src="{{ identity.avatar|relative_uri }}" alt="">
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<div style="align-content:center;">
|
||||
<hgroup>
|
||||
<h6 class="nickname">{{ identity.display_name }}</h6>
|
||||
<div>
|
||||
|
@ -68,9 +68,10 @@
|
|||
{% include 'users/profile_actions.html' %}
|
||||
{% endif %}
|
||||
</span>
|
||||
<p>
|
||||
{{ identity.summary|bleach:"a,p,span,br"|default:"<br>" }}
|
||||
</p>
|
||||
<div>
|
||||
{{ identity.summary|bleach:"a,p,span,br"|default:"" }}
|
||||
<br>
|
||||
</div>
|
||||
</details>
|
||||
</article>
|
||||
</section>
|
||||
|
|
17
journal/migrations/0025_pin_tags.py
Normal file
17
journal/migrations/0025_pin_tags.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 4.2.13 on 2024-06-04 19:29
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("journal", "0024_i18n"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="tag",
|
||||
name="pinned",
|
||||
field=models.BooleanField(default=False, null=True),
|
||||
),
|
||||
]
|
|
@ -47,8 +47,7 @@ class Tag(List):
|
|||
title = models.CharField(
|
||||
max_length=100, null=False, blank=False, validators=TagValidators
|
||||
)
|
||||
# TODO case convert and space removal on save
|
||||
# TODO check on save
|
||||
pinned = models.BooleanField(default=False, null=True)
|
||||
|
||||
class Meta:
|
||||
unique_together = [["owner", "title"]]
|
||||
|
@ -61,7 +60,27 @@ class Tag(List):
|
|||
@staticmethod
|
||||
def deep_cleanup_title(title):
|
||||
"""Remove all non-word characters, only for public index purpose"""
|
||||
return re.sub(r"\W+", " ", title).rstrip().lstrip("# ").lower() or "_"
|
||||
return re.sub(r"\W+", " ", title).rstrip().lstrip("# ").lower()[:100] or "_"
|
||||
|
||||
def update(
|
||||
self, title: str, visibility: int | None = None, pinned: bool | None = None
|
||||
):
|
||||
old_title = Tag.deep_cleanup_title(self.title)
|
||||
new_title = Tag.deep_cleanup_title(title)
|
||||
was_pinned = bool(self.pinned)
|
||||
if visibility is not None:
|
||||
self.visibility = 2 if visibility else 0
|
||||
if pinned is not None:
|
||||
self.pinned = pinned
|
||||
self.title = title
|
||||
self.save()
|
||||
if was_pinned != self.pinned or (old_title != new_title and self.pinned):
|
||||
from takahe.utils import Takahe
|
||||
|
||||
if was_pinned:
|
||||
Takahe.unpin_hashtag_for_user(self.owner.pk, old_title)
|
||||
if self.pinned:
|
||||
Takahe.pin_hashtag_for_user(self.owner.pk, new_title)
|
||||
|
||||
|
||||
class TagManager:
|
||||
|
|
|
@ -4,72 +4,57 @@
|
|||
{% load humanize %}
|
||||
{% load mastodon %}
|
||||
{% load thumb %}
|
||||
<div id="modal"
|
||||
_="on closeModal add .closing then wait for animationend then remove me">
|
||||
<div class="modal-underlay" _="on click trigger closeModal"></div>
|
||||
<div class="modal-content">
|
||||
<div class="add-to-list-modal__head">
|
||||
<span class="add-to-list-modal__title">{% trans 'Tag' %} - {{ item.title }} - {% trans 'Edit' %}</span>
|
||||
<span class="add-to-list-modal__close-button modal-close"
|
||||
_="on click trigger closeModal">
|
||||
<i class="fa-solid fa-xmark"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="add-to-list-modal__body">
|
||||
<dialog open
|
||||
class="tag-editor"
|
||||
_="on close_dialog add .closing then wait for animationend then remove me">
|
||||
<article>
|
||||
<header>
|
||||
<link to="#"
|
||||
aria-label="Close"
|
||||
class="close"
|
||||
_="on click trigger close_dialog" />
|
||||
<strong>{% trans 'Edit' %} {{ tag.title }}</strong>
|
||||
</header>
|
||||
<div>
|
||||
<form action="{% url 'journal:user_tag_edit' %}?tag={{ tag.title }}"
|
||||
method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="id" value="{{ tag.id }}">
|
||||
<div class="mark-modal__tag">
|
||||
<div class="tag-input">
|
||||
<input name="title" type="text" value="{{ tag.title }}" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mark-modal__option">
|
||||
<div class="mark-modal__visibility-radio">
|
||||
<span>{% trans "Visibility" %}
|
||||
<ul id="id_visibility">
|
||||
<li>
|
||||
<label for="id_visibility_0">
|
||||
<fieldset>
|
||||
<label for="_pinned">
|
||||
<input role="switch"
|
||||
type="checkbox"
|
||||
name="pinned"
|
||||
value="1"
|
||||
{% if tag.pinned %}checked{% endif %}
|
||||
id="_pinned">
|
||||
{% trans "Pin" %}
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<input type="radio"
|
||||
name="visibility"
|
||||
value="0"
|
||||
required=""
|
||||
id="id_visibility_0"
|
||||
{% if tag.visibility == 0 %}checked{% endif %}>
|
||||
{% trans "Public" %}
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label for="id_visibility_2">
|
||||
<label for="id_visibility_0">{% trans "Public" %}</label>
|
||||
<input type="radio"
|
||||
name="visibility"
|
||||
value="2"
|
||||
required=""
|
||||
id="id_visibility_2"
|
||||
{% if tag.visibility != 0 %}checked{% endif %}>
|
||||
{% trans "Personal" %}
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</span>
|
||||
</div>
|
||||
<i>{% trans "Personal tags are not shown to others when they view your tag list. However, if you use this tag when marking an item publicly, it might still be visible to others." %}</i>
|
||||
</div>
|
||||
<div class="mark-modal__confirm-button">
|
||||
<label for="id_visibility_2">{% trans "Personal" %}</label>
|
||||
</fieldset>
|
||||
<input type="submit" class="button float-right" value="{% trans "Save" %}">
|
||||
</div>
|
||||
<div class="mark-modal__option">
|
||||
<div class="mark-modal__visibility-radio">
|
||||
<span>
|
||||
<small>{% trans "Personal tags are not shown to others when they view your tag list, unless you pin them. However, if you use this tag when marking an item publicly, it might still be visible to others." %}</small>
|
||||
<label for="_delete">
|
||||
<input type="checkbox" name="delete" value="1" id="_delete">
|
||||
{% trans "Delete this tag" %}
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</dialog>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
{% include "_header.html" %}
|
||||
<main>
|
||||
<div class="grid__main">
|
||||
<h5 class="large-only">
|
||||
<h5>
|
||||
{% block head %}{{ identity.display_name }}{% endblock %}
|
||||
</h5>
|
||||
<div>
|
||||
|
|
|
@ -68,10 +68,11 @@ def user_tag_edit(request):
|
|||
):
|
||||
msg.error(request.user, _("Duplicated tag."))
|
||||
return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
|
||||
tag.title = tag_title
|
||||
tag.visibility = int(request.POST.get("visibility", 0))
|
||||
tag.visibility = 0 if tag.visibility == 0 else 2
|
||||
tag.save()
|
||||
tag.update(
|
||||
tag_title,
|
||||
int(request.POST.get("visibility", 0)),
|
||||
bool(request.POST.get("pinned", 0)),
|
||||
)
|
||||
msg.info(request.user, _("Tag updated."))
|
||||
return redirect(
|
||||
reverse(
|
||||
|
|
|
@ -6,7 +6,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-06-04 10:15-0400\n"
|
||||
"POT-Creation-Date: 2024-06-04 16:48-0400\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -667,7 +667,7 @@ msgstr "不再提示"
|
|||
#: catalog/templates/_item_comments_by_episode.html:78
|
||||
#: catalog/templates/_item_reviews.html:43
|
||||
#: catalog/templates/podcast_episode_data.html:41
|
||||
#: common/templates/_sidebar.html:203
|
||||
#: common/templates/_sidebar.html:204
|
||||
msgid "show more"
|
||||
msgstr "显示更多"
|
||||
|
||||
|
@ -1018,7 +1018,7 @@ msgstr "否"
|
|||
#: journal/templates/collection.html:132
|
||||
#: journal/templates/collection_edit.html:9
|
||||
#: journal/templates/collection_edit.html:25 journal/templates/review.html:80
|
||||
#: journal/templates/tag_edit.html:12
|
||||
#: journal/templates/tag_edit.html:16
|
||||
msgid "Edit"
|
||||
msgstr "编辑"
|
||||
|
||||
|
@ -1031,7 +1031,7 @@ msgstr "创建"
|
|||
#: journal/templates/add_to_collection.html:35
|
||||
#: journal/templates/collection_edit.html:38 journal/templates/comment.html:69
|
||||
#: journal/templates/mark.html:147 journal/templates/review_edit.html:39
|
||||
#: journal/templates/tag_edit.html:60 users/templates/users/account.html:43
|
||||
#: journal/templates/tag_edit.html:51 users/templates/users/account.html:43
|
||||
#: users/templates/users/account.html:104
|
||||
#: users/templates/users/preferences.html:168
|
||||
#: users/templates/users/preferences.html:193
|
||||
|
@ -1112,8 +1112,8 @@ msgstr "热门标签"
|
|||
|
||||
#: catalog/templates/discover.html:185 catalog/templates/item_base.html:236
|
||||
#: catalog/templates/item_mark_list.html:54
|
||||
#: catalog/templates/item_review_list.html:50 common/templates/_sidebar.html:98
|
||||
#: common/templates/_sidebar.html:198
|
||||
#: catalog/templates/item_review_list.html:50 common/templates/_sidebar.html:99
|
||||
#: common/templates/_sidebar.html:199
|
||||
#: common/templates/_sidebar_anonymous.html:43
|
||||
#: common/templates/_sidebar_anonymous.html:58
|
||||
#: journal/templates/collection_items.html:8 journal/templates/posts.html:45
|
||||
|
@ -1544,31 +1544,31 @@ msgstr "设置用户名"
|
|||
msgid "approving followers manually"
|
||||
msgstr "已开启关注审核"
|
||||
|
||||
#: common/templates/_sidebar.html:83
|
||||
#: common/templates/_sidebar.html:84
|
||||
msgid "Current Targets"
|
||||
msgstr "当前目标"
|
||||
|
||||
#: common/templates/_sidebar.html:96
|
||||
#: common/templates/_sidebar.html:97
|
||||
msgid "Set a collection as target, its progress will show up here."
|
||||
msgstr "将自己或他人的收藏单设为目标,这里就会显示进度"
|
||||
|
||||
#: common/templates/_sidebar.html:109
|
||||
#: common/templates/_sidebar.html:110
|
||||
msgid "Recent podcast episodes"
|
||||
msgstr "近期播客节目"
|
||||
|
||||
#: common/templates/_sidebar.html:144
|
||||
#: common/templates/_sidebar.html:145
|
||||
msgid "Currently reading"
|
||||
msgstr "正在阅读"
|
||||
|
||||
#: common/templates/_sidebar.html:167
|
||||
#: common/templates/_sidebar.html:168
|
||||
msgid "Currently watching"
|
||||
msgstr "正在追看"
|
||||
|
||||
#: common/templates/_sidebar.html:190
|
||||
#: common/templates/_sidebar.html:191
|
||||
msgid "Common Tags"
|
||||
msgstr "常用标签"
|
||||
|
||||
#: common/templates/_sidebar.html:214
|
||||
#: common/templates/_sidebar.html:215
|
||||
msgid "Recent Posts"
|
||||
msgstr "近期帖文"
|
||||
|
||||
|
@ -1722,8 +1722,8 @@ msgstr "发布到联邦宇宙"
|
|||
|
||||
#: journal/forms.py:25 journal/forms.py:45
|
||||
#: journal/templates/collection_share.html:24
|
||||
#: journal/templates/tag_edit.html:30 journal/templates/wrapped_share.html:36
|
||||
#: users/templates/users/data.html:38 users/templates/users/data.html:130
|
||||
#: journal/templates/wrapped_share.html:36 users/templates/users/data.html:38
|
||||
#: users/templates/users/data.html:130
|
||||
msgid "Visibility"
|
||||
msgstr "可见性"
|
||||
|
||||
|
@ -1756,7 +1756,7 @@ msgstr "备注"
|
|||
|
||||
#: journal/models/common.py:28 journal/templates/collection_share.html:35
|
||||
#: journal/templates/comment.html:35 journal/templates/mark.html:93
|
||||
#: journal/templates/tag_edit.html:40 journal/templates/wrapped_share.html:43
|
||||
#: journal/templates/tag_edit.html:42 journal/templates/wrapped_share.html:43
|
||||
#: users/templates/users/data.html:47 users/templates/users/data.html:139
|
||||
#: users/templates/users/preferences.html:54
|
||||
msgid "Public"
|
||||
|
@ -2493,7 +2493,7 @@ msgstr "日历"
|
|||
msgid "annual summary"
|
||||
msgstr "年度小结"
|
||||
|
||||
#: journal/templates/profile.html:131 mastodon/api.py:680
|
||||
#: journal/templates/profile.html:131 mastodon/api.py:670
|
||||
msgid "collection"
|
||||
msgstr "收藏单"
|
||||
|
||||
|
@ -2521,19 +2521,19 @@ msgstr "保存时将行首空格替换为全角"
|
|||
msgid "change review date"
|
||||
msgstr "指定评论日期"
|
||||
|
||||
#: journal/templates/tag_edit.html:12
|
||||
msgid "Tag"
|
||||
msgstr "标签"
|
||||
#: journal/templates/tag_edit.html:32
|
||||
msgid "Pin"
|
||||
msgstr "置顶"
|
||||
|
||||
#: journal/templates/tag_edit.html:51
|
||||
#: journal/templates/tag_edit.html:49
|
||||
msgid "Personal"
|
||||
msgstr "个人"
|
||||
|
||||
#: journal/templates/tag_edit.html:57
|
||||
msgid "Personal tags are not shown to others when they view your tag list. However, if you use this tag when marking an item publicly, it might still be visible to others."
|
||||
msgstr "个人标签仅限于在个人主页的标签列表里不向他人展示,如果公开标记一个条目时使用这个标签仍会被别人看到。"
|
||||
#: journal/templates/tag_edit.html:52
|
||||
msgid "Personal tags are not shown to others when they view your tag list, unless you pin them. However, if you use this tag when marking an item publicly, it might still be visible to others."
|
||||
msgstr "个人标签不被包括在条目的公共索引中,但如果公开标记一个条目时使用这个标签仍会被别人看到。"
|
||||
|
||||
#: journal/templates/tag_edit.html:67
|
||||
#: journal/templates/tag_edit.html:55
|
||||
msgid "Delete this tag"
|
||||
msgstr "删除这个标签"
|
||||
|
||||
|
@ -2685,7 +2685,7 @@ msgstr "标签已删除"
|
|||
msgid "Duplicated tag."
|
||||
msgstr "重复标签"
|
||||
|
||||
#: journal/views/tag.py:75
|
||||
#: journal/views/tag.py:76
|
||||
msgid "Tag updated."
|
||||
msgstr "标签已更新"
|
||||
|
||||
|
@ -2698,11 +2698,11 @@ msgstr "总结已发布到时间轴"
|
|||
msgid "regarding {item_title}, may contain spoiler or triggering content"
|
||||
msgstr "关于 {item_title},可能包含剧透或敏感内容"
|
||||
|
||||
#: mastodon/api.py:685
|
||||
#: mastodon/api.py:675
|
||||
msgid "shared my collection"
|
||||
msgstr "分享我的收藏单"
|
||||
|
||||
#: mastodon/api.py:688
|
||||
#: mastodon/api.py:678
|
||||
#, python-brace-format
|
||||
msgid "shared {username}'s collection"
|
||||
msgstr "分享 {username} 的收藏单"
|
||||
|
@ -2964,31 +2964,31 @@ msgstr "提及"
|
|||
msgid "follow"
|
||||
msgstr "关注"
|
||||
|
||||
#: takahe/models.py:427
|
||||
#: takahe/models.py:430
|
||||
msgid "Display Name"
|
||||
msgstr "昵称"
|
||||
|
||||
#: takahe/models.py:429
|
||||
#: takahe/models.py:432
|
||||
msgid "Bio"
|
||||
msgstr "简介"
|
||||
|
||||
#: takahe/models.py:431
|
||||
#: takahe/models.py:434
|
||||
msgid "Manually approve new followers"
|
||||
msgstr "手工审核关注者"
|
||||
|
||||
#: takahe/models.py:435
|
||||
#: takahe/models.py:438
|
||||
msgid "Include profile and posts in discovery"
|
||||
msgstr "允许个人资料和帖文包含在发现中"
|
||||
|
||||
#: takahe/models.py:438
|
||||
#: takahe/models.py:441
|
||||
msgid "Include posts in search results"
|
||||
msgstr "允许个人帖文包含在搜索结果中"
|
||||
|
||||
#: takahe/models.py:456
|
||||
#: takahe/models.py:459
|
||||
msgid "Profile picture"
|
||||
msgstr "头像"
|
||||
|
||||
#: takahe/models.py:463
|
||||
#: takahe/models.py:466
|
||||
msgid "Header picture"
|
||||
msgstr "背景图片"
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-06-04 10:15-0400\n"
|
||||
"POT-Creation-Date: 2024-06-04 16:48-0400\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -667,7 +667,7 @@ msgstr "不再提示"
|
|||
#: catalog/templates/_item_comments_by_episode.html:78
|
||||
#: catalog/templates/_item_reviews.html:43
|
||||
#: catalog/templates/podcast_episode_data.html:41
|
||||
#: common/templates/_sidebar.html:203
|
||||
#: common/templates/_sidebar.html:204
|
||||
msgid "show more"
|
||||
msgstr "顯示更多"
|
||||
|
||||
|
@ -1018,7 +1018,7 @@ msgstr "否"
|
|||
#: journal/templates/collection.html:132
|
||||
#: journal/templates/collection_edit.html:9
|
||||
#: journal/templates/collection_edit.html:25 journal/templates/review.html:80
|
||||
#: journal/templates/tag_edit.html:12
|
||||
#: journal/templates/tag_edit.html:16
|
||||
msgid "Edit"
|
||||
msgstr "編輯"
|
||||
|
||||
|
@ -1031,7 +1031,7 @@ msgstr "創建"
|
|||
#: journal/templates/add_to_collection.html:35
|
||||
#: journal/templates/collection_edit.html:38 journal/templates/comment.html:69
|
||||
#: journal/templates/mark.html:147 journal/templates/review_edit.html:39
|
||||
#: journal/templates/tag_edit.html:60 users/templates/users/account.html:43
|
||||
#: journal/templates/tag_edit.html:51 users/templates/users/account.html:43
|
||||
#: users/templates/users/account.html:104
|
||||
#: users/templates/users/preferences.html:168
|
||||
#: users/templates/users/preferences.html:193
|
||||
|
@ -1112,8 +1112,8 @@ msgstr "熱門標籤"
|
|||
|
||||
#: catalog/templates/discover.html:185 catalog/templates/item_base.html:236
|
||||
#: catalog/templates/item_mark_list.html:54
|
||||
#: catalog/templates/item_review_list.html:50 common/templates/_sidebar.html:98
|
||||
#: common/templates/_sidebar.html:198
|
||||
#: catalog/templates/item_review_list.html:50 common/templates/_sidebar.html:99
|
||||
#: common/templates/_sidebar.html:199
|
||||
#: common/templates/_sidebar_anonymous.html:43
|
||||
#: common/templates/_sidebar_anonymous.html:58
|
||||
#: journal/templates/collection_items.html:8 journal/templates/posts.html:45
|
||||
|
@ -1544,31 +1544,31 @@ msgstr "設置用戶名"
|
|||
msgid "approving followers manually"
|
||||
msgstr "已開啓關注審覈"
|
||||
|
||||
#: common/templates/_sidebar.html:83
|
||||
#: common/templates/_sidebar.html:84
|
||||
msgid "Current Targets"
|
||||
msgstr "當前目標"
|
||||
|
||||
#: common/templates/_sidebar.html:96
|
||||
#: common/templates/_sidebar.html:97
|
||||
msgid "Set a collection as target, its progress will show up here."
|
||||
msgstr "將自己或他人的收藏單設爲目標,這裏就會顯示進度"
|
||||
|
||||
#: common/templates/_sidebar.html:109
|
||||
#: common/templates/_sidebar.html:110
|
||||
msgid "Recent podcast episodes"
|
||||
msgstr "近期播客節目"
|
||||
|
||||
#: common/templates/_sidebar.html:144
|
||||
#: common/templates/_sidebar.html:145
|
||||
msgid "Currently reading"
|
||||
msgstr "正在閱讀"
|
||||
|
||||
#: common/templates/_sidebar.html:167
|
||||
#: common/templates/_sidebar.html:168
|
||||
msgid "Currently watching"
|
||||
msgstr "正在追看"
|
||||
|
||||
#: common/templates/_sidebar.html:190
|
||||
#: common/templates/_sidebar.html:191
|
||||
msgid "Common Tags"
|
||||
msgstr "常用標籤"
|
||||
|
||||
#: common/templates/_sidebar.html:214
|
||||
#: common/templates/_sidebar.html:215
|
||||
msgid "Recent Posts"
|
||||
msgstr "近期帖文"
|
||||
|
||||
|
@ -1722,8 +1722,8 @@ msgstr "發佈到聯邦宇宙"
|
|||
|
||||
#: journal/forms.py:25 journal/forms.py:45
|
||||
#: journal/templates/collection_share.html:24
|
||||
#: journal/templates/tag_edit.html:30 journal/templates/wrapped_share.html:36
|
||||
#: users/templates/users/data.html:38 users/templates/users/data.html:130
|
||||
#: journal/templates/wrapped_share.html:36 users/templates/users/data.html:38
|
||||
#: users/templates/users/data.html:130
|
||||
msgid "Visibility"
|
||||
msgstr "可見性"
|
||||
|
||||
|
@ -1756,7 +1756,7 @@ msgstr "備註"
|
|||
|
||||
#: journal/models/common.py:28 journal/templates/collection_share.html:35
|
||||
#: journal/templates/comment.html:35 journal/templates/mark.html:93
|
||||
#: journal/templates/tag_edit.html:40 journal/templates/wrapped_share.html:43
|
||||
#: journal/templates/tag_edit.html:42 journal/templates/wrapped_share.html:43
|
||||
#: users/templates/users/data.html:47 users/templates/users/data.html:139
|
||||
#: users/templates/users/preferences.html:54
|
||||
msgid "Public"
|
||||
|
@ -2493,7 +2493,7 @@ msgstr "日曆"
|
|||
msgid "annual summary"
|
||||
msgstr "年度小結"
|
||||
|
||||
#: journal/templates/profile.html:131 mastodon/api.py:680
|
||||
#: journal/templates/profile.html:131 mastodon/api.py:670
|
||||
msgid "collection"
|
||||
msgstr "收藏單"
|
||||
|
||||
|
@ -2521,19 +2521,19 @@ msgstr "保存時將行首空格替換爲全角"
|
|||
msgid "change review date"
|
||||
msgstr "指定評論日期"
|
||||
|
||||
#: journal/templates/tag_edit.html:12
|
||||
msgid "Tag"
|
||||
msgstr "標籤"
|
||||
#: journal/templates/tag_edit.html:32
|
||||
msgid "Pin"
|
||||
msgstr "置頂"
|
||||
|
||||
#: journal/templates/tag_edit.html:51
|
||||
#: journal/templates/tag_edit.html:49
|
||||
msgid "Personal"
|
||||
msgstr "個人"
|
||||
|
||||
#: journal/templates/tag_edit.html:57
|
||||
msgid "Personal tags are not shown to others when they view your tag list. However, if you use this tag when marking an item publicly, it might still be visible to others."
|
||||
msgstr "個人標籤僅限於在個人主頁的標籤列表裏不向他人展示,如果公開標記一個條目時使用這個標籤仍會被別人看到。"
|
||||
#: journal/templates/tag_edit.html:52
|
||||
msgid "Personal tags are not shown to others when they view your tag list, unless you pin them. However, if you use this tag when marking an item publicly, it might still be visible to others."
|
||||
msgstr "個人標籤不被包括在條目的公共索引中,但如果公開標記一個條目時使用這個標籤仍會被別人看到。"
|
||||
|
||||
#: journal/templates/tag_edit.html:67
|
||||
#: journal/templates/tag_edit.html:55
|
||||
msgid "Delete this tag"
|
||||
msgstr "刪除這個標籤"
|
||||
|
||||
|
@ -2685,7 +2685,7 @@ msgstr "標籤已刪除"
|
|||
msgid "Duplicated tag."
|
||||
msgstr "重複標籤"
|
||||
|
||||
#: journal/views/tag.py:75
|
||||
#: journal/views/tag.py:76
|
||||
msgid "Tag updated."
|
||||
msgstr "標籤已更新"
|
||||
|
||||
|
@ -2698,11 +2698,11 @@ msgstr "總結已發佈到時間軸"
|
|||
msgid "regarding {item_title}, may contain spoiler or triggering content"
|
||||
msgstr "關於 {item_title},可能包含劇透或敏感內容"
|
||||
|
||||
#: mastodon/api.py:685
|
||||
#: mastodon/api.py:675
|
||||
msgid "shared my collection"
|
||||
msgstr "分享我的收藏單"
|
||||
|
||||
#: mastodon/api.py:688
|
||||
#: mastodon/api.py:678
|
||||
#, python-brace-format
|
||||
msgid "shared {username}'s collection"
|
||||
msgstr "分享 {username} 的收藏單"
|
||||
|
@ -2964,31 +2964,31 @@ msgstr "提及"
|
|||
msgid "follow"
|
||||
msgstr "關注"
|
||||
|
||||
#: takahe/models.py:427
|
||||
#: takahe/models.py:430
|
||||
msgid "Display Name"
|
||||
msgstr "暱稱"
|
||||
|
||||
#: takahe/models.py:429
|
||||
#: takahe/models.py:432
|
||||
msgid "Bio"
|
||||
msgstr "簡介"
|
||||
|
||||
#: takahe/models.py:431
|
||||
#: takahe/models.py:434
|
||||
msgid "Manually approve new followers"
|
||||
msgstr "手工審覈關注者"
|
||||
|
||||
#: takahe/models.py:435
|
||||
#: takahe/models.py:438
|
||||
msgid "Include profile and posts in discovery"
|
||||
msgstr "允許個人資料和帖文包含在發現中"
|
||||
|
||||
#: takahe/models.py:438
|
||||
#: takahe/models.py:441
|
||||
msgid "Include posts in search results"
|
||||
msgstr "允許個人帖文包含在搜索結果中"
|
||||
|
||||
#: takahe/models.py:456
|
||||
#: takahe/models.py:459
|
||||
msgid "Profile picture"
|
||||
msgstr "頭像"
|
||||
|
||||
#: takahe/models.py:463
|
||||
#: takahe/models.py:466
|
||||
msgid "Header picture"
|
||||
msgstr "背景圖片"
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 4.2.13 on 2024-06-01 05:38
|
||||
# Generated by Django 4.2.13 on 2024-06-04 19:33
|
||||
|
||||
import functools
|
||||
|
||||
|
@ -102,6 +102,11 @@ class Migration(migrations.Migration):
|
|||
("public", models.BooleanField(null=True)),
|
||||
("state", models.CharField(default="outdated", max_length=100)),
|
||||
("state_changed", models.DateTimeField(auto_now_add=True)),
|
||||
("state_next_attempt", models.DateTimeField(blank=True, null=True)),
|
||||
(
|
||||
"state_locked_until",
|
||||
models.DateTimeField(blank=True, db_index=True, null=True),
|
||||
),
|
||||
("stats", models.JSONField(blank=True, null=True)),
|
||||
("stats_updated", models.DateTimeField(blank=True, null=True)),
|
||||
("aliases", models.JSONField(blank=True, null=True)),
|
||||
|
@ -162,8 +167,7 @@ class Migration(migrations.Migration):
|
|||
(
|
||||
"indexable",
|
||||
models.BooleanField(
|
||||
default=True,
|
||||
verbose_name="Include posts in search results",
|
||||
default=True, verbose_name="Include posts in search results"
|
||||
),
|
||||
),
|
||||
(
|
||||
|
@ -589,6 +593,40 @@ class Migration(migrations.Migration):
|
|||
blank=True, related_name="identities", to="takahe.user"
|
||||
),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="HashtagFeature",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
(
|
||||
"hashtag",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="featurers",
|
||||
to="takahe.hashtag",
|
||||
),
|
||||
),
|
||||
(
|
||||
"identity",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="hashtag_features",
|
||||
to="takahe.identity",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"db_table": "users_hashtagfeature",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="FanOut",
|
||||
fields=[
|
||||
|
|
|
@ -5,7 +5,7 @@ import re
|
|||
import secrets
|
||||
import ssl
|
||||
import time
|
||||
from datetime import date
|
||||
from datetime import date, timedelta
|
||||
from functools import cached_property, partial
|
||||
from typing import TYPE_CHECKING, Literal, Optional
|
||||
from urllib.parse import urlparse
|
||||
|
@ -385,7 +385,10 @@ class Identity(models.Model):
|
|||
Represents both local and remote Fediverse identities (actors)
|
||||
"""
|
||||
|
||||
if TYPE_CHECKING:
|
||||
domain_id: str
|
||||
inbound_follows: "models.QuerySet[Follow]"
|
||||
hashtag_features: "models.QuerySet[HashtagFeature]"
|
||||
|
||||
class Restriction(models.IntegerChoices):
|
||||
none = 0
|
||||
|
@ -729,6 +732,34 @@ class Identity(models.Model):
|
|||
self.shared_inbox_uri = f"https://{self.domain.uri_domain}/inbox/"
|
||||
self.save()
|
||||
|
||||
def get_remote_targets(self):
|
||||
"""
|
||||
Returns an iterable with Identities of followers that have unique
|
||||
shared_inbox among each other to be used as target.
|
||||
"""
|
||||
if not self.local:
|
||||
return []
|
||||
remote_follower_ids = Follow.objects.filter(
|
||||
target=self,
|
||||
target__local=False,
|
||||
state__in=["unrequested", "pending_approval", "accepting", "accepted"],
|
||||
).values_list("source", flat=True)
|
||||
deduped_targets = set()
|
||||
shared_inboxes = set()
|
||||
for target in Identity.objects.filter(pk__in=remote_follower_ids):
|
||||
if not target.shared_inbox_uri:
|
||||
deduped_targets.add(target)
|
||||
elif target.shared_inbox_uri not in shared_inboxes:
|
||||
shared_inboxes.add(target.shared_inbox_uri)
|
||||
deduped_targets.add(target)
|
||||
return deduped_targets
|
||||
|
||||
def fanout(self, type: str, **kwargs):
|
||||
for target in self.get_remote_targets():
|
||||
FanOut.objects.create(
|
||||
identity=target, subject_identity=self, type=type, **kwargs
|
||||
)
|
||||
|
||||
|
||||
class Follow(models.Model):
|
||||
"""
|
||||
|
@ -1569,6 +1600,8 @@ class Hashtag(models.Model):
|
|||
# state = StateField(HashtagStates)
|
||||
state = models.CharField(max_length=100, default="outdated")
|
||||
state_changed = models.DateTimeField(auto_now_add=True)
|
||||
state_next_attempt = models.DateTimeField(blank=True, null=True)
|
||||
state_locked_until = models.DateTimeField(null=True, blank=True, db_index=True)
|
||||
|
||||
# Metrics for this Hashtag
|
||||
stats = models.JSONField(null=True, blank=True)
|
||||
|
@ -1639,6 +1672,53 @@ class Hashtag(models.Model):
|
|||
results[date(year, month, day)] = val
|
||||
return dict(sorted(results.items(), reverse=True)[:num])
|
||||
|
||||
@property
|
||||
def needs_update(self):
|
||||
if self.stats_updated is None:
|
||||
return True
|
||||
return timezone.now() - self.stats_updated > timedelta(hours=1)
|
||||
|
||||
@classmethod
|
||||
def ensure_hashtag(cls, name, update=None):
|
||||
"""
|
||||
Properly strips/trims/lowercases the hashtag name, and makes sure a Hashtag
|
||||
object exists in the database, and returns it.
|
||||
"""
|
||||
name = name.strip().lstrip("#").lower()[: Hashtag.MAXIMUM_LENGTH]
|
||||
hashtag, created = cls.objects.get_or_create(hashtag=name)
|
||||
if created or update or hashtag.needs_update:
|
||||
hashtag.state = "outdated"
|
||||
hashtag.state_changed = timezone.now()
|
||||
hashtag.state_next_attempt = None
|
||||
hashtag.state_locked_until = None
|
||||
hashtag.save(
|
||||
update_fields=[
|
||||
"state",
|
||||
"state_changed",
|
||||
"state_next_attempt",
|
||||
"state_locked_until",
|
||||
]
|
||||
)
|
||||
return hashtag
|
||||
|
||||
|
||||
class HashtagFeature(models.Model):
|
||||
identity = models.ForeignKey(
|
||||
"takahe.Identity",
|
||||
on_delete=models.CASCADE,
|
||||
related_name="hashtag_features",
|
||||
)
|
||||
hashtag = models.ForeignKey(
|
||||
"takahe.Hashtag",
|
||||
on_delete=models.CASCADE,
|
||||
related_name="featurers",
|
||||
)
|
||||
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
db_table = "users_hashtagfeature"
|
||||
|
||||
|
||||
class PostInteraction(models.Model):
|
||||
"""
|
||||
|
|
|
@ -990,3 +990,21 @@ class Takahe:
|
|||
else:
|
||||
qs = qs.filter(visibility__in=[0, 1, 4])
|
||||
return qs.prefetch_related("attachments", "author")
|
||||
|
||||
@staticmethod
|
||||
def pin_hashtag_for_user(identity_pk: int, hashtag: str):
|
||||
tag = Hashtag.ensure_hashtag(hashtag)
|
||||
identity = Identity.objects.get(pk=identity_pk)
|
||||
feature, created = identity.hashtag_features.get_or_create(hashtag=tag)
|
||||
if created:
|
||||
identity.fanout("tag_featured", subject_hashtag=tag)
|
||||
|
||||
@staticmethod
|
||||
def unpin_hashtag_for_user(identity_pk: int, hashtag: str):
|
||||
identity = Identity.objects.get(pk=identity_pk)
|
||||
featured = HashtagFeature.objects.filter(
|
||||
identity=identity, hashtag_id=hashtag
|
||||
).first()
|
||||
if featured:
|
||||
identity.fanout("tag_unfeatured", subject_hashtag_id=hashtag)
|
||||
featured.delete()
|
||||
|
|
Loading…
Add table
Reference in a new issue