add: post tags
This commit is contained in:
parent
efeb660de6
commit
8c3024cb40
13 changed files with 180 additions and 34 deletions
|
@ -56,6 +56,7 @@ version = "1.0.0"
|
||||||
"auth:label.relationship" = "Relationship"
|
"auth:label.relationship" = "Relationship"
|
||||||
"auth:label.joined_communities" = "Joined communities"
|
"auth:label.joined_communities" = "Joined communities"
|
||||||
"auth:label.recent_posts" = "Recent posts"
|
"auth:label.recent_posts" = "Recent posts"
|
||||||
|
"auth:label.recent_with_tag" = "Recent posts (with tag)"
|
||||||
"auth:label.before_you_view" = "Before you view"
|
"auth:label.before_you_view" = "Before you view"
|
||||||
"auth:label.private_profile" = "Private profile"
|
"auth:label.private_profile" = "Private profile"
|
||||||
"auth:label.private_profile_message" = "This profile is private, meaning you can only view it if they follow you."
|
"auth:label.private_profile_message" = "This profile is private, meaning you can only view it if they follow you."
|
||||||
|
|
|
@ -197,6 +197,7 @@
|
||||||
reactions_enabled: true,
|
reactions_enabled: true,
|
||||||
is_nsfw: false,
|
is_nsfw: false,
|
||||||
content_warning: "",
|
content_warning: "",
|
||||||
|
tags: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
window.BLANK_INITIAL_SETTINGS = JSON.stringify(
|
window.BLANK_INITIAL_SETTINGS = JSON.stringify(
|
||||||
|
@ -238,12 +239,28 @@
|
||||||
window.POST_INITIAL_SETTINGS.content_warning,
|
window.POST_INITIAL_SETTINGS.content_warning,
|
||||||
"textarea",
|
"textarea",
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
["tags", "Tags"],
|
||||||
|
window.POST_INITIAL_SETTINGS.tags,
|
||||||
|
"input",
|
||||||
|
{
|
||||||
|
embed_html:
|
||||||
|
'<span class="fade">Tags should be separated by a comma.</span>',
|
||||||
|
},
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
trigger("ui::generate_settings_ui", [
|
trigger("ui::generate_settings_ui", [
|
||||||
document.getElementById("post_options"),
|
document.getElementById("post_options"),
|
||||||
settings_fields,
|
settings_fields,
|
||||||
window.POST_INITIAL_SETTINGS,
|
window.POST_INITIAL_SETTINGS,
|
||||||
|
{
|
||||||
|
tags: (new_tags) => {
|
||||||
|
window.POST_INITIAL_SETTINGS.tags = new_tags
|
||||||
|
.split(",")
|
||||||
|
.map((t) => t.trim());
|
||||||
|
},
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
}, 250);
|
}, 250);
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -253,6 +253,16 @@ and show_community and community.id != config.town_square or question %}
|
||||||
</span>
|
</span>
|
||||||
</details>
|
</details>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="flex flex-wrap gap-2 fade">
|
||||||
|
{% for tag in post.context.tags %}
|
||||||
|
<a
|
||||||
|
href="/@{{ owner.username }}?tag={{ tag }}"
|
||||||
|
class="flush fade"
|
||||||
|
>#{{ tag }}</a
|
||||||
|
>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -168,6 +168,15 @@
|
||||||
settings.content_warning,
|
settings.content_warning,
|
||||||
"textarea",
|
"textarea",
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
["tags", "Tags"],
|
||||||
|
settings.tags.join(", "),
|
||||||
|
"input",
|
||||||
|
{
|
||||||
|
embed_html:
|
||||||
|
'<span class="fade">Tags should be separated by a comma.</span>',
|
||||||
|
},
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
if (can_manage_pins) {
|
if (can_manage_pins) {
|
||||||
|
@ -186,7 +195,13 @@
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.generate_settings_ui(element, settings_fields, settings);
|
ui.generate_settings_ui(element, settings_fields, settings, {
|
||||||
|
tags: (new_tags) => {
|
||||||
|
settings.tags = new_tags
|
||||||
|
.split(",")
|
||||||
|
.map((t) => t.trim());
|
||||||
|
},
|
||||||
|
});
|
||||||
}, 250);
|
}, 250);
|
||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,7 +5,7 @@ profile.settings.allow_anonymous_questions) %}
|
||||||
{{ components::create_question_form(receiver=profile.id,
|
{{ components::create_question_form(receiver=profile.id,
|
||||||
header=profile.settings.motivational_header) }}
|
header=profile.settings.motivational_header) }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %} {% if pinned|length != 0 %}
|
{% endif %} {% if not tag and pinned|length != 0 %}
|
||||||
<div class="card-nest">
|
<div class="card-nest">
|
||||||
<div class="card small flex gap-2 items-center">
|
<div class="card small flex gap-2 items-center">
|
||||||
{{ icon "pin" }}
|
{{ icon "pin" }}
|
||||||
|
@ -29,8 +29,11 @@ profile.settings.allow_anonymous_questions) %}
|
||||||
|
|
||||||
<div class="card-nest">
|
<div class="card-nest">
|
||||||
<div class="card small flex gap-2 items-center">
|
<div class="card small flex gap-2 items-center">
|
||||||
{{ icon "clock" }}
|
{% if not tag %} {{ icon "clock" }}
|
||||||
<span>{{ text "auth:label.recent_posts" }}</span>
|
<span>{{ text "auth:label.recent_posts" }}</span>
|
||||||
|
{% else %} {{ icon "tag" }}
|
||||||
|
<span>{{ text "auth:label.recent_with_tag" }}: <b>{{ tag }}</b></span>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card flex flex-col gap-4">
|
<div class="card flex flex-col gap-4">
|
||||||
|
|
|
@ -858,7 +858,7 @@ media_theme_pref();
|
||||||
<label for="${option.key}"><b>${option.label.replaceAll("_", " ")}</b></label>
|
<label for="${option.key}"><b>${option.label.replaceAll("_", " ")}</b></label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card flex flex-col gap-2">
|
||||||
<${option.input_element_type || "input"}
|
<${option.input_element_type || "input"}
|
||||||
type="text"
|
type="text"
|
||||||
onchange="window.set_setting_field('${option.key}', event.target.value)"
|
onchange="window.set_setting_field('${option.key}', event.target.value)"
|
||||||
|
@ -867,6 +867,8 @@ media_theme_pref();
|
||||||
id="${option.key}"
|
id="${option.key}"
|
||||||
${option.input_element_type === "input" ? `value="${option.value}"/>` : ">"}
|
${option.input_element_type === "input" ? `value="${option.value}"/>` : ">"}
|
||||||
${option.input_element_type === "textarea" ? `${option.value}</textarea>` : ""}
|
${option.input_element_type === "textarea" ? `${option.value}</textarea>` : ""}
|
||||||
|
|
||||||
|
${(option.attributes || { embed_html: "" }).embed_html}
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
});
|
});
|
||||||
|
@ -885,7 +887,7 @@ ${option.input_element_type === "textarea" ? `${option.value}</textarea>` : ""}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.set_setting_field = (key, value) => {
|
window.set_setting_field = (key, value) => {
|
||||||
if (settings_ref) {
|
if (settings_ref && !key_map[key]) {
|
||||||
settings_ref[key] = value;
|
settings_ref[key] = value;
|
||||||
} else {
|
} else {
|
||||||
key_map[key](value);
|
key_map[key](value);
|
||||||
|
|
|
@ -140,6 +140,8 @@ pub struct ProfileQuery {
|
||||||
pub page: usize,
|
pub page: usize,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub warning: bool,
|
pub warning: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub tag: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
|
|
@ -198,40 +198,66 @@ pub async fn posts_request(
|
||||||
Vec::new()
|
Vec::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
let posts = match data
|
let posts = if props.tag.is_empty() {
|
||||||
.0
|
match data
|
||||||
.get_posts_by_user(other_user.id, 12, props.page, &user)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(p) => match data
|
|
||||||
.0
|
.0
|
||||||
.fill_posts_with_community(
|
.get_posts_by_user(other_user.id, 12, props.page, &user)
|
||||||
p,
|
|
||||||
if let Some(ref ua) = user { ua.id } else { 0 },
|
|
||||||
&ignore_users,
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(p) => p,
|
Ok(p) => match data
|
||||||
|
.0
|
||||||
|
.fill_posts_with_community(
|
||||||
|
p,
|
||||||
|
if let Some(ref ua) = user { ua.id } else { 0 },
|
||||||
|
&ignore_users,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
||||||
|
},
|
||||||
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
||||||
},
|
}
|
||||||
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
} else {
|
||||||
|
match data
|
||||||
|
.0
|
||||||
|
.get_posts_by_user_tag(other_user.id, &props.tag, 12, props.page, &user)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(p) => match data
|
||||||
|
.0
|
||||||
|
.fill_posts_with_community(
|
||||||
|
p,
|
||||||
|
if let Some(ref ua) = user { ua.id } else { 0 },
|
||||||
|
&ignore_users,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
||||||
|
},
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let pinned = match data.0.get_pinned_posts_by_user(other_user.id).await {
|
let pinned = if props.tag.is_empty() {
|
||||||
Ok(p) => match data
|
match data.0.get_pinned_posts_by_user(other_user.id).await {
|
||||||
.0
|
Ok(p) => match data
|
||||||
.fill_posts_with_community(
|
.0
|
||||||
p,
|
.fill_posts_with_community(
|
||||||
if let Some(ref ua) = user { ua.id } else { 0 },
|
p,
|
||||||
&ignore_users,
|
if let Some(ref ua) = user { ua.id } else { 0 },
|
||||||
)
|
&ignore_users,
|
||||||
.await
|
)
|
||||||
{
|
.await
|
||||||
Ok(p) => p,
|
{
|
||||||
|
Ok(p) => Some(p),
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
||||||
|
},
|
||||||
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
||||||
},
|
}
|
||||||
Err(e) => return Err(Html(render_error(e, &jar, &data, &user).await)),
|
} else {
|
||||||
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let communities = match data.0.get_memberships_by_owner(other_user.id).await {
|
let communities = match data.0.get_memberships_by_owner(other_user.id).await {
|
||||||
|
@ -282,6 +308,7 @@ pub async fn posts_request(
|
||||||
context.insert("posts", &posts);
|
context.insert("posts", &posts);
|
||||||
context.insert("pinned", &pinned);
|
context.insert("pinned", &pinned);
|
||||||
context.insert("page", &props.page);
|
context.insert("page", &props.page);
|
||||||
|
context.insert("tag", &props.tag);
|
||||||
profile_context(
|
profile_context(
|
||||||
&mut context,
|
&mut context,
|
||||||
&user,
|
&user,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
CREATE TABLE IF NOT EXISTS requests (
|
CREATE TABLE IF NOT EXISTS requests (
|
||||||
id BIGINT NOT NULL PRIMARY KEY,
|
id BIGINT NOT NULL,
|
||||||
created BIGINT NOT NULL,
|
created BIGINT NOT NULL,
|
||||||
owner BIGINT NOT NULL,
|
owner BIGINT NOT NULL,
|
||||||
action_type TEXT NOT NULL,
|
action_type TEXT NOT NULL,
|
||||||
|
|
|
@ -298,6 +298,68 @@ impl DataManager {
|
||||||
Ok(res.unwrap())
|
Ok(res.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get all posts from the given user with the given tag (from most recent).
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `id` - the ID of the user the requested posts belong to
|
||||||
|
/// * `tag` - the tag to filter by
|
||||||
|
/// * `batch` - the limit of posts in each page
|
||||||
|
/// * `page` - the page number
|
||||||
|
pub async fn get_posts_by_user_tag(
|
||||||
|
&self,
|
||||||
|
id: usize,
|
||||||
|
tag: &str,
|
||||||
|
batch: usize,
|
||||||
|
page: usize,
|
||||||
|
user: &Option<User>,
|
||||||
|
) -> Result<Vec<Post>> {
|
||||||
|
let other_user = self.get_user_by_id(id).await?;
|
||||||
|
|
||||||
|
let conn = match self.connect().await {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||||
|
};
|
||||||
|
|
||||||
|
// check if we should hide nsfw posts
|
||||||
|
let mut hide_nsfw: bool = true;
|
||||||
|
|
||||||
|
if let Some(ua) = user {
|
||||||
|
if ua.id == other_user.id {
|
||||||
|
hide_nsfw = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if other_user.settings.private_profile {
|
||||||
|
hide_nsfw = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
let res = query_rows!(
|
||||||
|
&conn,
|
||||||
|
&format!(
|
||||||
|
"SELECT * FROM posts WHERE owner = $1 AND context::json->>'tags' LIKE $2 {} ORDER BY created DESC LIMIT $3 OFFSET $4",
|
||||||
|
if hide_nsfw {
|
||||||
|
"AND NOT context LIKE '%\"is_nsfw\":true%'"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
),
|
||||||
|
params![
|
||||||
|
&(id as i64),
|
||||||
|
&format!("%\"{tag}\"%"),
|
||||||
|
&(batch as i64),
|
||||||
|
&((page * batch) as i64)
|
||||||
|
],
|
||||||
|
|x| { Self::get_post_from_row(x) }
|
||||||
|
);
|
||||||
|
|
||||||
|
if res.is_err() {
|
||||||
|
return Err(Error::GeneralNotFound("post".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(res.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
/// Get all posts from the given community (from most recent).
|
/// Get all posts from the given community (from most recent).
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
|
|
|
@ -130,7 +130,9 @@ impl DataManager {
|
||||||
.get_request_by_id_linked_asset(id, linked_asset)
|
.get_request_by_id_linked_asset(id, linked_asset)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if !force && user.id != y.owner && !user.permissions.check(FinePermission::MANAGE_REQUESTS)
|
if !force
|
||||||
|
&& (user.id != y.owner && user.id != y.linked_asset)
|
||||||
|
&& !user.permissions.check(FinePermission::MANAGE_REQUESTS)
|
||||||
{
|
{
|
||||||
return Err(Error::NotAllowed);
|
return Err(Error::NotAllowed);
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,6 +174,8 @@ pub struct PostContext {
|
||||||
pub reactions_enabled: bool,
|
pub reactions_enabled: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub content_warning: String,
|
pub content_warning: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub tags: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_comments_enabled() -> bool {
|
fn default_comments_enabled() -> bool {
|
||||||
|
@ -201,6 +203,7 @@ impl Default for PostContext {
|
||||||
answering: 0,
|
answering: 0,
|
||||||
reactions_enabled: default_reactions_enabled(),
|
reactions_enabled: default_reactions_enabled(),
|
||||||
content_warning: String::new(),
|
content_warning: String::new(),
|
||||||
|
tags: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2
sql_changes/requests_pkey.sql
Normal file
2
sql_changes/requests_pkey.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE requests
|
||||||
|
DROP CONSTRAINT requests_pkey;
|
Loading…
Add table
Add a link
Reference in a new issue