Edit tags in the middle of input
This commit is contained in:
parent
54379b98e0
commit
fe7edb93ad
9 changed files with 42 additions and 19 deletions
|
|
@ -11,7 +11,7 @@ readme = "README.md"
|
|||
members = [".", "migration"]
|
||||
|
||||
[dependencies]
|
||||
askama = "0.13.0"
|
||||
askama = { version = "0.13.0", features = ["serde_json"] }
|
||||
async-trait = "0.1.88"
|
||||
axum = { version = "0.8.3", features = ["multipart", "macros"] }
|
||||
axum-extra = { version = "0.10.1", features = ["form"] }
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ Sam's small image board. Currently a WIP.
|
|||
|
||||
## TODO
|
||||
|
||||
- [ ] Parent posts (including tags and stuff)
|
||||
- [ ] Edit tags in the middle of input
|
||||
- [ ] Pools
|
||||
- [ ] Video support
|
||||
- [ ] Cleanup/fixup background tasks
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ pub async fn get_router(db: DatabaseConnection, files_dir: &str) -> Result<Route
|
|||
post(upload).layer(DefaultBodyLimit::max(100_000_000)),
|
||||
)
|
||||
.route("/search_tags", post(search_tags))
|
||||
.route("/select_tag/{new_tag}", post(select_tag))
|
||||
.route("/select_tag", post(select_tag))
|
||||
.route("/posts", get(posts))
|
||||
.route("/posts/{page}", get(posts_page))
|
||||
.route("/view/{post_id}", get(view_post))
|
||||
|
|
|
|||
34
src/views.rs
34
src/views.rs
|
|
@ -235,20 +235,25 @@ struct SearchTag {
|
|||
#[template(path = "search_tags.html")]
|
||||
struct SearchTagsTemplate {
|
||||
tags: Vec<SearchTag>,
|
||||
selection_end: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub(crate) struct SearchTagsForm {
|
||||
tags: String,
|
||||
selection_end: usize,
|
||||
}
|
||||
|
||||
pub(crate) async fn search_tags(
|
||||
State(AppState { db, .. }): State<AppState>,
|
||||
Form(body): Form<SearchTagsForm>,
|
||||
) -> Result<impl IntoResponse, SameyError> {
|
||||
let tags = match body.tags.split(' ').last() {
|
||||
Some(tag) => {
|
||||
if tag.starts_with(NEGATIVE_PREFIX) {
|
||||
let tags = match body.tags[..body.selection_end].split(' ').last() {
|
||||
Some(mut tag) => {
|
||||
tag = tag.trim();
|
||||
if tag.is_empty() {
|
||||
vec![]
|
||||
} else if tag.starts_with(NEGATIVE_PREFIX) {
|
||||
if tag[NEGATIVE_PREFIX.len()..].starts_with(RATING_PREFIX) {
|
||||
[
|
||||
format!("{}u", RATING_PREFIX),
|
||||
|
|
@ -269,6 +274,7 @@ pub(crate) async fn search_tags(
|
|||
"LOWER(\"samey_tag\".\"name\") LIKE CONCAT(?, '%')",
|
||||
tag[NEGATIVE_PREFIX.len()..].to_lowercase(),
|
||||
))
|
||||
.limit(10)
|
||||
.all(&db)
|
||||
.await?
|
||||
.into_iter()
|
||||
|
|
@ -298,6 +304,7 @@ pub(crate) async fn search_tags(
|
|||
"LOWER(\"samey_tag\".\"name\") LIKE CONCAT(?, '%')",
|
||||
tag.to_lowercase(),
|
||||
))
|
||||
.limit(10)
|
||||
.all(&db)
|
||||
.await?
|
||||
.into_iter()
|
||||
|
|
@ -310,7 +317,13 @@ pub(crate) async fn search_tags(
|
|||
}
|
||||
_ => vec![],
|
||||
};
|
||||
Ok(Html(SearchTagsTemplate { tags }.render()?))
|
||||
Ok(Html(
|
||||
SearchTagsTemplate {
|
||||
tags,
|
||||
selection_end: body.selection_end,
|
||||
}
|
||||
.render()?,
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
|
|
@ -322,14 +335,15 @@ struct SelectTagTemplate {
|
|||
#[derive(Debug, Deserialize)]
|
||||
pub(crate) struct SelectTagForm {
|
||||
tags: String,
|
||||
new_tag: String,
|
||||
selection_end: usize,
|
||||
}
|
||||
|
||||
pub(crate) async fn select_tag(
|
||||
Path(new_tag): Path<String>,
|
||||
Form(body): Form<SelectTagForm>,
|
||||
) -> Result<impl IntoResponse, SameyError> {
|
||||
let mut tags = String::new();
|
||||
for (tag, _) in body.tags.split_whitespace().tuple_windows() {
|
||||
for (tag, _) in body.tags[..body.selection_end].split(' ').tuple_windows() {
|
||||
if !tags.is_empty() {
|
||||
tags.push(' ');
|
||||
}
|
||||
|
|
@ -338,7 +352,13 @@ pub(crate) async fn select_tag(
|
|||
if !tags.is_empty() {
|
||||
tags.push(' ');
|
||||
}
|
||||
tags.push_str(&new_tag);
|
||||
tags.push_str(&body.new_tag);
|
||||
for tag in body.tags[body.selection_end..].split(' ') {
|
||||
if !tags.is_empty() {
|
||||
tags.push(' ');
|
||||
}
|
||||
tags.push_str(tag);
|
||||
}
|
||||
tags.push(' ');
|
||||
Ok(Html(SelectTagTemplate { tags }.render()?))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,9 @@
|
|||
id="search-tags"
|
||||
name="tags"
|
||||
hx-post="/search_tags"
|
||||
hx-trigger="input changed delay:400ms"
|
||||
hx-trigger="input changed"
|
||||
hx-target="next .tags-autocomplete"
|
||||
hx-vals="js:{selection_end: event.target.selectionEnd}"
|
||||
hx-on::after-settle="this.focus(); this.setSelectionRange(-1, -1);"
|
||||
autofocus
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -16,10 +16,11 @@
|
|||
id="search-tags"
|
||||
name="tags"
|
||||
hx-post="/search_tags"
|
||||
hx-trigger="input changed delay:400ms"
|
||||
hx-trigger="input changed"
|
||||
hx-target="next .tags-autocomplete"
|
||||
hx-vals="js:{selection_end: event.target.selectionEnd}"
|
||||
hx-on::after-settle="this.focus(); this.setSelectionRange(-1, -1);"
|
||||
value="{% if let Some(tags_text) = tags_text %}{{ tags_text }}{% endif %}"
|
||||
autofocus
|
||||
/>
|
||||
<ul class="tags-autocomplete" id="search-autocomplete"></ul>
|
||||
<button type="submit">Search</button>
|
||||
|
|
@ -46,7 +47,7 @@
|
|||
title="{{ post.tags }}"
|
||||
>
|
||||
<img src="/files/{{ post.thumbnail }}" />
|
||||
<div>{{ post.rating }}</div>
|
||||
<div>{{ post.rating | upper }}</div>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
{% for tag in tags %}
|
||||
<li>
|
||||
<button
|
||||
hx-post="/select_tag/{{ tag.value }}"
|
||||
hx-post="/select_tag"
|
||||
hx-target="previous .tags"
|
||||
hx-swap="outerHTML"
|
||||
hx-vals='{"selection_end": {{ selection_end }}, "new_tag": {{ tag.value | json | safe }}}'
|
||||
>
|
||||
{{ tag.name }}
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
<input
|
||||
class="tags"
|
||||
type="text"
|
||||
id="search-tags"
|
||||
name="tags"
|
||||
hx-post="/search_tags"
|
||||
hx-trigger="input changed delay:400ms"
|
||||
hx-trigger="input changed"
|
||||
hx-target="next .tags-autocomplete"
|
||||
hx-swap="innerHTML"
|
||||
hx-vals="js:{selection_end: event.target.selectionEnd}"
|
||||
hx-on::after-settle="this.focus(); this.setSelectionRange(-1, -1);"
|
||||
value="{{ tags }}"
|
||||
autofocus
|
||||
/>
|
||||
<ul class="tags-autocomplete" hx-swap-oob="outerHTML:.tags-autocomplete"></ul>
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@
|
|||
<li>
|
||||
<a href="/view/{{ child_post.id }}" title="{{ child_post.tags }}">
|
||||
<img src="/files/{{ child_post.thumbnail }}" />
|
||||
<div>{{ child_post.rating }}</div>
|
||||
<div>{{ child_post.rating | upper }}</div>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue