feat: add support for encrypted attachment #45

Merged
reya merged 12 commits from encrypt-media into master 2026-09-16 01:25:15 +00:00
Showing only changes of commit 024dc90d07 - Show all commits
+52 -63
View File
@@ -33,6 +33,7 @@ use ui::input::{Input, InputEvent, InputState};
use ui::menu::DropdownMenu; use ui::menu::DropdownMenu;
use ui::notification::Notification; use ui::notification::Notification;
use ui::scroll::Scrollbar; use ui::scroll::Scrollbar;
use ui::tooltip::Tooltip;
use ui::{ use ui::{
Disableable, Icon, IconName, InteractiveElementExt, Sizable, StyledExt, WindowExtension, Disableable, Icon, IconName, InteractiveElementExt, Sizable, StyledExt, WindowExtension,
h_flex, v_flex, h_flex, v_flex,
@@ -1651,7 +1652,7 @@ impl ChatPanel {
.size_16() .size_16()
.when(cx.theme().shadow, |this| this.shadow_lg()) .when(cx.theme().shadow, |this| this.shadow_lg())
.rounded(cx.theme().radius) .rounded(cx.theme().radius)
.object_fit(ObjectFit::ScaleDown), .object_fit(ObjectFit::Cover),
) )
.child( .child(
div() div()
@@ -1734,33 +1735,23 @@ impl ChatPanel {
label: SharedString, label: SharedString,
cx: &Context<Self>, cx: &Context<Self>,
) -> AnyElement { ) -> AnyElement {
div() h_flex()
.id(SharedString::from(format!("file-{id}"))) .id(SharedString::from(format!("file-{id}")))
.flex() .self_start()
.items_center() .items_start()
.min_w_0()
.gap_2() .gap_2()
.py_1() .p_2()
.px_2()
.border_1() .border_1()
.border_color(cx.theme().border_variant) .border_color(cx.theme().border_variant)
.rounded(cx.theme().radius) .rounded(cx.theme().radius)
.hover(|this| this.bg(cx.theme().surface_background)) .child(Icon::new(IconName::Lock).text_color(cx.theme().icon_accent))
.child(
Icon::new(IconName::Lock)
.small()
.text_color(cx.theme().text_placeholder),
)
.child( .child(
v_flex() v_flex()
.flex_1() .min_w_0()
.overflow_hidden() .overflow_hidden()
.text_sm() .text_sm()
.child( .child(div().line_height(relative(1.2)).child(file.display_name()))
div()
.text_ellipsis()
.line_clamp(1)
.child(file.display_name()),
)
.child( .child(
div() div()
.text_xs() .text_xs()
@@ -1782,58 +1773,59 @@ impl ChatPanel {
/// Render an uploaded, encrypted file which is not sent yet /// Render an uploaded, encrypted file which is not sent yet
fn render_pending_file(&self, pending: &PendingFile, cx: &Context<Self>) -> impl IntoElement { fn render_pending_file(&self, pending: &PendingFile, cx: &Context<Self>) -> impl IntoElement {
let file = &pending.file; let file = &pending.file;
let label = file.display_name();
div() div()
.id(SharedString::from(file.url.to_string())) .id(SharedString::from(file.url.to_string()))
.relative() .relative()
.flex() .w_16()
.items_center() .tooltip(move |window, cx| Tooltip::new(label.clone(), window, cx).into())
.gap_2() .map(|this| {
.p_1() if file.is_image() {
.pr_2()
.border_1()
.border_color(cx.theme().border_variant)
.rounded(cx.theme().radius)
.when(file.is_image(), |this| {
this.child( this.child(
img(pending.path.clone()) img(pending.path.clone())
.size_8() .size_16()
.when(cx.theme().shadow, |this| this.shadow_sm())
.rounded(cx.theme().radius) .rounded(cx.theme().radius)
.object_fit(ObjectFit::Cover), .object_fit(ObjectFit::Cover),
) )
} else {
this.child(
div()
.size_16()
.flex()
.items_center()
.justify_center()
.rounded(cx.theme().radius)
.border_1()
.border_color(cx.theme().border_variant)
.bg(cx.theme().surface_background)
.text_xs()
.text_center()
.child("Preview not available"),
)
}
}) })
.child( .child(
v_flex() v_flex()
.text_sm() .absolute()
.child( .top_neg_1()
div() .right_neg_1()
.max_w(px(160.)) .size_4()
.text_ellipsis()
.line_clamp(1)
.child(file.display_name()),
)
.child(
h_flex()
.gap_1()
.items_center() .items_center()
.text_xs() .justify_center()
.text_color(cx.theme().text_placeholder) .rounded_full()
.child(Icon::new(IconName::Lock).size_2()) .border_1()
.child("End-to-end encrypted"), .border_color(cx.theme().border_variant)
), .bg(gpui::green())
.child(Icon::new(IconName::Lock).size_2().text_color(gpui::white())),
) )
.child(
Button::new(SharedString::from(format!("remove-{}", file.url)))
.icon(IconName::Close)
.xsmall()
.ghost()
.on_click({ .on_click({
let url = file.url.clone(); let url = file.url.clone();
cx.listener(move |this, _, _, cx| { cx.listener(move |this, _, _, cx| {
this.remove_pending_file(&url, cx); this.remove_pending_file(&url, cx);
}) })
}), })
)
} }
fn render_pending_file_list( fn render_pending_file_list(
@@ -2026,6 +2018,10 @@ impl Focusable for ChatPanel {
impl Render for ChatPanel { impl Render for ChatPanel {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
const WARNING: &str = "Attachments added while typing are uploaded without encryption";
let is_typing = !self.input.read(cx).value().trim().is_empty();
v_flex() v_flex()
.image_cache(coop_cache(self.id.clone(), 100)) .image_cache(coop_cache(self.id.clone(), 100))
.on_action(cx.listener(Self::on_command)) .on_action(cx.listener(Self::on_command))
@@ -2059,10 +2055,8 @@ impl Render for ChatPanel {
.map(|this| { .map(|this| {
if self.messages.is_empty() { if self.messages.is_empty() {
this.child( this.child(
div() h_flex()
.size_full() .size_full()
.flex()
.items_center()
.justify_end() .justify_end()
.child(self.render_announcement(cx)), .child(self.render_announcement(cx)),
) )
@@ -2089,20 +2083,15 @@ impl Render for ChatPanel {
.children(self.render_attachment_list(window, cx)) .children(self.render_attachment_list(window, cx))
.children(self.render_pending_file_list(window, cx)) .children(self.render_pending_file_list(window, cx))
.children(self.render_reply_list(window, cx)) .children(self.render_reply_list(window, cx))
.when( .when(is_typing, |this| {
!self.input.read(cx).value().trim().is_empty(),
|this| {
this.child( this.child(
div() div()
.px_1() .px_1()
.text_xs() .text_xs()
.text_color(cx.theme().text_warning) .text_color(cx.theme().text_warning)
.child( .child(WARNING),
"Attachments added while typing are uploaded without encryption",
),
)
},
) )
})
.child( .child(
h_flex() h_flex()
.items_end() .items_end()