mirror of
https://github.com/aljazceru/notedeck.git
synced 2026-02-23 09:24:20 +01:00
Add channel creation dialog
Implement a channel creation dialog that allows users to: - Enter a channel name - Specify comma-separated hashtags to track - Create and subscribe to new channels When a channel is created: 1. It's added to the active channels list 2. Saved to disk for persistence 3. Automatically subscribed to start fetching messages The dialog uses egui::Window for a modal-like experience and includes validation to ensure both name and hashtags are provided.
This commit is contained in:
@@ -50,6 +50,7 @@ pub struct Damus {
|
||||
pub decks_cache: DecksCache,
|
||||
pub channels_cache: crate::channels::ChannelsCache,
|
||||
pub relay_config: crate::relay_config::RelayConfig,
|
||||
pub channel_dialog: ui::ChannelDialog,
|
||||
pub view_state: ViewState,
|
||||
pub drafts: Drafts,
|
||||
pub timeline_cache: TimelineCache,
|
||||
@@ -399,6 +400,53 @@ fn render_damus(damus: &mut Damus, app_ctx: &mut AppContext<'_>, ui: &mut egui::
|
||||
|
||||
fullscreen_media_viewer_ui(ui, &mut damus.view_state.media_viewer, app_ctx.img_cache);
|
||||
|
||||
// Show channel creation dialog
|
||||
if let Some(dialog_action) = damus.channel_dialog.show(ui.ctx(), app_ctx.i18n) {
|
||||
match dialog_action {
|
||||
ui::ChannelDialogAction::Create { name, hashtags } => {
|
||||
// Create new channel
|
||||
let channel = crate::channels::Channel::new(name, hashtags);
|
||||
|
||||
// Add to active channels
|
||||
damus
|
||||
.channels_cache
|
||||
.active_channels_mut(app_ctx.i18n, app_ctx.accounts)
|
||||
.add_channel(channel);
|
||||
|
||||
// Save channels cache
|
||||
storage::save_channels_cache(app_ctx.path, &damus.channels_cache);
|
||||
|
||||
// Subscribe to the new channel
|
||||
let txn = nostrdb::Transaction::new(app_ctx.ndb).unwrap();
|
||||
let channel_count = damus.channels_cache.active_channels(app_ctx.accounts).num_channels();
|
||||
if let Some(channel) = damus.channels_cache.active_channels_mut(app_ctx.i18n, app_ctx.accounts).get_channel_mut(channel_count - 1) {
|
||||
if !channel.subscribed {
|
||||
if let Some(result) = damus.timeline_cache.open(
|
||||
&mut damus.subscriptions,
|
||||
app_ctx.ndb,
|
||||
app_ctx.note_cache,
|
||||
&txn,
|
||||
app_ctx.pool,
|
||||
&channel.timeline_kind,
|
||||
) {
|
||||
result.process(
|
||||
app_ctx.ndb,
|
||||
app_ctx.note_cache,
|
||||
&txn,
|
||||
&mut damus.timeline_cache,
|
||||
app_ctx.unknown_ids,
|
||||
);
|
||||
channel.subscribed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ui::ChannelDialogAction::Cancel => {
|
||||
// Dialog was canceled, nothing to do
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We use this for keeping timestamps and things up to date
|
||||
//ui.ctx().request_repaint_after(Duration::from_secs(5));
|
||||
|
||||
@@ -587,6 +635,7 @@ impl Damus {
|
||||
decks_cache,
|
||||
channels_cache,
|
||||
relay_config,
|
||||
channel_dialog: ui::ChannelDialog::default(),
|
||||
unrecognized_args,
|
||||
jobs,
|
||||
threads,
|
||||
@@ -642,6 +691,7 @@ impl Damus {
|
||||
decks_cache,
|
||||
channels_cache,
|
||||
relay_config,
|
||||
channel_dialog: ui::ChannelDialog::default(),
|
||||
unrecognized_args: BTreeSet::default(),
|
||||
jobs: JobsCache::default(),
|
||||
threads: Threads::default(),
|
||||
@@ -1061,7 +1111,7 @@ fn timelines_view(
|
||||
storage::save_channels_cache(ctx.path, &app.channels_cache);
|
||||
}
|
||||
ChannelSidebarAction::AddChannel => {
|
||||
// TODO: Show add channel dialog
|
||||
app.channel_dialog.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
172
crates/notedeck_columns/src/ui/channel_dialog.rs
Normal file
172
crates/notedeck_columns/src/ui/channel_dialog.rs
Normal file
@@ -0,0 +1,172 @@
|
||||
use egui::{RichText, TextEdit, Vec2};
|
||||
|
||||
use notedeck::{tr, Localization};
|
||||
|
||||
pub struct ChannelDialog {
|
||||
pub name: String,
|
||||
pub hashtags: String,
|
||||
pub is_open: bool,
|
||||
}
|
||||
|
||||
pub enum ChannelDialogAction {
|
||||
Create { name: String, hashtags: Vec<String> },
|
||||
Cancel,
|
||||
}
|
||||
|
||||
impl ChannelDialog {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
name: String::new(),
|
||||
hashtags: String::new(),
|
||||
is_open: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open(&mut self) {
|
||||
self.is_open = true;
|
||||
self.name.clear();
|
||||
self.hashtags.clear();
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
self.is_open = false;
|
||||
}
|
||||
|
||||
pub fn show(
|
||||
&mut self,
|
||||
ctx: &egui::Context,
|
||||
i18n: &mut Localization,
|
||||
) -> Option<ChannelDialogAction> {
|
||||
if !self.is_open {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut action: Option<ChannelDialogAction> = None;
|
||||
|
||||
egui::Window::new(tr!(i18n, "Create Channel", "Dialog title for creating a new channel"))
|
||||
.collapsible(false)
|
||||
.resizable(false)
|
||||
.anchor(egui::Align2::CENTER_CENTER, Vec2::ZERO)
|
||||
.fixed_size(Vec2::new(400.0, 300.0))
|
||||
.show(ctx, |ui| {
|
||||
ui.vertical(|ui| {
|
||||
ui.add_space(16.0);
|
||||
|
||||
// Channel name input
|
||||
ui.label(
|
||||
RichText::new(tr!(i18n, "Channel Name", "Label for channel name input"))
|
||||
.size(14.0)
|
||||
.strong(),
|
||||
);
|
||||
ui.add_space(8.0);
|
||||
|
||||
let name_response = ui.add(
|
||||
TextEdit::singleline(&mut self.name)
|
||||
.hint_text(tr!(
|
||||
i18n,
|
||||
"e.g., General, Bitcoin, News...",
|
||||
"Placeholder for channel name"
|
||||
))
|
||||
.desired_width(f32::INFINITY),
|
||||
);
|
||||
|
||||
// Auto-focus on name field when opened
|
||||
if name_response.changed() {
|
||||
name_response.request_focus();
|
||||
}
|
||||
|
||||
ui.add_space(16.0);
|
||||
|
||||
// Hashtags input
|
||||
ui.label(
|
||||
RichText::new(tr!(i18n, "Hashtags", "Label for hashtags input"))
|
||||
.size(14.0)
|
||||
.strong(),
|
||||
);
|
||||
ui.add_space(4.0);
|
||||
ui.label(
|
||||
RichText::new(tr!(
|
||||
i18n,
|
||||
"Comma-separated hashtags to track",
|
||||
"Help text for hashtags input"
|
||||
))
|
||||
.size(12.0)
|
||||
.color(ui.visuals().weak_text_color()),
|
||||
);
|
||||
ui.add_space(8.0);
|
||||
|
||||
ui.add(
|
||||
TextEdit::multiline(&mut self.hashtags)
|
||||
.hint_text(tr!(
|
||||
i18n,
|
||||
"e.g., bitcoin, nostr, news",
|
||||
"Placeholder for hashtags"
|
||||
))
|
||||
.desired_width(f32::INFINITY)
|
||||
.desired_rows(3),
|
||||
);
|
||||
|
||||
ui.add_space(24.0);
|
||||
|
||||
// Buttons
|
||||
ui.horizontal(|ui| {
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
// Create button
|
||||
let create_enabled = !self.name.trim().is_empty()
|
||||
&& !self.hashtags.trim().is_empty();
|
||||
|
||||
let create_button = egui::Button::new(
|
||||
RichText::new(tr!(i18n, "Create", "Button to create channel"))
|
||||
.size(14.0),
|
||||
)
|
||||
.min_size(Vec2::new(80.0, 32.0));
|
||||
|
||||
let create_response = ui.add_enabled(create_enabled, create_button);
|
||||
|
||||
if create_response.clicked() {
|
||||
let hashtags: Vec<String> = self
|
||||
.hashtags
|
||||
.split(',')
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
|
||||
action = Some(ChannelDialogAction::Create {
|
||||
name: self.name.trim().to_string(),
|
||||
hashtags,
|
||||
});
|
||||
}
|
||||
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Cancel button
|
||||
let cancel_button = egui::Button::new(
|
||||
RichText::new(tr!(i18n, "Cancel", "Button to cancel"))
|
||||
.size(14.0)
|
||||
.color(ui.visuals().weak_text_color()),
|
||||
)
|
||||
.frame(false)
|
||||
.min_size(Vec2::new(80.0, 32.0));
|
||||
|
||||
if ui.add(cancel_button).clicked() {
|
||||
action = Some(ChannelDialogAction::Cancel);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Close dialog if action was taken
|
||||
if action.is_some() {
|
||||
self.close();
|
||||
}
|
||||
|
||||
action
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ChannelDialog {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
pub mod account_login_view;
|
||||
pub mod accounts;
|
||||
pub mod add_column;
|
||||
pub mod channel_dialog;
|
||||
pub mod channel_sidebar;
|
||||
pub mod chat_view;
|
||||
pub mod column;
|
||||
@@ -26,6 +27,7 @@ pub mod wallet;
|
||||
pub mod widgets;
|
||||
|
||||
pub use accounts::AccountsView;
|
||||
pub use channel_dialog::{ChannelDialog, ChannelDialogAction};
|
||||
pub use channel_sidebar::{ChannelSidebar, ChannelSidebarAction};
|
||||
pub use chat_view::ChatView;
|
||||
pub use note::{PostReplyView, PostView};
|
||||
|
||||
Reference in New Issue
Block a user