feat: make the web version fully working (#67)
Reviewed-on: reya/coop#67
This commit is contained in:
@@ -9,7 +9,7 @@ use gpui_kit::component::input::{Input, InputEvent, InputState};
|
||||
use gpui_kit::component::{ActiveTheme, Disableable, WindowExt, v_flex};
|
||||
use instant::Duration;
|
||||
use nostr_connect::prelude::*;
|
||||
use state::NostrRegistry;
|
||||
use state::{NostrRegistry, StateEvent};
|
||||
|
||||
pub fn open(window: &mut Window, cx: &mut App) {
|
||||
let import = cx.new(|cx| ImportIdentity::new(window, cx));
|
||||
@@ -73,12 +73,13 @@ impl ImportIdentity {
|
||||
}
|
||||
|
||||
fn build(mode: Mode, window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let key_input = cx.new(|cx| InputState::new(window, cx).placeholder("secret or bunker://"));
|
||||
let pass_input = cx.new(|cx| InputState::new(window, cx).masked(true));
|
||||
let repass_input = cx.new(|cx| InputState::new(window, cx).masked(true));
|
||||
let error = cx.new(|_| None);
|
||||
|
||||
let subscriptions = vec![
|
||||
let mut subscriptions = vec![
|
||||
cx.subscribe_in(&key_input, window, |this, _input, event, _window, cx| {
|
||||
if let InputEvent::PressEnter { .. } = event {
|
||||
this.login(cx);
|
||||
@@ -96,6 +97,14 @@ impl ImportIdentity {
|
||||
}),
|
||||
];
|
||||
|
||||
subscriptions.push(
|
||||
cx.subscribe_in(&nostr, window, |this, _nostr, event, _window, cx| {
|
||||
if let StateEvent::Error(error) = event {
|
||||
this.set_error(error.clone(), cx);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
Self {
|
||||
mode,
|
||||
key_input,
|
||||
@@ -188,6 +197,14 @@ impl ImportIdentity {
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn extension(&mut self, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
nostr.update(cx, |this, cx| {
|
||||
this.connect_extension(cx);
|
||||
});
|
||||
}
|
||||
|
||||
fn set_loading(&mut self, status: bool, cx: &mut Context<Self>) {
|
||||
self.loading = status;
|
||||
cx.notify();
|
||||
@@ -226,7 +243,6 @@ impl Render for ImportIdentity {
|
||||
const BUNKER_WARN: &str = "Nostr Connect will usually take more time to get all your messages. Please keep your session open until you see all your messages.";
|
||||
const KEY_WARN: &str = "Coop encrypts your key with your passphrase and stores it on this device. You only need the passphrase to sign in next time.";
|
||||
|
||||
let is_wasm = cfg!(target_arch = "wasm32");
|
||||
let is_unlock = self.mode == Mode::Unlock;
|
||||
|
||||
let value = self.key_input.read(cx).value().trim().to_string();
|
||||
@@ -305,26 +321,45 @@ impl Render for ImportIdentity {
|
||||
)
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
Button::new("login")
|
||||
.label(if is_unlock { "Unlock" } else { "Continue" })
|
||||
.primary()
|
||||
.loading(self.loading)
|
||||
.disabled(self.loading)
|
||||
.on_click(cx.listener(move |this, _ev, _window, cx| {
|
||||
this.login(cx);
|
||||
})),
|
||||
)
|
||||
.when(!is_wasm && !is_unlock, |this| {
|
||||
this.child(
|
||||
Button::new("proxy")
|
||||
.label("Connect via Web Extension (Experimental)")
|
||||
.ghost()
|
||||
.child({
|
||||
let content = v_flex().child(
|
||||
Button::new("login")
|
||||
.label(if is_unlock { "Unlock" } else { "Continue" })
|
||||
.primary()
|
||||
.loading(self.loading)
|
||||
.disabled(self.loading)
|
||||
.on_click(cx.listener(move |this, _ev, _window, cx| {
|
||||
this.proxy(cx);
|
||||
this.login(cx);
|
||||
})),
|
||||
)
|
||||
);
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let content = content.when(!is_unlock, |this| {
|
||||
this.child(
|
||||
Button::new("proxy")
|
||||
.label("Connect via Web Extension (Experimental)")
|
||||
.ghost()
|
||||
.disabled(self.loading)
|
||||
.on_click(cx.listener(move |this, _ev, _window, cx| {
|
||||
this.proxy(cx);
|
||||
})),
|
||||
)
|
||||
});
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let content = content.when(!is_unlock, |this| {
|
||||
this.child(
|
||||
Button::new("extension")
|
||||
.label("Continue with Browser Extension (NIP-07)")
|
||||
.ghost()
|
||||
.disabled(self.loading)
|
||||
.on_click(cx.listener(move |this, _ev, _window, cx| {
|
||||
this.extension(cx);
|
||||
})),
|
||||
)
|
||||
});
|
||||
|
||||
content
|
||||
})
|
||||
.when_some(self.error.read(cx).as_ref(), |this, error| {
|
||||
this.child(
|
||||
|
||||
@@ -2,8 +2,8 @@ use anyhow::Error;
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{
|
||||
App, AppContext, Context, Div, Entity, FocusHandle, InteractiveElement as _, IntoElement,
|
||||
ParentElement, PathPromptOptions, Render, SharedString, StatefulInteractiveElement as _,
|
||||
StyleRefinement, Styled, Subscription, Task, WeakEntity, Window, div, px, rems,
|
||||
ParentElement, Render, SharedString, StatefulInteractiveElement as _, StyleRefinement, Styled,
|
||||
Subscription, Task, WeakEntity, Window, div, px, rems,
|
||||
};
|
||||
use gpui_base::{Popup, Select};
|
||||
use gpui_kit::assets::IconName;
|
||||
@@ -18,7 +18,7 @@ use gpui_kit::component::{ActiveTheme, Disableable, Icon, Sizable, WindowExt, h_
|
||||
use nip29::{GroupCreateOptions, GroupKey, GroupsEvent, GroupsRegistry};
|
||||
use nostr_sdk::prelude::*;
|
||||
use settings::AppSettings;
|
||||
use state::upload;
|
||||
use state::{pick_file, upload_file};
|
||||
use ui::{Avatar, settings_row};
|
||||
|
||||
const DEFAULT_RELAYS: [&str; 2] = ["wss://relay.ditto.net", "wss://chat.wisp.talk"];
|
||||
@@ -297,16 +297,11 @@ impl NewGroup {
|
||||
fn upload(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let server = AppSettings::get_file_server(cx);
|
||||
|
||||
let path = cx.prompt_for_paths(PathPromptOptions {
|
||||
files: true,
|
||||
directories: false,
|
||||
multiple: false,
|
||||
prompt: None,
|
||||
});
|
||||
let picker = pick_file(cx);
|
||||
|
||||
self.tasks.push(cx.spawn_in(window, async move |this, cx| {
|
||||
// Selecting no file means the prompt was cancelled
|
||||
let Some(path) = path.await??.and_then(|mut paths| paths.pop()) else {
|
||||
let Some(picked) = picker.await? else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
@@ -314,7 +309,7 @@ impl NewGroup {
|
||||
this.set_uploading(true, cx);
|
||||
})?;
|
||||
|
||||
match upload(server, path, cx).await {
|
||||
match upload_file(server, picked, cx).await {
|
||||
Ok(url) => {
|
||||
this.update(cx, |this, cx| {
|
||||
this.picture = Some(url.to_string().into());
|
||||
|
||||
@@ -4,8 +4,8 @@ use anyhow::Error;
|
||||
use chat::{ChatRegistry, Room, RoomKind};
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{
|
||||
App, AppContext, ClipboardItem, Context, Div, Entity, IntoElement, ParentElement,
|
||||
PathPromptOptions, Render, SharedString, Styled, Task, Window, div, retain_all,
|
||||
App, AppContext, ClipboardItem, Context, Div, Entity, IntoElement, ParentElement, Render,
|
||||
SharedString, Styled, Task, Window, div, retain_all,
|
||||
};
|
||||
use gpui_kit::assets::IconName;
|
||||
use gpui_kit::base::StyledExt as _;
|
||||
@@ -18,7 +18,7 @@ use instant::Duration;
|
||||
use nostr_sdk::prelude::*;
|
||||
use person::{Directory, Person, PersonRegistry, shorten_pubkey};
|
||||
use settings::AppSettings;
|
||||
use state::{NostrRegistry, upload};
|
||||
use state::{NostrRegistry, pick_file, upload_file};
|
||||
use ui::Avatar;
|
||||
|
||||
use crate::Workspace;
|
||||
@@ -179,17 +179,12 @@ impl ProfileDialog {
|
||||
// Get the user's configured blossom server
|
||||
let server = AppSettings::get_file_server(cx);
|
||||
|
||||
// Ask user for file upload
|
||||
let path = cx.prompt_for_paths(PathPromptOptions {
|
||||
files: true,
|
||||
directories: false,
|
||||
multiple: false,
|
||||
prompt: None,
|
||||
});
|
||||
// Ask the user for an image to upload
|
||||
let picker = pick_file(cx);
|
||||
|
||||
self.tasks.push(cx.spawn_in(window, async move |this, cx| {
|
||||
// Selecting no file means the prompt was cancelled
|
||||
let Some(path) = path.await??.and_then(|mut paths| paths.pop()) else {
|
||||
let Some(picked) = picker.await? else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
@@ -198,7 +193,7 @@ impl ProfileDialog {
|
||||
})?;
|
||||
|
||||
// Upload via blossom client
|
||||
match upload(server, path, cx).await {
|
||||
match upload_file(server, picked, cx).await {
|
||||
Ok(url) => {
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
this.avatar_input.update(cx, |this, cx| {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use gpui::PathPromptOptions;
|
||||
use gpui::http_client::Url;
|
||||
use gpui::{
|
||||
App, AppContext, Context, Entity, IntoElement, ParentElement, PathPromptOptions, Render,
|
||||
Styled, Subscription, Window, div,
|
||||
App, AppContext, Context, Entity, IntoElement, ParentElement, Render, Styled, Subscription,
|
||||
Window, div,
|
||||
};
|
||||
use gpui_kit::assets::IconName;
|
||||
use gpui_kit::component::button::{Button, ButtonVariants};
|
||||
@@ -150,29 +152,41 @@ impl Preferences {
|
||||
}
|
||||
|
||||
/// Ask the user for an image to show behind chat and group panels
|
||||
fn choose_background(cx: &mut App) {
|
||||
let task = cx.prompt_for_paths(PathPromptOptions {
|
||||
files: true,
|
||||
directories: false,
|
||||
multiple: false,
|
||||
prompt: None,
|
||||
});
|
||||
fn choose_background(
|
||||
#[cfg_attr(not(target_arch = "wasm32"), allow(unused_variables))] window: &mut Window,
|
||||
cx: &mut App,
|
||||
) {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
let note = Notification::warning("Background images are not supported on web yet.");
|
||||
window.push_notification(note, cx);
|
||||
}
|
||||
|
||||
cx.spawn(async move |cx| {
|
||||
let Ok(Ok(Some(mut paths))) = task.await else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(path) = paths.pop() else {
|
||||
return;
|
||||
};
|
||||
|
||||
cx.update(|cx| {
|
||||
AppSettings::update_panel_background_path(Some(path), cx);
|
||||
cx.refresh_windows();
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
let task = cx.prompt_for_paths(PathPromptOptions {
|
||||
files: true,
|
||||
directories: false,
|
||||
multiple: false,
|
||||
prompt: None,
|
||||
});
|
||||
})
|
||||
.detach();
|
||||
|
||||
cx.spawn(async move |cx| {
|
||||
let Ok(Ok(Some(mut paths))) = task.await else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(path) = paths.pop() else {
|
||||
return;
|
||||
};
|
||||
|
||||
cx.update(|cx| {
|
||||
AppSettings::update_panel_background_path(Some(path), cx);
|
||||
cx.refresh_windows();
|
||||
});
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,8 +344,8 @@ impl Render for Preferences {
|
||||
.ghost()
|
||||
.small()
|
||||
.disabled(!panel_background)
|
||||
.on_click(|_, _window, cx| {
|
||||
Self::choose_background(cx);
|
||||
.on_click(|_, window, cx| {
|
||||
Self::choose_background(window, cx);
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
|
||||
Reference in New Issue
Block a user