chore: re-add missing actions #17
3
assets/icons/input.svg
Normal file
3
assets/icons/input.svg
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||||
|
<path d="M11.75 6.75H19.25C20.3546 6.75 21.25 7.64543 21.25 8.75V15.25C21.25 16.3546 20.3546 17.25 19.25 17.25H11.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M5.75 6.75H4.75C3.64543 6.75 2.75 7.64543 2.75 8.75V15.25C2.75 16.3546 3.64543 17.25 4.75 17.25H5.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M8.75 3.75V20.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 604 B |
@@ -6,12 +6,11 @@ use settings::SignerKind;
|
|||||||
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
|
#[derive(Action, Clone, PartialEq, Eq, Deserialize)]
|
||||||
#[action(namespace = chat, no_json)]
|
#[action(namespace = chat, no_json)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
ChangeSubject(&'static str),
|
Insert(&'static str),
|
||||||
|
ChangeSubject(String),
|
||||||
ChangeSigner(SignerKind),
|
ChangeSigner(SignerKind),
|
||||||
ToggleBackup,
|
ToggleBackup,
|
||||||
|
Subject,
|
||||||
Insert(&'static str),
|
|
||||||
|
|
||||||
Copy(PublicKey),
|
Copy(PublicKey),
|
||||||
Relays(PublicKey),
|
Relays(PublicKey),
|
||||||
Njump(PublicKey),
|
Njump(PublicKey),
|
||||||
|
|||||||
@@ -72,9 +72,15 @@ pub struct ChatPanel {
|
|||||||
/// Mapping message (rumor event) ids to their reports
|
/// Mapping message (rumor event) ids to their reports
|
||||||
reports_by_id: Entity<BTreeMap<EventId, Vec<SendReport>>>,
|
reports_by_id: Entity<BTreeMap<EventId, Vec<SendReport>>>,
|
||||||
|
|
||||||
/// Input state
|
/// Chat input state
|
||||||
input: Entity<InputState>,
|
input: Entity<InputState>,
|
||||||
|
|
||||||
|
/// Subject input state
|
||||||
|
subject_input: Entity<InputState>,
|
||||||
|
|
||||||
|
/// Subject bar visibility
|
||||||
|
subject_bar: Entity<bool>,
|
||||||
|
|
||||||
/// Sent message ids
|
/// Sent message ids
|
||||||
sent_ids: Arc<RwLock<Vec<EventId>>>,
|
sent_ids: Arc<RwLock<Vec<EventId>>>,
|
||||||
|
|
||||||
@@ -91,7 +97,7 @@ pub struct ChatPanel {
|
|||||||
tasks: Vec<Task<Result<(), Error>>>,
|
tasks: Vec<Task<Result<(), Error>>>,
|
||||||
|
|
||||||
/// Event subscriptions
|
/// Event subscriptions
|
||||||
subscriptions: SmallVec<[Subscription; 2]>,
|
subscriptions: SmallVec<[Subscription; 3]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChatPanel {
|
impl ChatPanel {
|
||||||
@@ -124,15 +130,34 @@ impl ChatPanel {
|
|||||||
.clean_on_escape()
|
.clean_on_escape()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Define subject input state
|
||||||
|
let subject_input = cx.new(|cx| InputState::new(window, cx).placeholder("Nostr Meetup"));
|
||||||
|
let subject_bar = cx.new(|_cx| false);
|
||||||
|
|
||||||
// Define subscriptions
|
// Define subscriptions
|
||||||
let subscriptions =
|
let mut subscriptions = smallvec![];
|
||||||
smallvec![
|
|
||||||
|
subscriptions.push(
|
||||||
|
// Subscribe the chat input event
|
||||||
cx.subscribe_in(&input, window, move |this, _input, event, window, cx| {
|
cx.subscribe_in(&input, window, move |this, _input, event, window, cx| {
|
||||||
if let InputEvent::PressEnter { .. } = event {
|
if let InputEvent::PressEnter { .. } = event {
|
||||||
this.send_text_message(window, cx);
|
this.send_text_message(window, cx);
|
||||||
};
|
};
|
||||||
})
|
}),
|
||||||
];
|
);
|
||||||
|
|
||||||
|
subscriptions.push(
|
||||||
|
// Subscribe the subject input event
|
||||||
|
cx.subscribe_in(
|
||||||
|
&subject_input,
|
||||||
|
window,
|
||||||
|
move |this, _input, event, window, cx| {
|
||||||
|
if let InputEvent::PressEnter { .. } = event {
|
||||||
|
this.change_subject(window, cx);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// Define all functions that will run after the current cycle
|
// Define all functions that will run after the current cycle
|
||||||
cx.defer_in(window, |this, window, cx| {
|
cx.defer_in(window, |this, window, cx| {
|
||||||
@@ -149,6 +174,8 @@ impl ChatPanel {
|
|||||||
room,
|
room,
|
||||||
list_state,
|
list_state,
|
||||||
input,
|
input,
|
||||||
|
subject_input,
|
||||||
|
subject_bar,
|
||||||
replies_to,
|
replies_to,
|
||||||
attachments,
|
attachments,
|
||||||
rendered_texts_by_id: BTreeMap::new(),
|
rendered_texts_by_id: BTreeMap::new(),
|
||||||
@@ -254,10 +281,7 @@ impl ChatPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn subscribe_room_events(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
fn subscribe_room_events(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let Some(room) = self.room.upgrade() else {
|
if let Some(room) = self.room.upgrade() {
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.subscriptions.push(
|
self.subscriptions.push(
|
||||||
// Subscribe to room events
|
// Subscribe to room events
|
||||||
cx.subscribe_in(&room, window, move |this, _room, event, window, cx| {
|
cx.subscribe_in(&room, window, move |this, _room, event, window, cx| {
|
||||||
@@ -272,6 +296,7 @@ impl ChatPanel {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Load all messages belonging to this room
|
/// Load all messages belonging to this room
|
||||||
fn get_messages(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
|
fn get_messages(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
|
||||||
@@ -316,6 +341,16 @@ impl ChatPanel {
|
|||||||
content
|
content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn change_subject(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
let subject = self.subject_input.read(cx).value();
|
||||||
|
|
||||||
|
self.room
|
||||||
|
.update(cx, |this, cx| {
|
||||||
|
this.set_subject(subject, cx);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
|
||||||
fn send_text_message(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
fn send_text_message(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
// Get the message which includes all attachments
|
// Get the message which includes all attachments
|
||||||
let content = self.get_input_value(cx);
|
let content = self.get_input_value(cx);
|
||||||
@@ -613,7 +648,7 @@ impl ChatPanel {
|
|||||||
if self
|
if self
|
||||||
.room
|
.room
|
||||||
.update(cx, |this, cx| {
|
.update(cx, |this, cx| {
|
||||||
this.set_subject(*subject, cx);
|
this.set_subject(subject, cx);
|
||||||
})
|
})
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
@@ -642,6 +677,9 @@ impl ChatPanel {
|
|||||||
window.push_notification(Notification::error("Failed to toggle backup"), cx);
|
window.push_notification(Notification::error("Failed to toggle backup"), cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Command::Subject => {
|
||||||
|
self.open_subject(window, cx);
|
||||||
|
}
|
||||||
Command::Copy(public_key) => {
|
Command::Copy(public_key) => {
|
||||||
self.copy_author(public_key, cx);
|
self.copy_author(public_key, cx);
|
||||||
}
|
}
|
||||||
@@ -654,6 +692,47 @@ impl ChatPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn open_subject(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
let subject_input = self.subject_input.clone();
|
||||||
|
|
||||||
|
window.open_modal(cx, move |this, _window, cx| {
|
||||||
|
let subject = subject_input.read(cx).value();
|
||||||
|
|
||||||
|
this.title("Change subject")
|
||||||
|
.show_close(true)
|
||||||
|
.confirm()
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.gap_2()
|
||||||
|
.child(
|
||||||
|
v_flex()
|
||||||
|
.gap_1p5()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_sm()
|
||||||
|
.text_color(cx.theme().text_muted)
|
||||||
|
.child(SharedString::from("Subject:")),
|
||||||
|
)
|
||||||
|
.child(TextInput::new(&subject_input).small()),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.italic()
|
||||||
|
.text_xs()
|
||||||
|
.text_color(cx.theme().text_placeholder)
|
||||||
|
.child(SharedString::from(
|
||||||
|
"Subject will be updated when you send a new message.",
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.on_ok(move |_ev, window, cx| {
|
||||||
|
window
|
||||||
|
.dispatch_action(Box::new(Command::ChangeSubject(subject.to_string())), cx);
|
||||||
|
true
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn open_relays(&mut self, public_key: &PublicKey, window: &mut Window, cx: &mut Context<Self>) {
|
fn open_relays(&mut self, public_key: &PublicKey, window: &mut Window, cx: &mut Context<Self>) {
|
||||||
let profile = self.profile(public_key, cx);
|
let profile = self.profile(public_key, cx);
|
||||||
|
|
||||||
@@ -1349,12 +1428,30 @@ impl Panel for ChatPanel {
|
|||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
.gap_1p5()
|
.gap_1p5()
|
||||||
.child(Avatar::new(url).small())
|
.child(Avatar::new(url).xsmall())
|
||||||
.child(label)
|
.child(label)
|
||||||
.into_any_element()
|
.into_any_element()
|
||||||
})
|
})
|
||||||
.unwrap_or(div().child("Unknown").into_any_element())
|
.unwrap_or(div().child("Unknown").into_any_element())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn toolbar_buttons(&self, _window: &Window, _cx: &App) -> Vec<Button> {
|
||||||
|
let subject_bar = self.subject_bar.clone();
|
||||||
|
|
||||||
|
vec![
|
||||||
|
Button::new("subject")
|
||||||
|
.icon(IconName::Input)
|
||||||
|
.tooltip("Change subject")
|
||||||
|
.small()
|
||||||
|
.ghost()
|
||||||
|
.on_click(move |_ev, _window, cx| {
|
||||||
|
subject_bar.update(cx, |this, cx| {
|
||||||
|
*this = !*this;
|
||||||
|
cx.notify();
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventEmitter<PanelEvent> for ChatPanel {}
|
impl EventEmitter<PanelEvent> for ChatPanel {}
|
||||||
@@ -1370,6 +1467,33 @@ impl Render for ChatPanel {
|
|||||||
v_flex()
|
v_flex()
|
||||||
.on_action(cx.listener(Self::on_command))
|
.on_action(cx.listener(Self::on_command))
|
||||||
.size_full()
|
.size_full()
|
||||||
|
.when(*self.subject_bar.read(cx), |this| {
|
||||||
|
this.child(
|
||||||
|
h_flex()
|
||||||
|
.h_12()
|
||||||
|
.w_full()
|
||||||
|
.px_2()
|
||||||
|
.gap_2()
|
||||||
|
.border_b_1()
|
||||||
|
.border_color(cx.theme().border)
|
||||||
|
.child(
|
||||||
|
TextInput::new(&self.subject_input)
|
||||||
|
.text_sm()
|
||||||
|
.small()
|
||||||
|
.bordered(false),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("change")
|
||||||
|
.icon(IconName::CheckCircle)
|
||||||
|
.label("Change")
|
||||||
|
.secondary()
|
||||||
|
.disabled(self.uploading)
|
||||||
|
.on_click(cx.listener(move |this, _ev, window, cx| {
|
||||||
|
this.change_subject(window, cx);
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
.child(
|
.child(
|
||||||
v_flex()
|
v_flex()
|
||||||
.flex_1()
|
.flex_1()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use gpui::prelude::FluentBuilder as _;
|
use gpui::prelude::FluentBuilder as _;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
svg, AnyElement, App, AppContext, Context, Entity, Hsla, IntoElement, Radians, Render,
|
AnyElement, App, AppContext, Context, Entity, Hsla, IntoElement, Radians, Render, RenderOnce,
|
||||||
RenderOnce, SharedString, StyleRefinement, Styled, Svg, Transformation, Window,
|
SharedString, StyleRefinement, Styled, Svg, Transformation, Window, svg,
|
||||||
};
|
};
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
|
|
||||||
@@ -39,6 +39,7 @@ pub enum IconName {
|
|||||||
Ellipsis,
|
Ellipsis,
|
||||||
Emoji,
|
Emoji,
|
||||||
Eye,
|
Eye,
|
||||||
|
Input,
|
||||||
Info,
|
Info,
|
||||||
Invite,
|
Invite,
|
||||||
Inbox,
|
Inbox,
|
||||||
@@ -110,6 +111,7 @@ impl IconNamed for IconName {
|
|||||||
Self::Ellipsis => "icons/ellipsis.svg",
|
Self::Ellipsis => "icons/ellipsis.svg",
|
||||||
Self::Emoji => "icons/emoji.svg",
|
Self::Emoji => "icons/emoji.svg",
|
||||||
Self::Eye => "icons/eye.svg",
|
Self::Eye => "icons/eye.svg",
|
||||||
|
Self::Input => "icons/input.svg",
|
||||||
Self::Info => "icons/info.svg",
|
Self::Info => "icons/info.svg",
|
||||||
Self::Invite => "icons/invite.svg",
|
Self::Invite => "icons/invite.svg",
|
||||||
Self::Inbox => "icons/inbox.svg",
|
Self::Inbox => "icons/inbox.svg",
|
||||||
|
|||||||
Reference in New Issue
Block a user