add progress to note

This commit is contained in:
Your Name 2024-06-15 18:26:20 -04:00 committed by Henri Dickson
parent 3eafcd3ec8
commit 7157f3b0b0
22 changed files with 779 additions and 192 deletions

View file

@ -90,18 +90,25 @@
{% include "action_open_post.html" with post=note.latest_post %} {% include "action_open_post.html" with post=note.latest_post %}
{% endif %} {% endif %}
</span> </span>
<h6>{{ note.title|default:'' }}</h6> {% if note.title %}<h6>{{ note.title|default:'' }}</h6>{% endif %}
<p>{{ note.content|linebreaks }}</p> <p>
<div> {% if note.progress_value %}<span class="tag-list"><span><a>{{ note.progress_display }}</a></span></span>{% endif %}
{% for attachment in note.attachments %} {{ note.content|linebreaksbr }}
{% if attachment.type == 'image' %} <div class="attachments">
<img src="{{ attachment.url }}" {% for attachment in note.attachments %}
alt="image attachment" {% if attachment.type == 'image' %}
style="max-height:6em; <a href="#img_{{ note.uuid }}_{{ loop.index }}">
max-width:50%"> <img src="{{ attachment.preview_url }}"
{% endif %} alt="image attachment"
{% endfor %} class="preview">
</div> </a>
<a href="#" class="lightbox" id="img_{{ note.uuid }}_{{ loop.index }}">
<span style="background-image: url('{{ attachment.url }}')"></span>
</a>
{% endif %}
{% endfor %}
</div>
</p>
{% endfor %} {% endfor %}
</section> </section>
<section> <section>

View file

@ -3,11 +3,23 @@ import json
import django.contrib.postgres.forms as postgres import django.contrib.postgres.forms as postgres
from django import forms from django import forms
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.forms import ModelForm
from django.utils import formats from django.utils import formats
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from markdownx.fields import MarkdownxFormField from markdownx.fields import MarkdownxFormField
class NeoModelForm(ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# if "uuid" in self.fields:
# if self.instance and self.instance.pk:
# self.fields["uuid"].initial = self.instance.uuid
for visible in self.visible_fields():
w = visible.field.widget
w.attrs["class"] = "widget " + w.__class__.__name__.lower()
class PreviewImageInput(forms.FileInput): class PreviewImageInput(forms.FileInput):
template_name = "widgets/image.html" template_name = "widgets/image.html"

View file

@ -86,6 +86,16 @@ dialog {
article { article {
padding-bottom: 1em; padding-bottom: 1em;
form {
overflow: hidden;
input[type="submit"] {
margin-top: 1em;
}
div.widget.radioselect {
display: flex;
grid-column-gap: 1em;
}
}
} }
input[type="date"] { input[type="date"] {

View file

@ -0,0 +1,26 @@
.lightbox {
display: none;
position: fixed;
z-index: 999;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding: 1em;
background: rgba(0, 0, 0, 0.8);
-webkit-backdrop-filter: var(--pico-modal-overlay-backdrop-filter);
}
.lightbox:target {
display: block;
}
.lightbox span {
display: block;
width: 100%;
height: 100%;
background-position: center;
background-repeat: no-repeat;
background-size: contain;
}

View file

@ -27,3 +27,10 @@ section.replies {
} }
} }
} }
.attachements {
img.preview {
max-height: 6em;
max-width: 50%;
}
}

View file

@ -20,3 +20,4 @@
@import '_form.scss'; @import '_form.scss';
@import '_post.scss'; @import '_post.scss';
@import '_l10n.scss'; @import '_l10n.scss';
@import '_lightbox.scss';

View file

