forked from alemi/upub
feat(web): added reply button
not the best way to do it but it works!
This commit is contained in:
parent
102414e1a3
commit
f483ea14b4
4 changed files with 67 additions and 6 deletions
|
@ -84,6 +84,12 @@
|
||||||
font-size: .6em;
|
font-size: .6em;
|
||||||
color: var(--secondary);
|
color: var(--secondary);
|
||||||
}
|
}
|
||||||
|
span.nowrap {
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
hr.sep {
|
hr.sep {
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
|
|
|
@ -20,6 +20,9 @@ pub fn App() -> impl IntoView {
|
||||||
let user_tl = Timeline::new(format!("{URL_BASE}/users/{}/outbox/page", username.get().unwrap_or_default()));
|
let user_tl = Timeline::new(format!("{URL_BASE}/users/{}/outbox/page", username.get().unwrap_or_default()));
|
||||||
let context_tl = Timeline::new(format!("{URL_BASE}/outbox/page"));
|
let context_tl = Timeline::new(format!("{URL_BASE}/outbox/page"));
|
||||||
|
|
||||||
|
let reply_controls = ReplyControls::default();
|
||||||
|
provide_context(reply_controls);
|
||||||
|
|
||||||
let screen_width = window().screen().map(|x| x.avail_width().unwrap_or_default()).unwrap_or_default();
|
let screen_width = window().screen().map(|x| x.avail_width().unwrap_or_default()).unwrap_or_default();
|
||||||
|
|
||||||
let (menu, set_menu) = create_signal(screen_width <= 786);
|
let (menu, set_menu) = create_signal(screen_width <= 786);
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use crate::{prelude::*, URL_SENSITIVE};
|
use crate::{prelude::*, URL_SENSITIVE};
|
||||||
|
|
||||||
|
@ -127,7 +125,7 @@ pub fn Object(object: crate::Object) -> impl IntoView {
|
||||||
</Summary>
|
</Summary>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<div class="mt-s ml-1 rev">
|
<div class="mt-s ml-1 rev">
|
||||||
<ReplyButton n=comments />
|
<ReplyButton n=comments target=oid.clone() />
|
||||||
<LikeButton n=likes liked=already_liked target=oid.clone() author=author_id private=!public />
|
<LikeButton n=likes liked=already_liked target=oid.clone() author=author_id private=!public />
|
||||||
<RepostButton n=shares target=oid />
|
<RepostButton n=shares target=oid />
|
||||||
</div>
|
</div>
|
||||||
|
@ -199,14 +197,23 @@ pub fn LikeButton(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn ReplyButton(n: u64) -> impl IntoView {
|
pub fn ReplyButton(n: u64, target: String) -> impl IntoView {
|
||||||
|
let reply = use_context::<ReplyControls>().expect("missing reply controls context");
|
||||||
let comments = if n > 0 {
|
let comments = if n > 0 {
|
||||||
Some(view! { <small>{n}</small> })
|
Some(view! { <small>{n}</small> })
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
let _target = target.clone(); // TODO ughhhh useless clones
|
||||||
view! {
|
view! {
|
||||||
<span class="emoji ml-2">{comments}" 📨"</span>
|
<span
|
||||||
|
class:emoji=move || !reply.reply_to.get().map_or(false, |x| x == _target)
|
||||||
|
class="emoji-btn cursor ml-2"
|
||||||
|
on:click=move |_ev| reply.reply(&target)
|
||||||
|
>
|
||||||
|
{comments}
|
||||||
|
" 📨"
|
||||||
|
</span>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use apb::{ActivityMut, BaseMut, ObjectMut};
|
use apb::{ActivityMut, Base, BaseMut, Object, ObjectMut};
|
||||||
|
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
@ -14,9 +14,35 @@ pub fn Navigator() -> impl IntoView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct ReplyControls {
|
||||||
|
pub context: RwSignal<Option<String>>,
|
||||||
|
pub reply_to: RwSignal<Option<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReplyControls {
|
||||||
|
pub fn reply(&self, oid: &str) {
|
||||||
|
if let Some(obj) = CACHE.get(oid) {
|
||||||
|
self.context.set(obj.context().id());
|
||||||
|
self.reply_to.set(obj.id().map(|x| x.to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&self) {
|
||||||
|
self.context.set(None);
|
||||||
|
self.reply_to.set(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_author(post_id: &str) -> Option<crate::Object> {
|
||||||
|
let usr = CACHE.get(post_id)?.attributed_to().id()?;
|
||||||
|
CACHE.get(&usr)
|
||||||
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn PostBox(username: Signal<Option<String>>, advanced: WriteSignal<bool>) -> impl IntoView {
|
pub fn PostBox(username: Signal<Option<String>>, advanced: WriteSignal<bool>) -> impl IntoView {
|
||||||
let auth = use_context::<Auth>().expect("missing auth context");
|
let auth = use_context::<Auth>().expect("missing auth context");
|
||||||
|
let reply = use_context::<ReplyControls>().expect("missing reply controls");
|
||||||
let (posting, set_posting) = create_signal(false);
|
let (posting, set_posting) = create_signal(false);
|
||||||
let (error, set_error) = create_signal(None);
|
let (error, set_error) = create_signal(None);
|
||||||
let summary_ref: NodeRef<html::Input> = create_node_ref();
|
let summary_ref: NodeRef<html::Input> = create_node_ref();
|
||||||
|
@ -26,6 +52,23 @@ pub fn PostBox(username: Signal<Option<String>>, advanced: WriteSignal<bool>) ->
|
||||||
let private_ref: NodeRef<html::Input> = create_node_ref();
|
let private_ref: NodeRef<html::Input> = create_node_ref();
|
||||||
view! {
|
view! {
|
||||||
<div class:hidden=move || !auth.present() >
|
<div class:hidden=move || !auth.present() >
|
||||||
|
{move ||
|
||||||
|
reply.reply_to.get().map(|r| {
|
||||||
|
let actor_strip = post_author(&r).map(|x| view! { <ActorStrip object=x /> });
|
||||||
|
view! {
|
||||||
|
<span class="nowrap">
|
||||||
|
<span
|
||||||
|
class="cursor emoji emoji-btn mr-s ml-s"
|
||||||
|
on:click=move|_| reply.clear()
|
||||||
|
title={format!("> {r} | ctx: {}", reply.context.get().unwrap_or_default())}
|
||||||
|
>
|
||||||
|
"📨"
|
||||||
|
</span>
|
||||||
|
<small>{actor_strip}</small>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
<table class="align w-100">
|
<table class="align w-100">
|
||||||
<tr>
|
<tr>
|
||||||
<td><input type="checkbox" on:input=move |ev| advanced.set(event_target_checked(&ev)) title="advanced" /></td>
|
<td><input type="checkbox" on:input=move |ev| advanced.set(event_target_checked(&ev)) title="advanced" /></td>
|
||||||
|
@ -58,6 +101,8 @@ pub fn PostBox(username: Signal<Option<String>>, advanced: WriteSignal<bool>) ->
|
||||||
.set_object_type(Some(apb::ObjectType::Note))
|
.set_object_type(Some(apb::ObjectType::Note))
|
||||||
.set_summary(summary.as_deref())
|
.set_summary(summary.as_deref())
|
||||||
.set_content(Some(&content))
|
.set_content(Some(&content))
|
||||||
|
.set_context(apb::Node::maybe_link(reply.context.get()))
|
||||||
|
.set_in_reply_to(apb::Node::maybe_link(reply.reply_to.get()))
|
||||||
.set_to(to)
|
.set_to(to)
|
||||||
.set_cc(cc);
|
.set_cc(cc);
|
||||||
let target_url = format!("{URL_BASE}/users/test/outbox");
|
let target_url = format!("{URL_BASE}/users/test/outbox");
|
||||||
|
|
Loading…
Reference in a new issue