show podcast episode comment on timeline
This commit is contained in:
parent
41479999b7
commit
adce46e3ab
20 changed files with 440 additions and 174 deletions
|
@ -1,3 +1,21 @@
|
|||
function create_player(audio) {
|
||||
window.player = new Shikwasa.Player({
|
||||
container: () => document.querySelector('.player'),
|
||||
preload: 'metadata',
|
||||
autoplay: true,
|
||||
themeColor: '#1190C0',
|
||||
fixed: {
|
||||
type: 'fixed',
|
||||
position: 'bottom'
|
||||
},
|
||||
audio: audio
|
||||
});
|
||||
// $('.shk-title').on('click', e=>{
|
||||
// window.location = "#";
|
||||
// });
|
||||
$('.footer').attr('style', 'margin-bottom: 120px !important');
|
||||
}
|
||||
|
||||
function catalog_init(context) {
|
||||
// readonly star rating of detail display section
|
||||
let ratingLabels = $(".grid__main .rating-star", context);
|
||||
|
@ -47,6 +65,38 @@ function catalog_init(context) {
|
|||
e.preventDefault();
|
||||
})
|
||||
$('.source-label__rss', context).parent().attr('title', 'Copy link here and subscribe in your podcast app');
|
||||
|
||||
$('.episode', context).on('click', e=>{
|
||||
e.preventDefault();
|
||||
var ele = e.target;
|
||||
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() {
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
<div>{% if item.duration %}
|
||||
{% trans '时长:' %}{{ item.duration|duration_format }}
|
||||
{% trans '时长:' %}{{ item.duration|duration_format:1000 }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>{% if item.genre %}
|
||||
|
|
|
@ -120,6 +120,28 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% block content %}
|
||||
{% if item.contents %}
|
||||
<div class="entity-desc" id="contents">
|
||||
<h5 class="entity-desc__title">{% trans '目录' %}</h5>
|
||||
<p class="entity-desc__content">{{ item.contents | linebreaksbr }}</p>
|
||||
<div class="entity-desc__unfold-button entity-desc__unfold-button--hidden">
|
||||
<a href="javascript:void(0);">展开全部</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if item.track_list %}
|
||||
<div class="entity-desc" id="description">
|
||||
<h5 class="entity-desc__title">{% trans '曲目' %}</h5>
|
||||
<p class="entity-desc__content">{{ item.track_list | linebreaksbr }}</p>
|
||||
<div class="entity-desc__unfold-button entity-desc__unfold-button--hidden">
|
||||
<a href="javascript:void(0);">展开全部</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
<div class="entity-marks">
|
||||
<h5 class="entity-marks__title">{% trans '标记' %}</h5>
|
||||
{% if mark_list %}
|
||||
|
@ -309,7 +331,5 @@
|
|||
</div>
|
||||
{% include "partial/_footer.html" %}
|
||||
</div>
|
||||
<div class="player">
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -70,7 +70,6 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
{% if item.last_editor and item.last_editor.preference.show_last_edit %}
|
||||
<div>{% trans '最近编辑者:' %}<a href="{% url 'journal:user_profile' item.last_editor.mastodon_username %}">{{ item.last_editor | default:"" }}</a></div>
|
||||
{% endif %}
|
||||
|
@ -84,104 +83,46 @@
|
|||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<div class="entity-desc" id="episodes">
|
||||
<h5 class="entity-desc__title">{% trans '近期节目' %}</h5>
|
||||
<br>
|
||||
<div hx-get="{% url 'catalog:episode_data' item.uuid %}" hx-trigger="load" hx-swap="outerHTML"></div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
<!-- class specific sidebar -->
|
||||
{% block sidebar %}
|
||||
<div class="aside-section-wrapper">
|
||||
<div class="action-panel" id="episodes">
|
||||
<div class="action-panel__label">{% trans '近期单集' %}</div>
|
||||
<div >
|
||||
{% for ep in item.recent_episodes %}
|
||||
<p>
|
||||
<a data-media="{{ ep.media_url }}" data-cover="{{ ep.cover_url|default:item.cover }}" class="episode" href="{{ep.url}}" data-uuid="{{ep.uuid}}">{{ ep.title }}</a>
|
||||
<a href="#" hx-get="{% url 'journal:comment' item.uuid ep.uuid %}" hx-target="body" hx-swap="beforeend"><span class="comment icon"></span></a>
|
||||
</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="action-panel__button-group action-panel__button-group--center">
|
||||
<button href="#" class="podlove-subscribe-button-primary action-panel__button">{% trans '订阅' %}</button>
|
||||
<button href="#" class="podlove-subscribe-button-primary action-panel__button">{% trans '用播客应用订阅' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.podcastData = {
|
||||
"title": "{{ item.title | escapejs }}",
|
||||
"subtitle": "",
|
||||
"description": "{{ item.brief | escapejs }}",
|
||||
"cover": "{{ item.cover.url | escapejs }}",
|
||||
"feeds": [
|
||||
{
|
||||
"type": "audio",
|
||||
"format": "mp3",
|
||||
"url": "{{ item.feed_url | escapejs }}",
|
||||
"variant": "high"
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
<script class="podlove-subscribe-button" src="https://cdn.podlove.org/subscribe-button/javascripts/app.js" data-json-data="podcastData" data-buttonid="primary" data-language="en" data-hide="true" data-color="#1190C0">
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
album = "{{ item.title|escapejs }}"
|
||||
artists = "{{ item.hosts|join:' / '|escapejs }}"
|
||||
function create_player(audio) {
|
||||
window.player = new Shikwasa.Player({
|
||||
container: () => document.querySelector('.player'),
|
||||
preload: 'metadata',
|
||||
autoplay: true,
|
||||
themeColor: '#1190C0',
|
||||
fixed: {
|
||||
type: 'fixed',
|
||||
position: 'bottom'
|
||||
},
|
||||
audio: audio
|
||||
});
|
||||
$('.shk-title').on('click', e=>{
|
||||
window.location = "#episodes";
|
||||
});
|
||||
$('.footer').attr('style', 'margin-bottom: 120px !important');
|
||||
}
|
||||
$(()=>{
|
||||
$('.episode').on('click', e=>{
|
||||
e.preventDefault();
|
||||
ele = e.target;
|
||||
title = $(ele).text();
|
||||
cover_url = $(ele).data('cover');
|
||||
media_url = $(ele).data('media');
|
||||
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: artists
|
||||
})
|
||||
} else {
|
||||
window.player.update({
|
||||
title: title,
|
||||
cover: cover_url,
|
||||
src: media_url,
|
||||
album: album,
|
||||
artist: artists
|
||||
})
|
||||
}
|
||||
window.player.play()
|
||||
});
|
||||
{% if focus_item %}
|
||||
loc = 1 * "{{request.GET.position|escapejs}}";
|
||||
window.current_item_uuid = "{{focus_item.uuid}}";
|
||||
create_player({
|
||||
title: "{{ focus_item.title | escapejs }}",
|
||||
cover: "{{ focus_item.cover_url | default:'' | escapejs }}",
|
||||
src: "{{ focus_item.media_url | escapejs }}",
|
||||
album: album,
|
||||
artist: artists
|
||||
})
|
||||
if (loc) window.player._initSeek = loc;
|
||||
{% endif %}
|
||||
});
|
||||
window.podcastData = {
|
||||
"title": "{{ item.title | escapejs }}",
|
||||
"subtitle": "",
|
||||
"description": "{{ item.brief | escapejs }}",
|
||||
"cover": "{{ item.cover.url | escapejs }}",
|
||||
"feeds": [{"type": "audio", "format": "mp3", "url": "{{ item.feed_url | escapejs }}", "variant": "high"}]
|
||||
}
|
||||
$(()=>{
|
||||
{% if focus_item %}
|
||||
var position = 1 * "{{request.GET.position|escapejs}}";
|
||||
window.current_item_uuid = "{{focus_item.uuid}}";
|
||||
create_player({
|
||||
title: "{{ focus_item.title | escapejs }}",
|
||||
cover: "{{ focus_item.cover_url | default:item.cover.url | escapejs }}",
|
||||
src: "{{ focus_item.media_url | escapejs }}",
|
||||
album: "{{ item.title|escapejs }}",
|
||||
artist: "{{ item.hosts|join:' / '|escapejs }}"
|
||||
})
|
||||
if (position) window.player._initSeek = position;
|
||||
{% endif %}
|
||||
});
|
||||
</script>
|
||||
<script class="podlove-subscribe-button" src="https://cdn.podlove.org/subscribe-button/javascripts/app.js" data-json-data="podcastData" data-buttonid="primary" data-language="en" data-hide="true" data-color="#1190C0"></script>
|
||||
{% endblock %}
|
||||
|
|
39
catalog/templates/podcast_episode_data.html
Normal file
39
catalog/templates/podcast_episode_data.html
Normal file
|
@ -0,0 +1,39 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load humanize %}
|
||||
{% for ep in episodes %}
|
||||
<details>
|
||||
<summary>
|
||||
<h6 class="entity-desc__title">
|
||||
<a
|
||||
data-media="{{ ep.media_url }}"
|
||||
data-cover="{{ ep.cover_url|default:item.cover.url }}"
|
||||
class="episode gg-play-button-o"
|
||||
href="{{ep.url}}"
|
||||
data-uuid="{{ep.uuid}}"
|
||||
data-title="{{ ep.title }}"
|
||||
data-album = "{{ item.title }}"
|
||||
data-hosts = "{{ item.hosts|join:' / ' }}"
|
||||
style="top:4px;margin-right: 8px;">
|
||||
</a>
|
||||
{{ ep.title }}
|
||||
<small style="color:lightgrey;">{{ ep.pub_date|date }}</small>
|
||||
</h6>
|
||||
</summary>
|
||||
<p> {{ ep.brief | linebreaksbr }} </p>
|
||||
</details>
|
||||
<div style="margin-bottom: 8px;margin-left: 32px;">
|
||||
{% if request.user.is_authenticated %}
|
||||
<a class="icon gg-comment" style="--ggs:0.5;margin-right: 10px;" title="评论单集" href="#" hx-get="{% url 'journal:comment' item.uuid ep.uuid %}" hx-target="body" hx-swap="beforeend"></a>
|
||||
{% endif %}
|
||||
<a class="icon gg-external" style="--ggs:0.6;" title="打开源网站" target="_blank" href="{{ep.link}}"></a>
|
||||
</div>
|
||||
|
||||
{% if forloop.last %}
|
||||
<button hx-get="{% url 'catalog:episode_data' item.uuid %}?last={{ ep.pub_date|date:'Y-m-d H:i:s.uO'|urlencode }}" hx-trigger="click" hx-swap="outerHTML">显示更多</button>
|
||||
{% endif %}
|
||||
|
||||
{% empty %}
|
||||
<div>{% trans '目前没有更多内容了' %}</div>
|
||||
{% endfor %}
|
|
@ -29,6 +29,7 @@ urlpatterns = [
|
|||
retrieve,
|
||||
name="retrieve",
|
||||
),
|
||||
path("podcast/<str:item_uuid>/episodes", episode_data, name="episode_data"),
|
||||
path("catalog/create/<str:item_model>", create, name="create"),
|
||||
re_path(
|
||||
r"^(?P<item_path>"
|
||||
|
|
|
@ -126,7 +126,6 @@ def create(request, item_model):
|
|||
form.instance.save()
|
||||
return redirect(form.instance.url)
|
||||
else:
|
||||
pprint(form.errors)
|
||||
return HttpResponseBadRequest(form.errors)
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
@ -155,7 +154,6 @@ def edit(request, item_path, item_uuid):
|
|||
form.instance.save()
|
||||
return redirect(form.instance.url)
|
||||
else:
|
||||
pprint(form.errors)
|
||||
return HttpResponseBadRequest(form.errors)
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
|
@ -221,6 +219,16 @@ def merge(request, item_path, item_uuid):
|
|||
return redirect(new_item.url)
|
||||
|
||||
|
||||
def episode_data(request, item_uuid):
|
||||
item = get_object_or_404(Item, uid=base62.decode(item_uuid))
|
||||
qs = item.episodes.all().order_by("-pub_date")
|
||||
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]}
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
def mark_list(request, item_path, item_uuid, following_only=False):
|
||||
item = get_object_or_404(Item, uid=base62.decode(item_uuid))
|
||||
|
|
|
@ -197,31 +197,145 @@ progress {
|
|||
}
|
||||
|
||||
.icon {
|
||||
color: lightgray;
|
||||
color: grey;
|
||||
}
|
||||
.icon:hover {
|
||||
color: #00a1cc;
|
||||
}
|
||||
.comment.icon {
|
||||
box-sizing: unset;
|
||||
position: absolute;
|
||||
margin-left: 2px;
|
||||
margin-top: 4px;
|
||||
width: 15px;
|
||||
height: 10px;
|
||||
border: solid 1px currentColor;
|
||||
border-radius: 2px;
|
||||
|
||||
input:invalid#position {
|
||||
border: red dashed 1px;
|
||||
}
|
||||
.comment.icon:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 3px;
|
||||
top: 8px;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
-webkit-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
background-color: white;
|
||||
border-bottom: solid 1px currentColor;
|
||||
border-right: solid 1px currentColor;
|
||||
|
||||
summary:focus {
|
||||
outline-style: none;
|
||||
}
|
||||
summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.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
|
||||
}
|
||||
|
||||
.gg-link {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
transform: rotate(-45deg) scale(var(--ggs,1));
|
||||
width: 8px;
|
||||
height: 2px;
|
||||
background: currentColor;
|
||||
border-radius: 4px
|
||||
}
|
||||
.gg-link::after,
|
||||
.gg-link::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
border-radius: 3px;
|
||||
width: 8px;
|
||||
height: 10px;
|
||||
border: 2px solid;
|
||||
top: -4px
|
||||
}
|
||||
.gg-link::before {
|
||||
border-right: 0;
|
||||
border-top-left-radius: 40px;
|
||||
border-bottom-left-radius: 40px;
|
||||
left: -6px
|
||||
}
|
||||
.gg-link::after {
|
||||
border-left: 0;
|
||||
border-top-right-radius: 40px;
|
||||
border-bottom-right-radius: 40px;
|
||||
right: -6px
|
||||
}
|
||||
|
||||
.gg-external {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
transform: scale(var(--ggs,1));
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
box-shadow:
|
||||
-2px 2px 0 0,
|
||||
-4px -4px 0 -2px,
|
||||
4px 4px 0 -2px;
|
||||
margin-left: -2px;
|
||||
margin-top: 1px
|
||||
}
|
||||
.gg-external::after,
|
||||
.gg-external::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
right: -4px
|
||||
}
|
||||
.gg-external::before {
|
||||
background: currentColor;
|
||||
transform: rotate(-45deg);
|
||||
width: 12px;
|
||||
height: 2px;
|
||||
top: 1px
|
||||
}
|
||||
.gg-external::after {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-right: 2px solid;
|
||||
border-top: 2px solid;
|
||||
top: -4px
|
||||
}
|
||||
|
||||
.gg-comment {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
transform: scale(var(--ggs,1));
|
||||
width: 20px;
|
||||
height: 16px;
|
||||
border: 2px solid;
|
||||
border-bottom: 0;
|
||||
box-shadow:
|
||||
-6px 8px 0 -6px,
|
||||
6px 8px 0 -6px
|
||||
}
|
||||
.gg-comment::after,
|
||||
.gg-comment::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
width: 8px
|
||||
}
|
||||
.gg-comment::before {
|
||||
border: 2px solid;
|
||||
border-top-color: transparent;
|
||||
border-bottom-left-radius: 20px;
|
||||
right: 4px;
|
||||
bottom: -6px;
|
||||
height: 6px
|
||||
}
|
||||
|
|
|
@ -258,7 +258,7 @@
|
|||
var $stars = $starSet.$stars;
|
||||
|
||||
if(newSize <= 1 || newSize > 200) {
|
||||
console.log('star size out of bounds');
|
||||
console.error('star size out of bounds');
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,3 +15,5 @@
|
|||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<div class="player">
|
||||
</div>
|
||||
|
|
|
@ -7,8 +7,10 @@ register = template.Library()
|
|||
|
||||
@register.filter(is_safe=True)
|
||||
@stringfilter
|
||||
def duration_format(value):
|
||||
duration = int(value)
|
||||
h = duration // 3600000
|
||||
m = duration % 3600000 // 60000
|
||||
return (f"{h}小时 " if h else "") + (f"{m}分钟" if m else "")
|
||||
def duration_format(value, unit):
|
||||
duration = int(value or 0) // int(unit or 1)
|
||||
h = duration // 3600
|
||||
m = duration % 3600 // 60
|
||||
s = duration % 60
|
||||
return f"{h}:{m:02}:{s:02}" if h else f"{m}:{s:02}"
|
||||
# return (f"{h}小时 " if h else "") + (f"{m}分钟" if m else "")
|
||||
|
|
|
@ -195,6 +195,15 @@ class Comment(Content):
|
|||
def html(self):
|
||||
return render_text(self.text)
|
||||
|
||||
@property
|
||||
def item_url(self):
|
||||
if self.focus_item:
|
||||
return self.focus_item.get_absolute_url_with_position(
|
||||
self.metadata["position"]
|
||||
)
|
||||
else:
|
||||
return self.item.url
|
||||
|
||||
@staticmethod
|
||||
def comment_item_by_user(item, user, text, visibility=0):
|
||||
comment = Comment.objects.filter(owner=user, item=item).first()
|
||||
|
|
|
@ -13,57 +13,52 @@
|
|||
<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 '分享' %} {{ item.title }}: {{ focus_item.title }}</span>
|
||||
<span class="add-to-list-modal__title">{% trans '评论单集' %} {{ item.title }}: {{ focus_item.title }}</span>
|
||||
<span class="add-to-list-modal__close-button modal-close" _="on click trigger closeModal">
|
||||
<span class="icon-cross">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<polygon
|
||||
points="20 2.61 17.39 0 10 7.39 2.61 0 0 2.61 7.39 10 0 17.39 2.61 20 10 12.61 17.39 20 20 17.39 12.61 10 20 2.61">
|
||||
</polygon>
|
||||
</svg>
|
||||
points="20 2.61 17.39 0 10 7.39 2.61 0 0 2.61 7.39 10 0 17.39 2.61 20 10 12.61 17.39 20 20 17.39 12.61 10 20 2.61"></polygon>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="add-to-list-modal__body">
|
||||
<form action="{% url 'journal:comment' item.uuid focus_item.uuid %}" method="post">
|
||||
{% csrf_token %}
|
||||
</div>
|
||||
<div class="add-to-list-modal__body">
|
||||
<form action="{% url 'journal:comment' item.uuid focus_item.uuid %}" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
<textarea name="text" cols="40" rows="10" placeholder="超过360字部分实例可能无法显示" maxlength="360" id="id_text">{% if comment.text %}{{ comment.text }}{% endif %}</textarea>
|
||||
<textarea name="text" cols="40" rows="10" placeholder="超过360字部分实例可能无法显示" maxlength="360" id="id_text">{% if comment.text %}{{ comment.text }}{% endif %}</textarea>
|
||||
|
||||
<div class="mark-modal__share-checkbox float-right">
|
||||
<label for="id_share_to_mastodon"><input type="checkbox" name="share_to_mastodon" id="id_share_to_mastodon" value="1" checked>分享到联邦网络</label>
|
||||
</div>
|
||||
|
||||
<div class="mark-modal__option">
|
||||
<div class="mark-modal__visibility-radio">
|
||||
<span>可见性:
|
||||
<ul id="id_visibility">
|
||||
<li><label for="id_visibility_0"><input type="radio" name="visibility" value="0" required="" id="id_visibility_0" {% if comment.visibility == 0 or not comment %}checked{% endif %}> 公开</label> </li>
|
||||
<li><label for="id_visibility_1"><input type="radio" name="visibility" value="1" required="" id="id_visibility_1" {% if comment.visibility == 1 %}checked{% endif %}> 仅关注者</label> </li>
|
||||
<li><label for="id_visibility_2"><input type="radio" name="visibility" value="2" required="" id="id_visibility_2" {% if comment.visibility == 2 %}checked{% endif %}> 仅自己</label> </li>
|
||||
</ul>
|
||||
</span>
|
||||
<div class="mark-modal__share-checkbox float-right">
|
||||
<label for="id_share_to_mastodon"><input type="checkbox" name="share_to_mastodon" id="id_share_to_mastodon" value="1" checked>分享到联邦网络</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mark-modal__confirm-button">
|
||||
<input type="submit" class="button float-right" value="保存">
|
||||
</div>
|
||||
|
||||
<style type="text/css">
|
||||
input:invalid#position {
|
||||
border: red dashed 1px;
|
||||
}
|
||||
</style>
|
||||
<div class="mark-modal__option">
|
||||
<div class="mark-modal__visibility-radio">
|
||||
<span>
|
||||
<label for="share_position"><input type="checkbox" name="share_position" value="1" id="share_position"> 分享播放位置: </label>
|
||||
<input type="input" name="position" id="position" value="00:00:00" class="html-duration-picker" pattern="[0-9]{2}:[0-9]{2}:[0-9]{2}" title="hh:mm:ss" style="display: none;">
|
||||
</span>
|
||||
<div class="mark-modal__option">
|
||||
<div class="mark-modal__visibility-radio">
|
||||
<span>可见性:
|
||||
<ul id="id_visibility">
|
||||
<li><label for="id_visibility_0"><input type="radio" name="visibility" value="0" required="" id="id_visibility_0" {% if comment.visibility == 0 or not comment %}checked{% endif %}> 公开</label> </li>
|
||||
<li><label for="id_visibility_1"><input type="radio" name="visibility" value="1" required="" id="id_visibility_1" {% if comment.visibility == 1 %}checked{% endif %}> 仅关注者</label> </li>
|
||||
<li><label for="id_visibility_2"><input type="radio" name="visibility" value="2" required="" id="id_visibility_2" {% if comment.visibility == 2 %}checked{% endif %}> 仅自己</label> </li>
|
||||
</ul>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="mark-modal__confirm-button">
|
||||
<input type="submit" class="button float-right" value="保存">
|
||||
</div>
|
||||
|
||||
<div class="mark-modal__option">
|
||||
<div class="mark-modal__visibility-radio">
|
||||
<span>
|
||||
<label for="share_position"><input type="checkbox" name="share_position" value="1" id="share_position"> 分享播放位置: </label>
|
||||
<input type="input" name="position" id="position" value="00:00:00" class="html-duration-picker" pattern="[0-9]{2}:[0-9]{2}:[0-9]{2}" title="hh:mm:ss" style="display: none;">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<img src="{{ item.cover|thumb:'normal' }}" alt="" class="entity-list__entity-img">
|
||||
</a>
|
||||
{% if not action.taken %}
|
||||
<a title="加入想读" class="entity-list__entity-action-icon" hx-get="{% url 'journal:mark' item.uuid %}?shelf_type=wishlist" class="edit" hx-target="body" hx-swap="beforeend">➕</a>
|
||||
<a title="标记" class="entity-list__entity-action-icon" hx-get="{% url 'journal:mark' item.uuid %}?shelf_type=wishlist" class="edit" hx-target="body" hx-swap="beforeend">➕</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -215,8 +215,9 @@ def comment(request, item_uuid, focus_item_uuid):
|
|||
pos = datetime.strptime(position, "%H:%M:%S")
|
||||
position = pos.hour * 3600 + pos.minute * 60 + pos.second
|
||||
except:
|
||||
raise
|
||||
position = 0
|
||||
if settings.DEBUG:
|
||||
raise
|
||||
position = None
|
||||
share_to_mastodon = bool(request.POST.get("share_to_mastodon", default=False))
|
||||
shared_link = None
|
||||
post_error = False
|
||||
|
@ -224,7 +225,7 @@ def comment(request, item_uuid, focus_item_uuid):
|
|||
shared_link = comment.metadata.get("shared_link") if comment else None
|
||||
status_id = get_status_id_by_url(shared_link)
|
||||
link = focus_item.get_absolute_url_with_position(position or None)
|
||||
status = f"分享{ItemCategory(item.category).label}《{item.title}》的《{focus_item.title}》\n{link}\n{text}"
|
||||
status = f"分享{ItemCategory(item.category).label}《{item.title}》的《{focus_item.title}》\n{link}\n\n{text}"
|
||||
spoiler, status = get_spoiler_text(status, item)
|
||||
try:
|
||||
response = post_toot(
|
||||
|
@ -241,11 +242,13 @@ def comment(request, item_uuid, focus_item_uuid):
|
|||
if "url" in j:
|
||||
shared_link = j["url"]
|
||||
except Exception as e:
|
||||
raise
|
||||
if settings.DEBUG:
|
||||
raise
|
||||
post_error = True
|
||||
if comment:
|
||||
comment.visibility = visibility
|
||||
comment.text = text
|
||||
comment.metadata["position"] = position
|
||||
if shared_link:
|
||||
comment.metadata["shared_link"] = shared_link
|
||||
comment.save()
|
||||
|
@ -256,7 +259,7 @@ def comment(request, item_uuid, focus_item_uuid):
|
|||
focus_item=focus_item,
|
||||
text=text,
|
||||
visibility=visibility,
|
||||
metadata={"shared_link": shared_link},
|
||||
metadata={"shared_link": shared_link, "position": position},
|
||||
)
|
||||
if post_error:
|
||||
return render_relogin(request)
|
||||
|
@ -514,7 +517,6 @@ def review_edit(request, item_uuid, review_uuid=None):
|
|||
if review
|
||||
else ReviewForm(request.POST)
|
||||
)
|
||||
print(form.instance.body)
|
||||
if form.is_valid():
|
||||
if not review:
|
||||
form.instance.owner = request.user
|
||||
|
|
|
@ -28,6 +28,7 @@ class ActivityTemplate(models.TextChoices):
|
|||
CreateCollection = "create_collection"
|
||||
LikeCollection = "like_collection"
|
||||
FeatureCollection = "feature_collection"
|
||||
CommentFocusItem = "comment_focus_item"
|
||||
|
||||
|
||||
class LocalActivity(models.Model, UserOwnedObjectMixin):
|
||||
|
@ -174,10 +175,24 @@ class LikeCollectionProcessor(DefaultActivityProcessor):
|
|||
|
||||
def updated(self):
|
||||
if isinstance(self.action_object.target, Collection):
|
||||
super().update()
|
||||
super().updated()
|
||||
|
||||
|
||||
@DataSignalManager.register
|
||||
class FeaturedCollectionProcessor(DefaultActivityProcessor):
|
||||
model = FeaturedCollection
|
||||
template = ActivityTemplate.FeatureCollection
|
||||
|
||||
|
||||
@DataSignalManager.register
|
||||
class CommentFocusItemProcessor(DefaultActivityProcessor):
|
||||
model = Comment
|
||||
template = ActivityTemplate.CommentFocusItem
|
||||
|
||||
def created(self):
|
||||
if self.action_object.focus_item:
|
||||
super().created()
|
||||
|
||||
def updated(self):
|
||||
if self.action_object.focus_item:
|
||||
super().updated()
|
||||
|
|
66
social/templates/activity/comment_focus_item.html
Normal file
66
social/templates/activity/comment_focus_item.html
Normal file
|
@ -0,0 +1,66 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
{% load admin_url %}
|
||||
{% load mastodon %}
|
||||
{% load oauth_token %}
|
||||
{% load truncate %}
|
||||
{% load thumb %}
|
||||
{% load prettydate %}
|
||||
{% load user_actions %}
|
||||
{% load duration %}
|
||||
|
||||
{% wish_item_action activity.action_object.item as action %}
|
||||
|
||||
<div class="entity-list__entity-img-wrapper">
|
||||
<a href="{{ activity.action_object.item.url }}">
|
||||
<img src="{{ activity.action_object.item.cover|thumb:'normal' }}" alt="" class="entity-list__entity-img" style="min-width:80px;max-width:80px">
|
||||
</a>
|
||||
{% if not action.taken %}
|
||||
<a title="标记" class="entity-list__entity-action-icon" hx-get="{% url 'journal:mark' activity.action_object.item.uuid %}?shelf_type=wishlist" class="edit" hx-target="body" hx-swap="beforeend">➕</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="entity-list__entity-text">
|
||||
<div class="collection-item-position-edit">
|
||||
<span class="entity-marks__mark-time">
|
||||
{% if activity.action_object.metadata.shared_link %}
|
||||
<a href="{{ activity.action_object.metadata.shared_link }}" action_object="_blank">
|
||||
<img src="{% static 'img/fediverse.svg' %}" style="filter: invert(93%) sepia(1%) saturate(53%) hue-rotate(314deg) brightness(95%) contrast(80%); vertical-align:text-top; max-width:14px; margin-right:6px;" />
|
||||
<span class="entity-marks__mark-time">{{ activity.action_object.created_time|prettydate }}</span>
|
||||
</a>
|
||||
{% else %}
|
||||
<a><span class="entity-marks__mark-time">{{ activity.action_object.created_time|prettydate }}</span></a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<span class="entity-list__entity-info" style="top:0px;">
|
||||
<a href="{% url 'journal:user_profile' activity.owner.mastodon_username %}">{{ activity.owner.display_name }}</a> 在听
|
||||
</span>
|
||||
<div class="entity-list__entity-title">
|
||||
<a href="{{ activity.action_object.item.url }}" class="entity-list__entity-link" style="font-weight:bold;">{{ activity.action_object.item.title }}
|
||||
{% if activity.action_object.item.year %}<small style="font-weight: lighter">({{ activity.action_object.item.year }})</small>{% endif %}
|
||||
</a>
|
||||
{% for res in activity.action_object.item.external_resources.all %}
|
||||
<a href="{{ res.url }}">
|
||||
<span class="source-label source-label__{{ res.site_name }}">{{ res.site_name.label }}</span>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<p class="entity-list__entity-brief">
|
||||
<a class="episode gg-play-button-o" href="#"
|
||||
data-uuid="{{ activity.action_object.focus_item.uuid }}"
|
||||
data-media = "{{ activity.action_object.focus_item.media_url }}"
|
||||
data-cover = "{{ activity.action_object.focus_item.cover_url|default:activity.action_object.item.cover.url }}"
|
||||
data-title = "{{ activity.action_object.focus_item.title }}"
|
||||
data-album = "{{ activity.action_object.item.title }}"
|
||||
data-hosts = "{{ activity.action_object.item.hosts|join:' / ' }}"
|
||||
data-position = "{{ activity.action_object.metadata.position | default:0 }}"
|
||||
style="top:4px;margin-right: 8px;"></a>
|
||||
<a href="{{ activity.action_object.item_url }}">{{ activity.action_object.focus_item.title }}</a>
|
||||
{% if activity.action_object.metadata.position %}
|
||||
<span style="color:lightgrey;">{{ activity.action_object.metadata.position|duration_format:1 }}</span>
|
||||
{% endif %}
|
||||
<br>
|
||||
{{ activity.action_object.html | safe }}
|
||||
</p>
|
||||
</div>
|
|
@ -16,7 +16,7 @@
|
|||
<img src="{{ activity.action_object.item.cover|thumb:'normal' }}" alt="" class="entity-list__entity-img" style="min-width:80px;max-width:80px">
|
||||
</a>
|
||||
{% if not action.taken %}
|
||||
<a title="加入想读" class="entity-list__entity-action-icon" hx-get="{% url 'journal:mark' activity.action_object.item.uuid %}?shelf_type=wishlist" class="edit" hx-target="body" hx-swap="beforeend">➕</a>
|
||||
<a title="标记" class="entity-list__entity-action-icon" hx-get="{% url 'journal:mark' activity.action_object.item.uuid %}?shelf_type=wishlist" class="edit" hx-target="body" hx-swap="beforeend">➕</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="entity-list__entity-text">
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<img src="{{ activity.action_object.item.cover|thumb:'normal' }}" alt="" class="entity-list__entity-img" style="min-width:80px;max-width:80px">
|
||||
</a>
|
||||
{% if not action.taken %}
|
||||
<a title="加入想读" class="entity-list__entity-action-icon" hx-get="{% url 'journal:mark' activity.action_object.item.uuid %}?shelf_type=wishlist" class="edit" hx-target="body" hx-swap="beforeend">➕</a>
|
||||
<a title="标记" class="entity-list__entity-action-icon" hx-get="{% url 'journal:mark' activity.action_object.item.uuid %}?shelf_type=wishlist" class="edit" hx-target="body" hx-swap="beforeend">➕</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="entity-list__entity-text">
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
{% include "common_libs.html" with jquery=0 %}
|
||||
<script src="{% static 'js/mastodon.js' %}"></script>
|
||||
<script src="{% static 'js/home.js' %}"></script>
|
||||
<script src=" https://cdn.jsdelivr.net/npm/shikwasa@2.2.0/dist/shikwasa.min.js "></script>
|
||||
<link href=" https://cdn.jsdelivr.net/npm/shikwasa@2.2.0/dist/style.min.css " rel="stylesheet"></link>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
Loading…
Add table
Reference in a new issue