Theme update (#553)

* Start to update the theme

- Ligther colors
- No more border radius
- Buttons are now always colored
- Start to redesign the post page (according to the Figma mockups)

* Fix build script: it now recompiles everytime a scss file changed

* Make sure the article illustrations are not too big

* Make articles wider (70 characters)

* Better contrast between gray shades

* Various improvements

* Better mobile style

* New style for the footer

* Improve comment style

* Better responsiveness again

* Limit the size of the article cover

* Last details?

- Improve buttons on the media page
- Improve lists

* Pin the stdweb version that we use

It changed because I removed Cargo.lock to handle a merge conflict

I could have updated cargo web too, but it mean I should have re-built
the CI docker image and it was taking forever.

* Better contrast for links in the header of the article

* Add a basic privacy policy

* Remove "also"

* Fix a few issues

- Don't watch static/css in build.rs
- Another shade of white
- Remove useless margin rule for error messages
This commit is contained in:
Baptiste Gelez
2019-05-18 13:09:51 +01:00
committed by GitHub
parent c67f65e684
commit ad3a8b92d1
49 changed files with 1989 additions and 1174 deletions
+16 -7
View File
@@ -1,4 +1,5 @@
@use plume_models::CONFIG;
@use plume_models::instance::Instance;
@use template_utils::*;
@use routes::*;
@(ctx: BaseContext, title: String, head: Content, header: Content, content: Content)
@@ -78,13 +79,21 @@
@:content()
</main>
<footer>
<span>@concat!("Plume ", env!("CARGO_PKG_VERSION"))</span>
<a href="@uri!(instance::about)">@i18n!(ctx.1, "About this instance")</a>
<a href="https://github.com/Plume-org/Plume">@i18n!(ctx.1, "Source code")</a>
<a href="https://riot.im/app/#/room/#plume:disroot.org">@i18n!(ctx.1, "Matrix room")</a>
@if ctx.2.clone().map(|a| a.is_admin).unwrap_or(false) {
<a href="@uri!(instance::admin)">@i18n!(ctx.1, "Administration")</a>
}
<hr/>
<div>
<h3>@Instance::get_local().map(|i| i.name).unwrap_or_default()</h3>
<a href="@uri!(instance::about)">@i18n!(ctx.1, "About this instance")</a>
<a href="@uri!(instance::privacy)">@i18n!(ctx.1, "Privacy policy")</a>
@if ctx.2.clone().map(|a| a.is_admin).unwrap_or(false) {
<a href="@uri!(instance::admin)">@i18n!(ctx.1, "Administration")</a>
}
</div>
<div>
<h3>@concat!("Plume ", env!("CARGO_PKG_VERSION"))</h3>
<a href="https://docs.joinplu.me">@i18n!(ctx.1, "Documentation")</a>
<a href="https://github.com/Plume-org/Plume">@i18n!(ctx.1, "Source code")</a>
<a href="https://riot.im/app/#/room/#plume:disroot.org">@i18n!(ctx.1, "Matrix room")</a>
</div>
</footer>
<script src="@uri!(plume_static_files: file = "plume-front.js", _build_id = CACHE_NAME)"></script>
</body>
+2 -2
View File
@@ -55,7 +55,7 @@
<a href="@uri!(blogs::edit: name = &blog.fqn)" class="button">@i18n!(ctx.1, "Edit")</a>
}
</div>
<main class="user-summary">
<p>
@i18n!(ctx.1, "There's one author on this blog: ", "There are {0} authors on this blog: "; authors.len())
@@ -74,7 +74,7 @@
@if posts.is_empty() {
<p>@i18n!(ctx.1, "No posts to see here yet.")</p>
}
<div class="cards spaced">
<div class="cards">
@for article in posts {
@:post_card(ctx, article)
}
+2 -1
View File
@@ -9,6 +9,8 @@
<h1>@i18n!(ctx.1, "About {0}"; instance.name)</h1>
<section>
@Html(instance.short_description_html)
<p>@i18n!(ctx.1, "Runs Plume {0}"; env!("CARGO_PKG_VERSION"))</p>
</section>
<div class="banner">
<section class="stats">
@@ -27,7 +29,6 @@
<p><a href="@uri!(user::details: name = &admin.fqn)">@admin.name()</a><small>@@@admin.fqn</small></p>
</div>
</section>
<p>@i18n!(ctx.1, "Runs Plume {0}"; env!("CARGO_PKG_VERSION"))</p>
</div>
<section>
@Html(instance.long_description_html)
+1 -1
View File
@@ -24,7 +24,7 @@
])
}
<div class="cards spaced">
<div class="cards">
@for article in articles {
@:post_card(ctx, article)
}
+1 -1
View File
@@ -16,7 +16,7 @@
])
@if !articles.is_empty() {
<div class="cards spaced">
<div class="cards">
@for article in articles {
@:post_card(ctx, article)
}
+1 -1
View File
@@ -16,7 +16,7 @@
<div class="list">
@for instance in instances {
<div class="flex">
<div class="card flex compact">
<p class="grow">
<a href="https://@instance.public_domain">@instance.name</a>
<small>@instance.public_domain</small>
+1 -1
View File
@@ -25,7 +25,7 @@
])
}
<div class="cards spaced">
<div class="cards">
@for article in articles {
@:post_card(ctx, article)
}
+13
View File
@@ -0,0 +1,13 @@
@use templates::base;
@use template_utils::*;
@(ctx: BaseContext)
@:base(ctx, i18n!(ctx.1, "Privacy policy"), {}, {}, {
<h1>@i18n!(ctx.1, "Privacy policy")</h1>
<section>
<p>@i18n!(ctx.1, "If you are browsing this site as a visitor, no data about you is collected.")</p>
<p>@i18n!(ctx.1, "As a registered user, you have to provide your username (which does not have to be your real name), your functional email address and a password, in order to be able to log in, write articles and comment. The content you submit is stored until you delete it.")</p>
<p>@i18n!(ctx.1, "When you log in, we store two cookies, one to keep your session open, the second to prevent other people to act on your behalf. We don't store any other cookies.")</p>
</section>
})
+1 -1
View File
@@ -16,7 +16,7 @@
<div class="list">
@for user in users {
<div class="flex">
<div class="card flex compact">
@avatar(ctx.0, &user, Size::Small, false, ctx.1)
<p class="grow">
<a href="@uri!(user::details: name = &user.fqn)">@user.name()</a>
+11 -9
View File
@@ -24,15 +24,17 @@
</p>
<code>@media.markdown().unwrap_or_else(|_| SafeString::new(""))</code>
</div>
<div>
@if media.category() == MediaCategory::Image {
<form class="inline" method="post" action="@uri!(medias::set_avatar: id = media.id)">
<input class="button" type="submit" value="@i18n!(ctx.1, "Use as an avatar")">
</form>
}
<form class="inline" method="post" action="@uri!(medias::delete: id = media.id)">
<input class="button" type="submit" value="@i18n!(ctx.1, "Delete")">
</section>
<section>
@if media.category() == MediaCategory::Image {
<form method="post" action="@uri!(medias::set_avatar: id = media.id)">
<input class="button" type="submit" value="@i18n!(ctx.1, "Use as an avatar")">
</form>
</div>
}
<form method="post" action="@uri!(medias::delete: id = media.id)">
<input class="button destructive" type="submit" value="@i18n!(ctx.1, "Delete")">
</form>
</section>
})
+1 -1
View File
@@ -15,7 +15,7 @@
<p>@i18n!(ctx.1, "You don't have any media yet.")</p>
}
<div class="cards spaced">
<div class="cards">
@for media in medias {
<div class="card">
<div class="cover media-preview @media.category().to_string()"
+33 -26
View File
@@ -7,33 +7,40 @@
@if let Some(ref comm) = Some(&comment_tree.comment) {
@if let Ok(author) = comm.get_author(ctx.0) {
<div class="comment u-comment h-cite" id="comment-@comm.id">
<a class="author u-author h-card" href="@uri!(user::details: name = &author.fqn)">
@avatar(ctx.0, &author, Size::Small, true, ctx.1)
<span class="display-name p-name">@author.name()</span>
<small>@author.fqn</small>
</a>
@if let Some(ref ap_url) = comm.ap_url {
<a class="u-url" href="@ap_url"></a>
}
@if let Some(ref in_reply_to) = in_reply_to {
<a class="u-in-reply-to" href="@in_reply_to"></a>
}
<div class="text p-content">
@if comm.sensitive {
<details>
<summary>@comm.spoiler_text</summary>
<main class="content">
<header>
<a class="author u-author h-card" href="@uri!(user::details: name = &author.fqn)">
@avatar(ctx.0, &author, Size::Small, true, ctx.1)
<span class="display-name p-name">@author.name()</span>
<small>@author.fqn</small>
</a>
<p class="dt-published" datetime="@comm.creation_date.format("%F %T")">
@if let Some(ref ap_url) = comm.ap_url {
<a class="u-url" href="@ap_url">@comm.creation_date.format("%B %e, %Y %H:%M")</a>
}
</p>
@if let Some(ref in_reply_to) = in_reply_to {
<a class="u-in-reply-to hidden" href="@in_reply_to"></a>
}
</header>
<div class="text p-content">
@if comm.sensitive {
<details>
<summary>@comm.spoiler_text</summary>
}
@Html(&comm.content)
@if comm.sensitive {
</details>
}
</div>
<a class="button icon icon-message-circle" href="?responding_to=@comm.id">@i18n!(ctx.1, "Respond")</a>
@if ctx.2.clone().map(|u| u.id == author.id).unwrap_or(false) {
<form class="inline icon icon-trash" method="post" action="@uri!(comments::delete: blog = blog, slug = slug, id = comm.id)">
<input onclick="return confirm('@i18n!(ctx.1, "Are you sure?")')" type="submit" value="@i18n!(ctx.1, "Delete this comment")">
</form>
}
@Html(&comm.content)
@if comm.sensitive {
</details>
}
</div>
<a class="button icon icon-message-circle" href="?responding_to=@comm.id">@i18n!(ctx.1, "Respond")</a>
@if ctx.2.clone().map(|u| u.id == author.id).unwrap_or(false) {
<form class="inline icon icon-trash" method="post" action="@uri!(comments::delete: blog = blog, slug = slug, id = comm.id)">
<input onclick="return confirm('@i18n!(ctx.1, "Are you sure?")')" type="submit" value="@i18n!(ctx.1, "Delete this comment")">
</form>
}
</main>
@for res in &comment_tree.responses {
@:comment(ctx, res, comm.ap_url.as_ref().map(|u| &**u), blog, slug)
}
+1 -1
View File
@@ -7,7 +7,7 @@
@if !articles.is_empty() {
<div class="h-feed">
<h2><span class="p-name">@title</span> &mdash; <a href="@link">@i18n!(ctx.1, "View all")</a></h2>
<div class="cards spaced">
<div class="cards">
@for article in articles {
@:post_card(ctx, article)
}
+24 -26
View File
@@ -4,31 +4,29 @@
@(ctx: BaseContext, instance: Instance, n_users: i64, n_articles: i64)
<section class="spaced">
<div class="cards">
<div class="presentation card">
<h2>@i18n!(ctx.1, "What is Plume?")</h2>
<main>
<p>@i18n!(ctx.1, "Plume is a decentralized blogging engine.")</p>
<p>@i18n!(ctx.1, "Authors can manage multiple blogs, each as its own website.")</p>
<p>@i18n!(ctx.1, "Articles are also visible on other Plume instances, and you can interact with them directly from other platforms like Mastodon.")</p>
</main>
<a href="@uri!(user::new)">@i18n!(ctx.1, "Create your account")</a>
</div>
<div class="presentation card">
<h2>@i18n!(ctx.1, "About {0}"; instance.name)</h2>
<main>
@Html(instance.short_description_html)
<section class="stats">
<div>
<p>@Html(i18n!(ctx.1, "Home to <em>{0}</em> people"; n_users))</p>
</div>
<div>
<p>@Html(i18n!(ctx.1, "Who wrote <em>{0}</em> articles"; n_articles))</p>
</div>
</section>
</main>
<a href="@uri!(instance::about)">@i18n!(ctx.1, "Read the detailed rules")</a>
</div>
<section class="split">
<div class="presentation card">
<h2>@i18n!(ctx.1, "What is Plume?")</h2>
<main>
<p>@i18n!(ctx.1, "Plume is a decentralized blogging engine.")</p>
<p>@i18n!(ctx.1, "Authors can manage multiple blogs, each as its own website.")</p>
<p>@i18n!(ctx.1, "Articles are also visible on other Plume instances, and you can interact with them directly from other platforms like Mastodon.")</p>
</main>
<a href="@uri!(user::new)">@i18n!(ctx.1, "Create your account")</a>
</div>
<div class="presentation card">
<h2>@i18n!(ctx.1, "About {0}"; instance.name)</h2>
<main>
@Html(instance.short_description_html)
<section class="stats">
<div>
<p>@Html(i18n!(ctx.1, "Home to <em>{0}</em> people"; n_users))</p>
</div>
<div>
<p>@Html(i18n!(ctx.1, "Who wrote <em>{0}</em> articles"; n_articles))</p>
</div>
</section>
</main>
<a href="@uri!(instance::about)">@i18n!(ctx.1, "Read the detailed rules")</a>
</div>
</section>
+94 -72
View File
@@ -23,69 +23,53 @@
<a href="@uri!(blogs::details: name = &blog.fqn, page = _)">@blog.title</a>
}, {
<div class="h-entry">
<h1 class="article p-name">@article.title</h1>
<h2 class="article p-summary">@article.subtitle</h2>
<div class="article-info">
<span class="author">
@Html(i18n!(ctx.1, "Written by {0}"; format!("<a href=\"{}\">{}</a>",
uri!(user::details: name = &author.fqn),
escape(&author.name()))))
</span>
&mdash;
<span class="date dt-published" datetime="@article.creation_date.format("%F %T")">@article.creation_date.format("%B %e, %Y")</span><a class="u-url" href="@article.ap_url"></a>
@if ctx.2.clone().map(|u| u.id == author.id).unwrap_or(false) {
&mdash;
<a href="@uri!(posts::edit: blog = &blog.fqn, slug = &article.slug)">@i18n!(ctx.1, "Edit")</a>
&mdash;
<form class="inline" method="post" action="@uri!(posts::delete: blog_name = &blog.fqn, slug = &article.slug)">
<input onclick="return confirm('@i18n!(ctx.1, "Are you sure?")')" type="submit" value="@i18n!(ctx.1, "Delete this article")">
</form>
<header
class="article @if article.cover_id.is_some() { illustrated }"
@if article.cover_id.is_some() { style="background-image: url('@article.cover_url(ctx.0).unwrap_or_default()'" }
>
<div>
<h1 class="article p-name">@article.title</h1>
<div class="article-info">
<span class="author">
@Html(i18n!(ctx.1, "Written by {0}"; format!("<a href=\"{}\">{}</a>",
uri!(user::details: name = &author.fqn),
escape(&author.name()))))
</span>
&mdash;
<span class="date dt-published" datetime="@article.creation_date.format("%F %T")">@article.creation_date.format("%B %e, %Y")</span><a class="u-url" href="@article.ap_url"></a>
</div>
<h2 class="article p-summary">@article.subtitle</h2>
</div>
@if article.cover_id.is_some() {
<div class="shadow"></div>
<img class="u-photo hidden" src="@article.cover_url(ctx.0).unwrap_or_default()"/>
}
@if !article.published {
<span class="badge">@i18n!(ctx.1, "Draft")</span>
}
</div>
@if article.cover_id.is_some() {
<div class="cover" style="background-image: url('@Html(article.cover_url(ctx.0).unwrap_or_default())')"></div>
<img class="hidden u-photo" src="@article.cover_url(ctx.0).unwrap_or_default()"/>
}
</header>
<article class="e-content">
@Html(&article.content)
</article>
<div class="article-meta">
<p>
@if article.license.is_empty() {
@i18n!(ctx.1, "All rights reserved."; &article.license)
} else {
@i18n!(ctx.1, "This article is under the {0} license."; &article.license)
}
</p>
<ul class="tags">
@for tag in tags {
@if !tag.is_hashtag {
<li><a class="p-category" href="@uri!(tags::tag: name = &tag.tag, page = _)">@tag.tag</a></li>
} else {
<span class="hidden p-category">@tag.tag</span>
<section class="split">
<ul class="tags">
@for tag in tags {
@if !tag.is_hashtag {
<li><a class="p-category" href="@uri!(tags::tag: name = &tag.tag, page = _)">@tag.tag</a></li>
} else {
<span class="hidden p-category">@tag.tag</span>
}
}
}
</ul>
<div class="flex p-author h-card">
@avatar(ctx.0, &author, Size::Medium, true, ctx.1)
<div class="grow">
<h2 class="p-name">
<a href="@uri!(user::details: name = &author.fqn)">@author.name()</a>
<a rel="author" class="u-url" href="@author.ap_url"></a>
</h2>
<p>@Html(&author.summary_html)</h2>
</div>
@if !ctx.2.as_ref().map(|u| u.id == author.id).unwrap_or(false) {
<form action="@uri!(user::follow: name = &author.fqn)" method="POST">
<input type="submit" class="button" value="@if is_following {@i18n!(ctx.1, "Unsubscribe")} else {@i18n!(ctx.1, "Subscribe")}">
</form>
}
</div>
</ul>
<p class="right">
@if article.license.is_empty() {
@i18n!(ctx.1, "All rights reserved."; &article.license)
} else {
@i18n!(ctx.1, "This article is under the {0} license."; &article.license)
}
</p>
</section>
@if ctx.2.is_some() {
<div class="actions">
<section class="actions">
<form class="likes" action="@uri!(likes::create: blog = &blog.fqn, slug = &article.slug)" method="POST">
<p aria-label="@i18n!(ctx.1, "One like", "{0} likes"; n_likes)" title="@i18n!(ctx.1, "One like", "{0} likes"; n_likes)">
@n_likes
@@ -93,7 +77,7 @@
@if has_liked {
<button type="submit" class="action liked">@icon!("heart") @i18n!(ctx.1, "I don't like this anymore")</button>
} else {
} else {
<button type="submit" class="action">@icon!("heart") @i18n!(ctx.1, "Add yours")</button>
}
</form>
@@ -108,18 +92,19 @@
<button type="submit" class="action">@icon!("repeat") @i18n!(ctx.1, "Boost")</button>
}
</form>
</div>
</section>
} else {
<p class="center">@Html(i18n!(ctx.1, "{0}Log in{1}, or {2}use your Fediverse account{3} to interact with this article";
format!("<a href='{}'>", uri!(session::new: m = _)), "</a>",
format!("<a href='{}'>", uri!(posts::remote_interact: blog_name = &blog.fqn, slug = &article.slug)), "</a>"
))</p>
<div class="actions">
<p class="center">@Html(i18n!(ctx.1, "{0}Log in{1}, or {2}use your Fediverse account{3} to interact with this article";
format!("<a href='{}'>", uri!(session::new: m = _)), "</a>",
format!("<a href='{}'>", uri!(posts::remote_interact: blog_name = &blog.fqn, slug = &article.slug)), "</a>"
))
</p>
<section class="actions">
<div class="likes">
<p aria-label="@i18n!(ctx.1, "One like", "{0} likes"; n_likes)" title="@i18n!(ctx.1, "One like", "{0} likes"; n_likes)">
@n_likes
</p>
<a href="@uri!(posts::remote_interact: blog_name = &blog.fqn, slug = &article.slug)" class="action">@icon!("heart") @i18n!(ctx.1, "Add yours")</a>
<a href="@uri!(posts::remote_interact: blog_name = &blog.fqn, slug = &article.slug)" class="action">@icon!("heart") @i18n!(ctx.1, "Add yours")</a>
</div>
<div class="reshares">
@@ -128,9 +113,26 @@
</p>
<a href="@uri!(posts::remote_interact: blog_name = &blog.fqn, slug = &article.slug)" class="action">@icon!("repeat") @i18n!(ctx.1, "Boost")</a>
</div>
</div>
</section>
}
<div class="comments">
<section class="banner">
<div class="flex p-author h-card user">
@avatar(ctx.0, &author, Size::Medium, true, ctx.1)
<div class="grow">
<h2 class="p-name">
<a href="@uri!(user::details: name = &author.fqn)">@author.name()</a>
<a rel="author" class="u-url" href="@author.ap_url"></a>
</h2>
<p>@Html(&author.summary_html)</p>
</div>
@if !ctx.2.as_ref().map(|u| u.id == author.id).unwrap_or(false) {
<form action="@uri!(user::follow: name = &author.fqn)" method="POST">
<input type="submit" class="button" value="@if is_following {@i18n!(ctx.1, "Unsubscribe")} else {@i18n!(ctx.1, "Subscribe")}">
</form>
}
</div>
</section>
<section class="comments">
<h2>@i18n!(ctx.1, "Comments")</h2>
@if ctx.2.is_some() {
@@ -147,15 +149,35 @@
}
@if !comments.is_empty() {
<div class="list">
@for comm in comments {
@:comment(ctx, &comm, Some(&article.ap_url), &blog.fqn, &article.slug)
}
</div>
@for comm in comments {
@:comment(ctx, &comm, Some(&article.ap_url), &blog.fqn, &article.slug)
}
} else {
<p class="center">@i18n!(ctx.1, "No comments yet. Be the first to react!")</p>
}
</div>
</section>
</div>
</div>
@if ctx.2.clone().and_then(|u| article.is_author(ctx.0, u.id).ok()).unwrap_or(false) {
<aside class="bottom-bar">
<div>
<form class="inline" method="post" action="@uri!(posts::delete: blog_name = &blog.fqn, slug = &article.slug)">
<input class="button destructive" onclick="return confirm('@i18n!(ctx.1, "Are you sure?")')" type="submit" value="@i18n!(ctx.1, "Delete")">
</form>
</div>
<div>
@if !article.published {
<p>@i18n!(ctx.1, "This article is still a draft. Only you and other authors can see it.")</p>
} else {
<p>@i18n!(ctx.1, "Only you and other authors can edit this article.")</p>
}
</div>
<div>
@if !article.published {
<a class="button secondary" href="@uri!(posts::edit: blog = &blog.fqn, slug = &article.slug)">@i18n!(ctx.1, "Publish")</a>
}
<a class="button" href="@uri!(posts::edit: blog = &blog.fqn, slug = &article.slug)">@i18n!(ctx.1, "Edit")</a>
</div>
</aside>
}
})