@ -159,7 +159,17 @@ class Migration(migrations.Migration):
to="journal.piece", to="journal.piece",
), ),
), ),
("visibility", models.PositiveSmallIntegerField(default=0)), (
"visibility",
models.PositiveSmallIntegerField(
choices=[
(0, "Public"),
(1, "Followers Only"),
(2, "Mentioned Only"),
],
default=0,
),
),
( (
"created_time", "created_time",
models.DateTimeField(default=django.utils.timezone.now), models.DateTimeField(default=django.utils.timezone.now),
@ -205,7 +215,17 @@ class Migration(migrations.Migration):
to="journal.piece", to="journal.piece",
), ),
), ),
("visibility", models.PositiveSmallIntegerField(default=0)), (
"visibility",
models.PositiveSmallIntegerField(
choices=[
(0, "Public"),
(1, "Followers Only"),
(2, "Mentioned Only"),
],
default=0,
),
),
( (
"created_time", "created_time",
models.DateTimeField(default=django.utils.timezone.now), models.DateTimeField(default=django.utils.timezone.now),
@ -233,7 +253,17 @@ class Migration(migrations.Migration):
to="journal.piece", to="journal.piece",
), ),
), ),
("visibility", models.PositiveSmallIntegerField(default=0)), (
"visibility",
models.PositiveSmallIntegerField(
choices=[
(0, "Public"),
(1, "Followers Only"),
(2, "Mentioned Only"),
],
default=0,
),
),
( (
"created_time", "created_time",
models.DateTimeField(default=django.utils.timezone.now), models.DateTimeField(default=django.utils.timezone.now),
@ -265,7 +295,17 @@ class Migration(migrations.Migration):
to="journal.piece", to="journal.piece",
), ),
), ),
("visibility", models.PositiveSmallIntegerField(default=0)), (
"visibility",
models.PositiveSmallIntegerField(
choices=[
(0, "Public"),
(1, "Followers Only"),
(2, "Mentioned Only"),
],
default=0,
),
),
( (
"created_time", "created_time",
models.DateTimeField(default=django.utils.timezone.now), models.DateTimeField(default=django.utils.timezone.now),
@ -316,7 +356,17 @@ class Migration(migrations.Migration):
to="journal.piece", to="journal.piece",
), ),
), ),
("visibility", models.PositiveSmallIntegerField(default=0)), (
"visibility",
models.PositiveSmallIntegerField(
choices=[
(0, "Public"),
(1, "Followers Only"),
(2, "Mentioned Only"),
],
default=0,
),
),
( (
"created_time", "created_time",
models.DateTimeField(default=django.utils.timezone.now), models.DateTimeField(default=django.utils.timezone.now),
@ -343,7 +393,17 @@ class Migration(migrations.Migration):
to="journal.piece", to="journal.piece",
), ),
), ),
("visibility", models.PositiveSmallIntegerField(default=0)), (
"visibility",
models.PositiveSmallIntegerField(
choices=[
(0, "Public"),
(1, "Followers Only"),
(2, "Mentioned Only"),
],
default=0,
),
),
( (
"created_time", "created_time",
models.DateTimeField(default=django.utils.timezone.now), models.DateTimeField(default=django.utils.timezone.now),
@ -382,7 +442,17 @@ class Migration(migrations.Migration):
to="journal.piece", to="journal.piece",
), ),
), ),
("visibility", models.PositiveSmallIntegerField(default=0)), (
"visibility",
models.PositiveSmallIntegerField(
choices=[
(0, "Public"),
(1, "Followers Only"),
(2, "Mentioned Only"),
],
default=0,
),
),
( (
"created_time", "created_time",
models.DateTimeField(default=django.utils.timezone.now), models.DateTimeField(default=django.utils.timezone.now),
@ -415,7 +485,17 @@ class Migration(migrations.Migration):
to="journal.piece", to="journal.piece",
), ),
), ),
("visibility", models.PositiveSmallIntegerField(default=0)), (
"visibility",
models.PositiveSmallIntegerField(
choices=[
(0, "Public"),
(1, "Followers Only"),
(2, "Mentioned Only"),
],
default=0,
),
),
( (
"created_time", "created_time",
models.DateTimeField(default=django.utils.timezone.now), models.DateTimeField(default=django.utils.timezone.now),
@ -451,7 +531,17 @@ class Migration(migrations.Migration):
to="journal.piece", to="journal.piece",
), ),
), ),
("visibility", models.PositiveSmallIntegerField(default=0)), (
"visibility",
models.PositiveSmallIntegerField(
choices=[
(0, "Public"),
(1, "Followers Only"),
(2, "Mentioned Only"),
],
default=0,
),
),
( (
"created_time", "created_time",
models.DateTimeField(default=django.utils.timezone.now), models.DateTimeField(default=django.utils.timezone.now),
@ -596,7 +686,17 @@ class Migration(migrations.Migration):
to="journal.piece", to="journal.piece",
), ),
), ),
("visibility", models.PositiveSmallIntegerField(default=0)), (
"visibility",
models.PositiveSmallIntegerField(
choices=[
(0, "Public"),
(1, "Followers Only"),
(2, "Mentioned Only"),
],
default=0,
),
),
( (
"created_time", "created_time",
models.DateTimeField(default=django.utils.timezone.now), models.DateTimeField(default=django.utils.timezone.now),
@ -656,7 +756,17 @@ class Migration(migrations.Migration):
to="journal.piece", to="journal.piece",
), ),
), ),
("visibility", models.PositiveSmallIntegerField(default=0)), (
"visibility",
models.PositiveSmallIntegerField(
choices=[
(0, "Public"),
(1, "Followers Only"),
(2, "Mentioned Only"),
],
default=0,
),
),
( (
"created_time", "created_time",
models.DateTimeField(default=django.utils.timezone.now), models.DateTimeField(default=django.utils.timezone.now),

View file

@ -27,7 +27,17 @@ class Migration(migrations.Migration):
to="journal.piece", to="journal.piece",
), ),
), ),
("visibility", models.PositiveSmallIntegerField(default=0)), (
"visibility",
models.PositiveSmallIntegerField(
choices=[
(0, "Public"),
(1, "Followers Only"),
(2, "Mentioned Only"),
],
default=0,
),
),
( (
"created_time", "created_time",
models.DateTimeField(default=django.utils.timezone.now), models.DateTimeField(default=django.utils.timezone.now),

View file

@ -0,0 +1,44 @@
# Generated by Django 4.2.13 on 2024-06-14 22:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("journal", "0002_note"),
]
operations = [
migrations.AddField(
model_name="note",
name="progress_type",
field=models.CharField(
blank=True,
choices=[
("page", "Page"),
("chapter", "Chapter"),
("part", "Part"),
("episode", "Episode"),
("track", "Track"),
("cycle", "Cycle"),
("timestamp", "Timestamp"),
("percentage", "Percentage"),
],
default=None,
max_length=50,
null=True,
),
),
migrations.AddField(
model_name="note",
name="progress_value",
field=models.CharField(blank=True, default=None, max_length=500, null=True),
),
migrations.AddIndex(
model_name="note",
index=models.Index(
fields=["owner", "item", "created_time"],
name="journal_not_owner_i_7f9460_idx",
),
),
]

View file

@ -434,8 +434,8 @@ class PieceInteraction(models.Model):
class Content(Piece): class Content(Piece):
owner = models.ForeignKey(APIdentity, on_delete=models.PROTECT) owner = models.ForeignKey(APIdentity, on_delete=models.PROTECT)
visibility = models.PositiveSmallIntegerField( visibility = models.PositiveSmallIntegerField(
default=0 choices=VisibilityType.choices, default=0, null=False
) # 0: Public / 1: Follower only / 2: Self only # type:ignore ) # type:ignore
created_time = models.DateTimeField(default=timezone.now) created_time = models.DateTimeField(default=timezone.now)
edited_time = models.DateTimeField(auto_now=True) edited_time = models.DateTimeField(auto_now=True)
metadata = models.JSONField(default=dict) metadata = models.JSONField(default=dict)

View file

@ -8,7 +8,7 @@ from django.utils import timezone
from catalog.models import Item, ItemCategory from catalog.models import Item, ItemCategory
from users.models import APIdentity from users.models import APIdentity
from .common import Piece from .common import Piece, VisibilityType
list_add = django.dispatch.Signal() list_add = django.dispatch.Signal()
list_remove = django.dispatch.Signal() list_remove = django.dispatch.Signal()
@ -25,8 +25,8 @@ class List(Piece):
items: "models.ManyToManyField[Item, List]" items: "models.ManyToManyField[Item, List]"
owner = models.ForeignKey(APIdentity, on_delete=models.PROTECT) owner = models.ForeignKey(APIdentity, on_delete=models.PROTECT)
visibility = models.PositiveSmallIntegerField( visibility = models.PositiveSmallIntegerField(
default=0 choices=VisibilityType.choices, default=0, null=False
) # 0: Public / 1: Follower only / 2: Self only # type:ignore ) # type:ignore
created_time = models.DateTimeField(default=timezone.now) created_time = models.DateTimeField(default=timezone.now)
edited_time = models.DateTimeField(auto_now=True) edited_time = models.DateTimeField(auto_now=True)
metadata = models.JSONField(default=dict) metadata = models.JSONField(default=dict)
@ -151,8 +151,8 @@ class ListMember(Piece):
parent: models.ForeignKey["ListMember", "List"] parent: models.ForeignKey["ListMember", "List"]
owner = models.ForeignKey(APIdentity, on_delete=models.PROTECT) owner = models.ForeignKey(APIdentity, on_delete=models.PROTECT)
visibility = models.PositiveSmallIntegerField( visibility = models.PositiveSmallIntegerField(
default=0 choices=VisibilityType.choices, default=0, null=False
) # 0: Public / 1: Follower only / 2: Self only # type:ignore[reportAssignmentType] ) # type:ignore
created_time = models.DateTimeField(default=timezone.now) created_time = models.DateTimeField(default=timezone.now)
edited_time = models.DateTimeField(auto_now=True) edited_time = models.DateTimeField(auto_now=True)
metadata = models.JSONField(default=dict) metadata = models.JSONField(default=dict)

View file

@ -5,14 +5,14 @@ from django.utils.translation import gettext_lazy as _
from users.models import APIdentity from users.models import APIdentity
from .common import Piece from .common import Piece, VisibilityType
class Like(Piece): # TODO remove class Like(Piece): # TODO remove
owner = models.ForeignKey(APIdentity, on_delete=models.PROTECT) owner = models.ForeignKey(APIdentity, on_delete=models.PROTECT)
visibility = models.PositiveSmallIntegerField( visibility = models.PositiveSmallIntegerField(
default=0 choices=VisibilityType.choices, default=0, null=False
) # 0: Public / 1: Follower only / 2: Self only # type: ignore ) # type:ignore
created_time = models.DateTimeField(default=timezone.now) created_time = models.DateTimeField(default=timezone.now)
edited_time = models.DateTimeField(auto_now=True) edited_time = models.DateTimeField(auto_now=True)
target = models.ForeignKey(Piece, on_delete=models.CASCADE, related_name="likes") target = models.ForeignKey(Piece, on_delete=models.CASCADE, related_name="likes")

View file

@ -108,7 +108,9 @@ class Mark:
@cached_property @cached_property
def notes(self): def notes(self):
return Note.objects.filter(owner=self.owner, item=self.item) return Note.objects.filter(owner=self.owner, item=self.item).order_by(
"-created_time"
)
# post_ids = PiecePost.objects.filter( # post_ids = PiecePost.objects.filter(
# piece__note__owner_id=self.owner.pk, piece__note__item_id=self.item.pk # piece__note__owner_id=self.owner.pk, piece__note__item_id=self.item.pk
# ).values_list("post_id", flat=True) # ).values_list("post_id", flat=True)

View file

@ -1,9 +1,12 @@
import re
from functools import cached_property from functools import cached_property
from typing import override from typing import override
from django.db import models from django.db import models
from django.utils.translation import gettext_lazy as _
from loguru import logger from loguru import logger
from catalog.models import Item
from mastodon.api import delete_toot_later from mastodon.api import delete_toot_later
from takahe.utils import Takahe from takahe.utils import Takahe
@ -11,17 +14,74 @@ from .common import Content
from .renderers import render_text from .renderers import render_text
from .shelf import ShelfMember from .shelf import ShelfMember
_progress = re.compile(
r"^\s*(?P<prefix>(p|pg|page|ch|chapter|pt|part|e|ep|episode|trk|track|cycle))?(\s|\.|#)*(?P<value>(\d[\d\:\.\-]*\d|\d))\s*(?P<postfix>(%))?\s*(\s|\n|\.|:)",
re.IGNORECASE,
)
_number = re.compile(r"^[\s\d\:\.]+$")
class Note(Content): class Note(Content):
class ProgressType(models.TextChoices):
PAGE = "page", _("Page")
CHAPTER = "chapter", _("Chapter")
# SECTION = "section", _("Section")
# VOLUME = "volume", _("Volume")
PART = "part", _("Part")
EPISODE = "episode", _("Episode")
TRACK = "track", _("Track")
CYCLE = "cycle", _("Cycle")
TIMESTAMP = "timestamp", _("Timestamp")
PERCENTAGE = "percentage", _("Percentage")
title = models.TextField(blank=True, null=True, default=None) title = models.TextField(blank=True, null=True, default=None)
content = models.TextField(blank=False, null=False) content = models.TextField(blank=False, null=False)
sensitive = models.BooleanField(default=False, null=False) sensitive = models.BooleanField(default=False, null=False)
attachments = models.JSONField(default=list) attachments = models.JSONField(default=list)
progress_type = models.CharField(
max_length=50,
choices=ProgressType.choices,
blank=True,
null=True,
default=None,
)
progress_value = models.CharField(
max_length=500, blank=True, null=True, default=None
)
_progress_display_template = {
ProgressType.PAGE: _("Page {value}"),
ProgressType.CHAPTER: _("Chapter {value}"),
# ProgressType.SECTION: _("Section {value}"),
# ProgressType.VOLUME: _("Volume {value}"),
ProgressType.PART: _("Part {value}"),
ProgressType.EPISODE: _("Episode {value}"),
ProgressType.TRACK: _("Track {value}"),
ProgressType.CYCLE: _("Cycle {value}"),
ProgressType.PERCENTAGE: "{value}%",
ProgressType.TIMESTAMP: "{value}",
}
class Meta:
indexes = [models.Index(fields=["owner", "item", "created_time"])]
@property @property
def html(self): def html(self):
return render_text(self.content) return render_text(self.content)
@property
def progress_display(self) -> str:
if not self.progress_value:
return ""
if not self.progress_type:
return str(self.progress_value)
tpl = Note._progress_display_template.get(self.progress_type, None)
if not tpl:
return str(self.progress_value)
if _number.match(self.progress_value):
return tpl.format(value=self.progress_value)
return self.progress_type.display + ": " + self.progress_value
@property @property
def ap_object(self): def ap_object(self):
d = { d = {
@ -36,6 +96,11 @@ class Note(Content):
"withRegardTo": self.item.absolute_url, "withRegardTo": self.item.absolute_url,
"href": self.absolute_url, "href": self.absolute_url,
} }
if self.progress_value:
d["progress"] = {
"type": self.progress_type,
"value": self.progress_value,
}
return d return d
@override @override
@ -47,6 +112,17 @@ class Note(Content):
"sensitive": obj.get("sensitive", post.sensitive), "sensitive": obj.get("sensitive", post.sensitive),
"attachments": [], "attachments": [],
} }
progress = obj.get("progress", {})
if progress.get("type"):
params["progress_type"] = progress.get("type")
if progress.get("value"):
params["progress_value"] = progress.get("value")
if post.local:
progress_type, progress_value = cls.extract_progress(params["content"])
print(progress_type, progress_value)
if progress_value:
params["progress_type"] = progress_type
params["progress_value"] = progress_value
if post: if post:
for atta in post.attachments.all(): for atta in post.attachments.all():
params["attachments"].append( params["attachments"].append(
@ -103,3 +179,79 @@ class Note(Content):
), ),
# not passing "attachments" so it won't change # not passing "attachments" so it won't change
} }
@classmethod
def extract_progress(cls, content):
m = _progress.match(content)
if m and m["value"]:
typ_ = "percentage" if m["postfix"] == "%" else m["prefix"]
match typ_:
case "p" | "pg" | "page":
typ = Note.ProgressType.PAGE
case "ch" | "chapter":
typ = Note.ProgressType.CHAPTER
# case "vol" | "volume":
# typ = ProgressType.VOLUME
# case "section":
# typ = ProgressType.SECTION
case "pt" | "part":
typ = Note.ProgressType.PART
case "e" | "ep" | "episode":
typ = Note.ProgressType.EPISODE
case "trk" | "track":
typ = Note.ProgressType.TRACK
case "cycle":
typ = Note.ProgressType.CYCLE
case "percentage":
typ = Note.ProgressType.PERCENTAGE
case _:
typ = "timestamp" if ":" in m["value"] else None
return typ, m["value"]
return None, None
@classmethod
def get_progress_types_by_item(cls, item: Item):
match item.__class__.__name__:
case "Edition":
v = [
Note.ProgressType.PAGE,
Note.ProgressType.CHAPTER,
Note.ProgressType.PERCENTAGE,
]
case "TVShow" | "TVSeason":
v = [
Note.ProgressType.PART,
Note.ProgressType.EPISODE,
Note.ProgressType.PERCENTAGE,
]
case "Movie":
v = [
Note.ProgressType.PART,
Note.ProgressType.TIMESTAMP,
Note.ProgressType.PERCENTAGE,
]
case "Podcast":
v = [
Note.ProgressType.EPISODE,
]
case "TVEpisode" | "PodcastEpisode":
v = []
case "Album":
v = [
Note.ProgressType.TRACK,
Note.ProgressType.TIMESTAMP,
Note.ProgressType.PERCENTAGE,
]
case "Game":
v = [
Note.ProgressType.CYCLE,
]
case "Performance" | "PerformanceProduction":
v = [
Note.ProgressType.PART,
Note.ProgressType.TIMESTAMP,
Note.ProgressType.PERCENTAGE,
]
case _:
v = []
return v

View file

@ -16,50 +16,23 @@
<strong>{% trans 'Note' %} - {{ item.display_title }}</strong> <strong>{% trans 'Note' %} - {{ item.display_title }}</strong>
</header> </header>
<div> <div>
<form action="{% url 'journal:note' item.uuid %}" method="post"> <form action="{% url 'journal:note' item.uuid %}"
method="post"
onsubmit="return !!$('#{{ form.content.id_for_label }}').val() || confirm('{% trans "Note with empty content will be deleted, sure to continue?" %}');;">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="uuid" value="{{ note.uuid|default:'' }}"> {{ form.uuid }}
<textarea name="content" cols="40" rows="10" placeholder="" id="id_content">{{ note.content|default:'' }}</textarea>
<div class="grid"> <div class="grid">
<div>{{ form.progress_type }}</div>
<div>{{ form.progress_value }}</div>
</div>
{{ form.content }}
{{ form.title }}
<div class="grid">
<div>{{ form.visibility }}</div>
<div> <div>
<fieldset> <label for="{{ form.share_to_mastodon.id_for_label }}">
<input type="radio" {{ form.share_to_mastodon }} {{ form.share_to_mastodon.label }}
name="visibility" </label>
value="0"
required=""
id="id_visibility_0"
{% if note.visibility == 0 or not note %}checked{% endif %}>
<label for="id_visibility_0">{% trans "Public" %}</label>
<input type="radio"
name="visibility"
value="1"
required=""
id="id_visibility_1"
{% if note.visibility == 1 %}checked{% endif %}>
<label for="id_visibility_1">{% trans "Followers Only" %}</label>
<input type="radio"
name="visibility"
value="2"
required=""
id="id_visibility_2"
{% if note.visibility == 2 %}checked{% endif %}>
<label for="id_visibility_2">{% trans "Mentioned Only" %}</label>
</fieldset>
</div>
<div>
<fieldset>
{% if request.user.mastodon_acct %}
<label for="id_share_to_mastodon">
<input role="switch"
type="checkbox"
name="share_to_mastodon"
id="id_share_to_mastodon"
value="1"
{% if request.user.preference.mastodon_default_repost %}checked{% endif %}>
{% trans "Repost to timeline" %}
</label>
{% endif %}
</fieldset>
</div> </div>
</div> </div>
<div> <div>

View file

@ -25,13 +25,17 @@
</span> </span>
<div>{{ post.summary|default:'' }}</div> <div>{{ post.summary|default:'' }}</div>
<div {% if post.summary or post.sensitive %}class="spoiler" _="on click toggle .revealed on me"{% endif %}> <div {% if post.summary or post.sensitive %}class="spoiler" _="on click toggle .revealed on me"{% endif %}>
<div> <div class="attachments">
{% for attachment in post.attachments.all %} {% for attachment in post.attachments.all %}
{% if attachment.is_image %} {% if attachment.is_image %}
<img src="{{ attachment.full_url.relative }}" <a href="#img_{{ post.pk }}_{{ loop.index }}">
alt="attachment.file_display_name" <img src="{{ attachment.thumbnail_url.relative }}"
style="max-height:6em; alt="image attachment"
max-width:50%"> class="preview">
</a>
<a href="#" class="lightbox" id="img_{{ post.pk }}_{{ loop.index }}">
<span style="background-image: url('{{ attachment.full_url.relative }}')"></span>
</a>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</div> </div>

View file

@ -21,8 +21,8 @@ urlpatterns = [
path("wish/<str:item_uuid>", wish, name="wish"), path("wish/<str:item_uuid>", wish, name="wish"),
path("mark/<str:item_uuid>", mark, name="mark"), path("mark/<str:item_uuid>", mark, name="mark"),
path("comment/<str:item_uuid>", comment, name="comment"), path("comment/<str:item_uuid>", comment, name="comment"),
path("note/<str:item_uuid>", note, name="note"), path("item/<str:item_uuid>/note", note_edit, name="note"),
path("note/<str:item_uuid>/<str:note_uuid>", note, name="note"), path("item/<str:item_uuid>/note/<str:note_uuid>", note_edit, name="note"),
path("piece/<str:piece_uuid>/replies", piece_replies, name="piece_replies"), path("piece/<str:piece_uuid>/replies", piece_replies, name="piece_replies"),
path("post/<int:post_id>/replies", post_replies, name="post_replies"), path("post/<int:post_id>/replies", post_replies, name="post_replies"),
path("post/<int:post_id>/reply", post_reply, name="post_reply"), path("post/<int:post_id>/reply", post_reply, name="post_reply"),

View file

@ -16,7 +16,8 @@ from .collection import (
user_liked_collection_list, user_liked_collection_list,
) )
from .common import piece_delete from .common import piece_delete
from .mark import comment, mark, mark_log, note, user_mark_list, wish from .mark import comment, mark, mark_log, user_mark_list, wish
from .note import note_edit
from .post import ( from .post import (
piece_replies, piece_replies,
post_boost, post_boost,

View file

@ -1,5 +1,6 @@
from datetime import datetime from datetime import datetime
from django import forms
from django.conf import settings from django.conf import settings
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.exceptions import BadRequest, ObjectDoesNotExist, PermissionDenied from django.core.exceptions import BadRequest, ObjectDoesNotExist, PermissionDenied
@ -13,10 +14,8 @@ from loguru import logger
from catalog.models import * from catalog.models import *
from common.utils import AuthedHttpRequest, get_uuid_or_404 from common.utils import AuthedHttpRequest, get_uuid_or_404
from mastodon.api import boost_toot_later
from takahe.utils import Takahe
from ..models import Comment, Mark, Note, ShelfManager, ShelfType, TagManager from ..models import Comment, Mark, ShelfManager, ShelfType, TagManager
from .common import render_list, render_relogin, target_identity_required from .common import render_list, render_relogin, target_identity_required
PAGE_SIZE = 10 PAGE_SIZE = 10
@ -190,53 +189,6 @@ def comment(request: AuthedHttpRequest, item_uuid):
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/")) return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
@login_required
@require_http_methods(["GET", "POST"])
def note(request: AuthedHttpRequest, item_uuid: str, note_uuid: str = ""):
item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
note_uuid = request.POST.get("uuid", note_uuid)
note = None
content = request.POST.get("content")
if note_uuid:
note = get_object_or_404(
Note, owner=request.user.identity, item=item, uid=get_uuid_or_404(note_uuid)
)
if request.method == "GET":
return render(
request,
"note.html",
{
"item": item,
"note": note,
},
)
else:
if request.POST.get("delete", default=False) or not content:
if not note:
raise Http404(_("Content not found"))
note.delete()
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
share_to_mastodon = bool(request.POST.get("share_to_mastodon", default=False))
visibility = int(request.POST.get("visibility", default=0))
delete_existing_post = False
if note:
delete_existing_post = visibility != note.visibility
note.content = content
note.visibility = visibility
note.save()
else:
note = Note.objects.create(
owner=request.user.identity,
item=item,
content=content,
visibility=visibility,
)
note.sync_to_timeline(delete_existing=delete_existing_post)
if share_to_mastodon:
note.sync_to_mastodon(delete_existing=delete_existing_post)
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
def user_mark_list(request: AuthedHttpRequest, user_name, shelf_type, item_category): def user_mark_list(request: AuthedHttpRequest, user_name, shelf_type, item_category):
return render_list( return render_list(
request, user_name, "mark", shelf_type=shelf_type, item_category=item_category request, user_name, "mark", shelf_type=shelf_type, item_category=item_category

110
journal/views/note.py Normal file
View file

@ -0,0 +1,110 @@
from django import forms
from django.contrib.auth.decorators import login_required
from django.core.exceptions import BadRequest, ObjectDoesNotExist, PermissionDenied
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.translation import gettext as _
from django.views.decorators.http import require_http_methods
from catalog.models import Item
from common.forms import NeoModelForm
from common.utils import AuthedHttpRequest, get_uuid_or_404
from ..models import Note
from ..models.common import VisibilityType
class NoteForm(NeoModelForm):
# _progress_choices = [
# ("", _("Progress Type (optional)"))
# ] + Note.ProgressType.choices
# progress_type = forms.ChoiceField(choices=_progress_choices, required=False)
visibility = forms.ChoiceField(
widget=forms.RadioSelect(), choices=VisibilityType.choices, initial=0
)
share_to_mastodon = forms.BooleanField(
label=_("Post to Fediverse"), initial=True, required=False
)
uuid = forms.CharField(widget=forms.HiddenInput(), required=False)
# content = forms.CharField(required=False, widget=forms.Textarea)
class Meta:
model = Note
fields = [
"id",
"title",
"content",
"visibility",
"progress_type",
"progress_value",
"sensitive",
]
widgets = {
"progress_value": forms.TextInput(
attrs={"placeholder": _("Progress (optional)")}
),
"content": forms.Textarea(attrs={"placeholder": _("Note Content")}),
"title": forms.TextInput(
attrs={"placeholder": _("Content Warning (optional)")}
),
}
def __init__(self, *args, **kwargs):
item = kwargs.pop("item")
super().__init__(*args, **kwargs)
# allow submit empty content for existing note, and we'll delete it
if self.instance.id:
self.fields["content"].required = False
# get the corresponding progress types for the item
types = Note.get_progress_types_by_item(item)
if self.instance.progress_type and self.instance.progress_type not in types:
types.append(self.instance.progress_type)
choices = [("", _("Progress Type (optional)"))] + [(x, x.label) for x in types]
self.fields["progress_type"].choices = choices # type: ignore
@login_required
@require_http_methods(["GET", "POST"])
def note_edit(request: AuthedHttpRequest, item_uuid: str, note_uuid: str = ""):
item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
owner = request.user.identity
note_uuid = request.POST.get("uuid", note_uuid)
note = None
if note_uuid:
note = get_object_or_404(
Note, owner=owner, item=item, uid=get_uuid_or_404(note_uuid)
)
form = NoteForm(
request.POST or None, item=item, instance=note, initial={"uuid": note_uuid}
)
form.instance.owner = owner
form.instance.item = item
if request.method == "GET":
return render(
request,
"note.html",
{
"item": item,
"note": note,
"form": form,
},
)
if not form.data["content"]:
if not note:
raise Http404(_("Content not found"))
note.delete()
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
if note:
orig_visibility = note.visibility
else:
orig_visibility = None
if not form.is_valid():
raise BadRequest(_("Invalid form data"))
note = form.save()
delete_existing_post = (
orig_visibility is not None and orig_visibility != note.visibility
)
note.sync_to_timeline(delete_existing=delete_existing_post)
if form.cleaned_data["share_to_mastodon"]:
note.sync_to_mastodon(delete_existing=delete_existing_post)
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))

View file

@ -6,7 +6,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-13 20:50-0400\n" "POT-Creation-Date: 2024-06-15 18:22-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -715,15 +715,15 @@ msgstr "我的短评和标签"
msgid "my notes" msgid "my notes"
msgstr "我的笔记" msgstr "我的笔记"
#: catalog/templates/_item_user_pieces.html:101 #: catalog/templates/_item_user_pieces.html:118
msgid "my review" msgid "my review"
msgstr "我的评论" msgstr "我的评论"
#: catalog/templates/_item_user_pieces.html:143 #: catalog/templates/_item_user_pieces.html:160
msgid "my collection" msgid "my collection"
msgstr "我的收藏单" msgstr "我的收藏单"
#: catalog/templates/_item_user_pieces.html:173 #: catalog/templates/_item_user_pieces.html:190
msgid "mark history" msgid "mark history"
msgstr "标记历史" msgstr "标记历史"
@ -1029,7 +1029,7 @@ msgstr "创建"
#: catalog/templates/catalog_edit.html:50 #: catalog/templates/catalog_edit.html:50
#: journal/templates/add_to_collection.html:35 #: journal/templates/add_to_collection.html:35
#: journal/templates/collection_edit.html:38 journal/templates/comment.html:69 #: journal/templates/collection_edit.html:38 journal/templates/comment.html:69
#: journal/templates/mark.html:147 journal/templates/note.html:66 #: journal/templates/mark.html:147 journal/templates/note.html:39
#: journal/templates/review_edit.html:39 journal/templates/tag_edit.html:51 #: journal/templates/review_edit.html:39 journal/templates/tag_edit.html:51
#: users/templates/users/account.html:43 users/templates/users/account.html:104 #: users/templates/users/account.html:43 users/templates/users/account.html:104
#: users/templates/users/preferences.html:168 #: users/templates/users/preferences.html:168
@ -1114,7 +1114,7 @@ msgstr "热门标签"
#: catalog/templates/item_review_list.html:50 common/templates/_sidebar.html:99 #: catalog/templates/item_review_list.html:50 common/templates/_sidebar.html:99
#: common/templates/_sidebar_anonymous.html:43 #: common/templates/_sidebar_anonymous.html:43
#: common/templates/_sidebar_anonymous.html:58 #: common/templates/_sidebar_anonymous.html:58
#: journal/templates/collection_items.html:8 journal/templates/posts.html:45 #: journal/templates/collection_items.html:8 journal/templates/posts.html:49
#: journal/templates/profile.html:109 journal/templates/profile.html:151 #: journal/templates/profile.html:109 journal/templates/profile.html:151
#: journal/templates/profile.html:187 #: journal/templates/profile.html:187
#: journal/templates/user_collection_list.html:51 #: journal/templates/user_collection_list.html:51
@ -1393,7 +1393,7 @@ msgstr "权限不足"
#: catalog/views_edit.py:200 journal/views/collection.py:229 #: catalog/views_edit.py:200 journal/views/collection.py:229
#: journal/views/collection.py:296 journal/views/common.py:81 #: journal/views/collection.py:296 journal/views/common.py:81
#: journal/views/mark.py:142 journal/views/post.py:41 journal/views/post.py:55 #: journal/views/mark.py:141 journal/views/post.py:41 journal/views/post.py:55
#: journal/views/review.py:93 journal/views/review.py:96 users/views.py:169 #: journal/views/review.py:93 journal/views/review.py:96 users/views.py:169
msgid "Invalid parameter" msgid "Invalid parameter"
msgstr "无效参数" msgstr "无效参数"
@ -1678,7 +1678,7 @@ msgstr "标题"
msgid "Content (Markdown)" msgid "Content (Markdown)"
msgstr "内容 Markdown格式" msgstr "内容 Markdown格式"
#: journal/forms.py:21 #: journal/forms.py:21 journal/views/note.py:26
msgid "Post to Fediverse" msgid "Post to Fediverse"
msgstr "发布到联邦宇宙" msgstr "发布到联邦宇宙"
@ -1722,31 +1722,96 @@ msgstr "备注"
#: journal/templates/action_open_post.html:14 #: journal/templates/action_open_post.html:14
#: journal/templates/action_open_post.html:16 #: journal/templates/action_open_post.html:16
#: journal/templates/collection_share.html:35 journal/templates/comment.html:35 #: journal/templates/collection_share.html:35 journal/templates/comment.html:35
#: journal/templates/mark.html:93 journal/templates/note.html:32 #: journal/templates/mark.html:93 journal/templates/tag_edit.html:42
#: journal/templates/tag_edit.html:42 journal/templates/wrapped_share.html:43 #: journal/templates/wrapped_share.html:43 users/templates/users/data.html:47
#: users/templates/users/data.html:47 users/templates/users/data.html:139 #: users/templates/users/data.html:139
#: users/templates/users/preferences.html:54 #: users/templates/users/preferences.html:54
msgid "Public" msgid "Public"
msgstr "公开" msgstr "公开"
#: journal/models/common.py:34 journal/templates/action_open_post.html:10 #: journal/models/common.py:34 journal/templates/action_open_post.html:10
#: journal/templates/collection_share.html:46 journal/templates/comment.html:42 #: journal/templates/collection_share.html:46 journal/templates/comment.html:42
#: journal/templates/mark.html:100 journal/templates/note.html:39 #: journal/templates/mark.html:100 journal/templates/wrapped_share.html:49
#: journal/templates/wrapped_share.html:49 users/templates/users/data.html:55 #: users/templates/users/data.html:55 users/templates/users/data.html:147
#: users/templates/users/data.html:147
#: users/templates/users/preferences.html:61 #: users/templates/users/preferences.html:61
msgid "Followers Only" msgid "Followers Only"
msgstr "仅关注者" msgstr "仅关注者"
#: journal/models/common.py:35 journal/templates/action_open_post.html:12 #: journal/models/common.py:35 journal/templates/action_open_post.html:12
#: journal/templates/collection_share.html:57 journal/templates/comment.html:49 #: journal/templates/collection_share.html:57 journal/templates/comment.html:49
#: journal/templates/mark.html:107 journal/templates/note.html:46 #: journal/templates/mark.html:107 journal/templates/wrapped_share.html:55
#: journal/templates/wrapped_share.html:55 users/templates/users/data.html:63 #: users/templates/users/data.html:63 users/templates/users/data.html:155
#: users/templates/users/data.html:155
#: users/templates/users/preferences.html:68 #: users/templates/users/preferences.html:68
msgid "Mentioned Only" msgid "Mentioned Only"
msgstr "自己和提到的人" msgstr "自己和提到的人"
#: journal/models/note.py:27
msgid "Page"
msgstr "页码"
#: journal/models/note.py:28
msgid "Chapter"
msgstr "章节"
#: journal/models/note.py:31
msgid "Part"
msgstr "分部"
#: journal/models/note.py:32
msgid "Episode"
msgstr "单集"
#: journal/models/note.py:33
msgid "Track"
msgstr "曲目"
#: journal/models/note.py:34
msgid "Cycle"
msgstr "周目"
#: journal/models/note.py:35
msgid "Timestamp"
msgstr "时间戳"
#: journal/models/note.py:36
msgid "Percentage"
msgstr "百分比"
#: journal/models/note.py:53
#, python-brace-format
msgid "Page {value}"
msgstr "第{value}页"
#: journal/models/note.py:54
#, python-brace-format
msgid "Chapter {value}"
msgstr "第{value}章"
#: journal/models/note.py:57
#, python-brace-format
msgid "Part {value}"
msgstr "第{value}部"
#: journal/models/note.py:58
#, python-brace-format
msgid "Episode {value}"
msgstr "第{value}集"
#: journal/models/note.py:59
#, python-brace-format
msgid "Track {value}"
msgstr "第{value}首"
#: journal/models/note.py:60
#, python-brace-format
msgid "Cycle {value}"
msgstr "{value}周目"
#: journal/models/renderers.py:94 mastodon/api.py:619 takahe/utils.py:540
#, python-brace-format
msgid "regarding {item_title}, may contain spoiler or triggering content"
msgstr "关于 {item_title},可能包含剧透或敏感内容"
#: journal/models/shelf.py:24 #: journal/models/shelf.py:24
msgid "WISHLIST" msgid "WISHLIST"
msgstr "" msgstr ""
@ -2323,7 +2388,6 @@ msgid "Tips: use &gt;!text!&lt; for spoilers; some instances may not be able to
msgstr "提示: 善用 &gt;!文字!&lt; 标记可隐藏剧透; 超过360字可能无法分享到联邦宇宙实例时间轴。" msgstr "提示: 善用 &gt;!文字!&lt; 标记可隐藏剧透; 超过360字可能无法分享到联邦宇宙实例时间轴。"
#: journal/templates/comment.html:62 journal/templates/mark.html:120 #: journal/templates/comment.html:62 journal/templates/mark.html:120
#: journal/templates/note.html:59
msgid "Repost to timeline" msgid "Repost to timeline"
msgstr "转发到时间轴" msgstr "转发到时间轴"
@ -2450,6 +2514,10 @@ msgstr ""
msgid "Note" msgid "Note"
msgstr "笔记" msgstr "笔记"
#: journal/templates/note.html:21
msgid "Note with empty content will be deleted, sure to continue?"
msgstr ""
#: journal/templates/profile.html:55 #: journal/templates/profile.html:55
msgid "calendar" msgid "calendar"
msgstr "日历" msgstr "日历"
@ -2458,7 +2526,7 @@ msgstr "日历"
msgid "annual summary" msgid "annual summary"
msgstr "年度小结" msgstr "年度小结"
#: journal/templates/profile.html:131 mastodon/api.py:747 #: journal/templates/profile.html:131 mastodon/api.py:678
msgid "collection" msgid "collection"
msgstr "收藏单" msgstr "收藏单"
@ -2590,7 +2658,7 @@ msgstr "找不到条目,请使用本站条目网址。"
msgid "Login required" msgid "Login required"
msgstr "登录后访问" msgstr "登录后访问"
#: journal/views/common.py:33 journal/views/mark.py:119 #: journal/views/common.py:33 journal/views/mark.py:118
msgid "Data saved but unable to repost to Fediverse instance." msgid "Data saved but unable to repost to Fediverse instance."
msgstr "数据已保存但未能转发到联邦实例。" msgstr "数据已保存但未能转发到联邦实例。"
@ -2602,15 +2670,35 @@ msgstr "正在重定向到你的联邦实例以重新认证。"
msgid "List not found." msgid "List not found."
msgstr "列表未找到" msgstr "列表未找到"
#: journal/views/mark.py:110 #: journal/views/mark.py:109
msgid "Content too long for your Fediverse instance." msgid "Content too long for your Fediverse instance."
msgstr "内容过长,超出了你的联邦实例的限制。" msgstr "内容过长,超出了你的联邦实例的限制。"
#: journal/views/mark.py:164 journal/views/mark.py:218 #: journal/views/mark.py:163 journal/views/note.py:94
#: journal/views/review.py:30 #: journal/views/review.py:30
msgid "Content not found" msgid "Content not found"
msgstr "内容未找到" msgstr "内容未找到"
#: journal/views/note.py:44
msgid "Progress (optional)"
msgstr "进度(选填)"
#: journal/views/note.py:46
msgid "Note Content"
msgstr "笔记内容"
#: journal/views/note.py:48
msgid "Content Warning (optional)"
msgstr "剧透或敏感内容提示(选填)"
#: journal/views/note.py:62
msgid "Progress Type (optional)"
msgstr "进度类型(选填)"
#: journal/views/note.py:102
msgid "Invalid form data"
msgstr "无效表单信息。"
#: journal/views/review.py:112 journal/views/review.py:126 #: journal/views/review.py:112 journal/views/review.py:126
#, python-brace-format #, python-brace-format
msgid "Reviews by {0}" msgid "Reviews by {0}"
@ -2649,16 +2737,11 @@ msgstr "标签已更新"
msgid "Summary posted to timeline." msgid "Summary posted to timeline."
msgstr "总结已发布到时间轴" msgstr "总结已发布到时间轴"
#: mastodon/api.py:600 takahe/utils.py:540 #: mastodon/api.py:683
#, python-brace-format
msgid "regarding {item_title}, may contain spoiler or triggering content"
msgstr "关于 {item_title},可能包含剧透或敏感内容"
#: mastodon/api.py:752
msgid "shared my collection" msgid "shared my collection"
msgstr "分享我的收藏单" msgstr "分享我的收藏单"
#: mastodon/api.py:755 #: mastodon/api.py:686
#, python-brace-format #, python-brace-format
msgid "shared {username}'s collection" msgid "shared {username}'s collection"
msgstr "分享 {username} 的收藏单" msgstr "分享 {username} 的收藏单"

View file

@ -6,7 +6,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-13 20:50-0400\n" "POT-Creation-Date: 2024-06-15 18:22-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -715,15 +715,15 @@ msgstr "我的短評和標籤"
msgid "my notes" msgid "my notes"
msgstr "我的筆記" msgstr "我的筆記"
#: catalog/templates/_item_user_pieces.html:101 #: catalog/templates/_item_user_pieces.html:118
msgid "my review" msgid "my review"
msgstr "我的評論" msgstr "我的評論"
#: catalog/templates/_item_user_pieces.html:143 #: catalog/templates/_item_user_pieces.html:160
msgid "my collection" msgid "my collection"
msgstr "我的收藏單" msgstr "我的收藏單"
#: catalog/templates/_item_user_pieces.html:173 #: catalog/templates/_item_user_pieces.html:190
msgid "mark history" msgid "mark history"
msgstr "標記歷史" msgstr "標記歷史"
@ -1029,7 +1029,7 @@ msgstr "創建"
#: catalog/templates/catalog_edit.html:50 #: catalog/templates/catalog_edit.html:50
#: journal/templates/add_to_collection.html:35 #: journal/templates/add_to_collection.html:35
#: journal/templates/collection_edit.html:38 journal/templates/comment.html:69 #: journal/templates/collection_edit.html:38 journal/templates/comment.html:69
#: journal/templates/mark.html:147 journal/templates/note.html:66 #: journal/templates/mark.html:147 journal/templates/note.html:39
#: journal/templates/review_edit.html:39 journal/templates/tag_edit.html:51 #: journal/templates/review_edit.html:39 journal/templates/tag_edit.html:51
#: users/templates/users/account.html:43 users/templates/users/account.html:104 #: users/templates/users/account.html:43 users/templates/users/account.html:104
#: users/templates/users/preferences.html:168 #: users/templates/users/preferences.html:168
@ -1114,7 +1114,7 @@ msgstr "熱門標籤"
#: catalog/templates/item_review_list.html:50 common/templates/_sidebar.html:99 #: catalog/templates/item_review_list.html:50 common/templates/_sidebar.html:99
#: common/templates/_sidebar_anonymous.html:43 #: common/templates/_sidebar_anonymous.html:43
#: common/templates/_sidebar_anonymous.html:58 #: common/templates/_sidebar_anonymous.html:58
#: journal/templates/collection_items.html:8 journal/templates/posts.html:45 #: journal/templates/collection_items.html:8 journal/templates/posts.html:49
#: journal/templates/profile.html:109 journal/templates/profile.html:151 #: journal/templates/profile.html:109 journal/templates/profile.html:151
#: journal/templates/profile.html:187 #: journal/templates/profile.html:187
#: journal/templates/user_collection_list.html:51 #: journal/templates/user_collection_list.html:51
@ -1393,7 +1393,7 @@ msgstr "權限不足"
#: catalog/views_edit.py:200 journal/views/collection.py:229 #: catalog/views_edit.py:200 journal/views/collection.py:229
#: journal/views/collection.py:296 journal/views/common.py:81 #: journal/views/collection.py:296 journal/views/common.py:81
#: journal/views/mark.py:142 journal/views/post.py:41 journal/views/post.py:55 #: journal/views/mark.py:141 journal/views/post.py:41 journal/views/post.py:55
#: journal/views/review.py:93 journal/views/review.py:96 users/views.py:169 #: journal/views/review.py:93 journal/views/review.py:96 users/views.py:169
msgid "Invalid parameter" msgid "Invalid parameter"
msgstr "無效參數" msgstr "無效參數"
@ -1678,7 +1678,7 @@ msgstr "標題"
msgid "Content (Markdown)" msgid "Content (Markdown)"
msgstr "內容 Markdown格式" msgstr "內容 Markdown格式"
#: journal/forms.py:21 #: journal/forms.py:21 journal/views/note.py:26
msgid "Post to Fediverse" msgid "Post to Fediverse"
msgstr "發佈到聯邦宇宙" msgstr "發佈到聯邦宇宙"
@ -1722,31 +1722,96 @@ msgstr "備註"
#: journal/templates/action_open_post.html:14 #: journal/templates/action_open_post.html:14
#: journal/templates/action_open_post.html:16 #: journal/templates/action_open_post.html:16
#: journal/templates/collection_share.html:35 journal/templates/comment.html:35 #: journal/templates/collection_share.html:35 journal/templates/comment.html:35
#: journal/templates/mark.html:93 journal/templates/note.html:32 #: journal/templates/mark.html:93 journal/templates/tag_edit.html:42
#: journal/templates/tag_edit.html:42 journal/templates/wrapped_share.html:43 #: journal/templates/wrapped_share.html:43 users/templates/users/data.html:47
#: users/templates/users/data.html:47 users/templates/users/data.html:139 #: users/templates/users/data.html:139
#: users/templates/users/preferences.html:54 #: users/templates/users/preferences.html:54
msgid "Public" msgid "Public"
msgstr "公開" msgstr "公開"
#: journal/models/common.py:34 journal/templates/action_open_post.html:10 #: journal/models/common.py:34 journal/templates/action_open_post.html:10
#: journal/templates/collection_share.html:46 journal/templates/comment.html:42 #: journal/templates/collection_share.html:46 journal/templates/comment.html:42
#: journal/templates/mark.html:100 journal/templates/note.html:39 #: journal/templates/mark.html:100 journal/templates/wrapped_share.html:49
#: journal/templates/wrapped_share.html:49 users/templates/users/data.html:55 #: users/templates/users/data.html:55 users/templates/users/data.html:147
#: users/templates/users/data.html:147
#: users/templates/users/preferences.html:61 #: users/templates/users/preferences.html:61
msgid "Followers Only" msgid "Followers Only"
msgstr "僅關注者" msgstr "僅關注者"
#: journal/models/common.py:35 journal/templates/action_open_post.html:12 #: journal/models/common.py:35 journal/templates/action_open_post.html:12
#: journal/templates/collection_share.html:57 journal/templates/comment.html:49 #: journal/templates/collection_share.html:57 journal/templates/comment.html:49
#: journal/templates/mark.html:107 journal/templates/note.html:46 #: journal/templates/mark.html:107 journal/templates/wrapped_share.html:55
#: journal/templates/wrapped_share.html:55 users/templates/users/data.html:63 #: users/templates/users/data.html:63 users/templates/users/data.html:155
#: users/templates/users/data.html:155
#: users/templates/users/preferences.html:68 #: users/templates/users/preferences.html:68
msgid "Mentioned Only" msgid "Mentioned Only"
msgstr "自己和提到的人" msgstr "自己和提到的人"
#: journal/models/note.py:27
msgid "Page"
msgstr "頁碼"
#: journal/models/note.py:28
msgid "Chapter"
msgstr "章節"
#: journal/models/note.py:31
msgid "Part"
msgstr "分部"
#: journal/models/note.py:32
msgid "Episode"
msgstr "單集"
#: journal/models/note.py:33
msgid "Track"
msgstr "曲目"
#: journal/models/note.py:34
msgid "Cycle"
msgstr "周目"
#: journal/models/note.py:35
msgid "Timestamp"
msgstr "時間戳"
#: journal/models/note.py:36
msgid "Percentage"
msgstr "百分比"
#: journal/models/note.py:53
#, python-brace-format
msgid "Page {value}"
msgstr "第{value}頁"
#: journal/models/note.py:54
#, python-brace-format
msgid "Chapter {value}"
msgstr "第{value}章"
#: journal/models/note.py:57
#, python-brace-format
msgid "Part {value}"
msgstr "第{value}部"
#: journal/models/note.py:58
#, python-brace-format
msgid "Episode {value}"
msgstr "第{value}集"
#: journal/models/note.py:59
#, python-brace-format
msgid "Track {value}"
msgstr "第{value}首"
#: journal/models/note.py:60
#, python-brace-format
msgid "Cycle {value}"
msgstr "{value}周目"
#: journal/models/renderers.py:94 mastodon/api.py:619 takahe/utils.py:540
#, python-brace-format
msgid "regarding {item_title}, may contain spoiler or triggering content"
msgstr "關於 {item_title},可能包含劇透或敏感內容"
#: journal/models/shelf.py:24 #: journal/models/shelf.py:24
msgid "WISHLIST" msgid "WISHLIST"
msgstr "" msgstr ""
@ -2323,7 +2388,6 @@ msgid "Tips: use &gt;!text!&lt; for spoilers; some instances may not be able to
msgstr "提示: 善用 &gt;!文字!&lt; 標記可隱藏劇透; 超過360字可能無法分享到聯邦宇宙實例時間軸。" msgstr "提示: 善用 &gt;!文字!&lt; 標記可隱藏劇透; 超過360字可能無法分享到聯邦宇宙實例時間軸。"
#: journal/templates/comment.html:62 journal/templates/mark.html:120 #: journal/templates/comment.html:62 journal/templates/mark.html:120
#: journal/templates/note.html:59
msgid "Repost to timeline" msgid "Repost to timeline"
msgstr "轉發到時間軸" msgstr "轉發到時間軸"
@ -2450,6 +2514,10 @@ msgstr ""
msgid "Note" msgid "Note"
msgstr "筆記" msgstr "筆記"
#: journal/templates/note.html:21
msgid "Note with empty content will be deleted, sure to continue?"
msgstr ""
#: journal/templates/profile.html:55 #: journal/templates/profile.html:55
msgid "calendar" msgid "calendar"
msgstr "日曆" msgstr "日曆"
@ -2458,7 +2526,7 @@ msgstr "日曆"
msgid "annual summary" msgid "annual summary"
msgstr "年度小結" msgstr "年度小結"
#: journal/templates/profile.html:131 mastodon/api.py:747 #: journal/templates/profile.html:131 mastodon/api.py:678
msgid "collection" msgid "collection"
msgstr "收藏單" msgstr "收藏單"
@ -2590,7 +2658,7 @@ msgstr "找不到條目,請使用本站條目網址。"
msgid "Login required" msgid "Login required"
msgstr "登錄後訪問" msgstr "登錄後訪問"
#: journal/views/common.py:33 journal/views/mark.py:119 #: journal/views/common.py:33 journal/views/mark.py:118
msgid "Data saved but unable to repost to Fediverse instance." msgid "Data saved but unable to repost to Fediverse instance."
msgstr "數據已保存但未能轉發到聯邦實例。" msgstr "數據已保存但未能轉發到聯邦實例。"
@ -2602,15 +2670,35 @@ msgstr "正在重定向到你的聯邦實例以重新認證。"
msgid "List not found." msgid "List not found."
msgstr "列表未找到" msgstr "列表未找到"
#: journal/views/mark.py:110 #: journal/views/mark.py:109
msgid "Content too long for your Fediverse instance." msgid "Content too long for your Fediverse instance."
msgstr "內容過長,超出了你的聯邦實例的限制。" msgstr "內容過長,超出了你的聯邦實例的限制。"
#: journal/views/mark.py:164 journal/views/mark.py:218 #: journal/views/mark.py:163 journal/views/note.py:94
#: journal/views/review.py:30 #: journal/views/review.py:30
msgid "Content not found" msgid "Content not found"
msgstr "內容未找到" msgstr "內容未找到"
#: journal/views/note.py:44
msgid "Progress (optional)"
msgstr "進度(選填)"
#: journal/views/note.py:46
msgid "Note Content"
msgstr "筆記內容"
#: journal/views/note.py:48
msgid "Content Warning (optional)"
msgstr "劇透或敏感內容提示(選填)"
#: journal/views/note.py:62
msgid "Progress Type (optional)"
msgstr "進度類型(選填)"
#: journal/views/note.py:102
msgid "Invalid form data"
msgstr "無效表單信息。"
#: journal/views/review.py:112 journal/views/review.py:126 #: journal/views/review.py:112 journal/views/review.py:126
#, python-brace-format #, python-brace-format
msgid "Reviews by {0}" msgid "Reviews by {0}"
@ -2649,16 +2737,11 @@ msgstr "標籤已更新"
msgid "Summary posted to timeline." msgid "Summary posted to timeline."
msgstr "總結已發佈到時間軸" msgstr "總結已發佈到時間軸"
#: mastodon/api.py:600 takahe/utils.py:540 #: mastodon/api.py:683
#, python-brace-format
msgid "regarding {item_title}, may contain spoiler or triggering content"
msgstr "關於 {item_title},可能包含劇透或敏感內容"
#: mastodon/api.py:752
msgid "shared my collection" msgid "shared my collection"
msgstr "分享我的收藏單" msgstr "分享我的收藏單"
#: mastodon/api.py:755 #: mastodon/api.py:686
#, python-brace-format #, python-brace-format
msgid "shared {username}'s collection" msgid "shared {username}'s collection"
msgstr "分享 {username} 的收藏單" msgstr "分享 {username} 的收藏單"