activity-pub implementation
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
use askama::Template;
|
||||
use chrono::Datelike;
|
||||
use application::ports::{
|
||||
ActivityFeedPageData, HtmlPageContext, HtmlRenderer, LoginPageData,
|
||||
ActivityFeedPageData, FollowingPageData, HtmlPageContext, HtmlRenderer, LoginPageData,
|
||||
NewReviewPageData, ProfilePageData, RegisterPageData, UsersPageData,
|
||||
};
|
||||
use domain::models::{
|
||||
DiaryEntry, FeedEntry, MonthActivity, MonthlyRating, UserStats, UserSummary, UserTrends,
|
||||
collections::Paginated,
|
||||
DiaryEntry, FeedEntry, MonthActivity, MonthlyRating, ReviewSource, UserStats, UserSummary,
|
||||
UserTrends, collections::Paginated,
|
||||
};
|
||||
|
||||
struct PageItem {
|
||||
@@ -110,6 +110,24 @@ struct ProfileTemplate<'a> {
|
||||
monthly_rating_rows: Vec<MonthlyRatingRow<'a>>,
|
||||
heatmap: Vec<HeatmapCell>,
|
||||
page_items: Vec<PageItem>,
|
||||
is_own_profile: bool,
|
||||
error: Option<String>,
|
||||
following_count: usize,
|
||||
}
|
||||
|
||||
struct RemoteActorData {
|
||||
handle: String,
|
||||
display_name: Option<String>,
|
||||
url: String,
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "following.html")]
|
||||
struct FollowingTemplate {
|
||||
ctx: HtmlPageContext,
|
||||
user_id: uuid::Uuid,
|
||||
actors: Vec<RemoteActorData>,
|
||||
error: Option<String>,
|
||||
}
|
||||
|
||||
struct HeatmapCell {
|
||||
@@ -276,6 +294,24 @@ impl HtmlRenderer for AskamaHtmlRenderer {
|
||||
monthly_rating_rows,
|
||||
heatmap,
|
||||
page_items: build_page_items(total_pages, current_page),
|
||||
is_own_profile: data.is_own_profile,
|
||||
error: data.error,
|
||||
following_count: data.following_count,
|
||||
}
|
||||
.render()
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
fn render_following_page(&self, data: FollowingPageData) -> Result<String, String> {
|
||||
FollowingTemplate {
|
||||
ctx: data.ctx,
|
||||
user_id: data.user_id,
|
||||
actors: data.actors.into_iter().map(|a| RemoteActorData {
|
||||
handle: a.handle,
|
||||
display_name: a.display_name,
|
||||
url: a.url,
|
||||
}).collect(),
|
||||
error: data.error,
|
||||
}
|
||||
.render()
|
||||
.map_err(|e| e.to_string())
|
||||
|
||||
@@ -27,6 +27,11 @@
|
||||
<div class="feed-meta">
|
||||
<a href="/users/{{ entry.review().user_id().value() }}" class="feed-user">{{ entry.user_display_name() }}</a>
|
||||
<span class="feed-time">{{ entry.review().watched_at().format("%b %-d, %Y") }}</span>
|
||||
{% match entry.review().source() %}
|
||||
{% when ReviewSource::Remote with { actor_url } %}
|
||||
<span class="remote-badge" title="{{ actor_url }}">↗ federated</span>
|
||||
{% when ReviewSource::Local %}
|
||||
{% endmatch %}
|
||||
</div>
|
||||
{% if ctx.is_current_user(entry.review().user_id().value()) %}
|
||||
<form method="post" action="/reviews/{{ entry.review().id().value() }}/delete" class="delete-form">
|
||||
|
||||
26
crates/adapters/template-askama/templates/following.html
Normal file
26
crates/adapters/template-askama/templates/following.html
Normal file
@@ -0,0 +1,26 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<h2>Following</h2>
|
||||
{% if let Some(err) = error %}
|
||||
<p class="error">{{ err }}</p>
|
||||
{% endif %}
|
||||
{% if actors.is_empty() %}
|
||||
<p>Not following anyone yet. Follow remote users from your <a href="/users/{{ user_id }}">profile page</a>.</p>
|
||||
{% else %}
|
||||
<ul class="following-list">
|
||||
{% for actor in actors %}
|
||||
<li class="following-item">
|
||||
<strong>{{ actor.handle }}</strong>
|
||||
{% if let Some(name) = actor.display_name %}
|
||||
({{ name }})
|
||||
{% endif %}
|
||||
<a href="{{ actor.url }}" target="_blank" rel="noopener noreferrer">{{ actor.url }}</a>
|
||||
<form method="POST" action="/users/{{ user_id }}/unfollow" style="display:inline">
|
||||
<input type="hidden" name="actor_url" value="{{ actor.url }}">
|
||||
<button type="submit">Unfollow</button>
|
||||
</form>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -24,6 +24,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if is_own_profile %}
|
||||
<section class="follow-section">
|
||||
<h3>Follow remote user</h3>
|
||||
<form method="POST" action="/users/{{ profile_user_id }}/follow">
|
||||
<input type="text" name="handle" placeholder="user@instance.example.com" required>
|
||||
<button type="submit">Follow</button>
|
||||
</form>
|
||||
{% if let Some(err) = error %}
|
||||
<p class="error">{{ err }}</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
<a href="/users/{{ profile_user_id }}/following-list">View following ({{ following_count }})</a>
|
||||
{% endif %}
|
||||
|
||||
<div class="view-tabs">
|
||||
<a href="?view=recent" class="view-tab {% if view == "recent" %}active{% endif %}">Recent</a>
|
||||
<a href="?view=ratings" class="view-tab {% if view == "ratings" %}active{% endif %}">Top Rated</a>
|
||||
|
||||
Reference in New Issue
Block a user