initial refactor in preparation for routing
Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
@@ -8,7 +8,7 @@ pub struct Keypair {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Keypair {
|
impl Keypair {
|
||||||
pub fn new(secret_key: SecretKey) -> Self {
|
pub fn from_secret(secret_key: SecretKey) -> Self {
|
||||||
let cloned_secret_key = secret_key.clone();
|
let cloned_secret_key = secret_key.clone();
|
||||||
let nostr_keys = nostr::Keys::new(secret_key);
|
let nostr_keys = nostr::Keys::new(secret_key);
|
||||||
Keypair {
|
Keypair {
|
||||||
@@ -17,6 +17,10 @@ impl Keypair {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new(pubkey: Pubkey, secret_key: Option<SecretKey>) -> Self {
|
||||||
|
Keypair { pubkey, secret_key }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn only_pubkey(pubkey: Pubkey) -> Self {
|
pub fn only_pubkey(pubkey: Pubkey) -> Self {
|
||||||
Keypair {
|
Keypair {
|
||||||
pubkey,
|
pubkey,
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use enostr::FullKeypair;
|
use enostr::Keypair;
|
||||||
|
|
||||||
|
use crate::key_storage::KeyStorage;
|
||||||
pub use crate::user_account::UserAccount;
|
pub use crate::user_account::UserAccount;
|
||||||
use crate::{key_storage::KeyStorage, relay_generation::RelayGenerator};
|
|
||||||
|
|
||||||
/// The interface for managing the user's accounts.
|
/// The interface for managing the user's accounts.
|
||||||
/// Represents all user-facing operations related to account management.
|
/// Represents all user-facing operations related to account management.
|
||||||
@@ -11,34 +11,16 @@ pub struct AccountManager {
|
|||||||
currently_selected_account: Option<usize>,
|
currently_selected_account: Option<usize>,
|
||||||
accounts: Vec<UserAccount>,
|
accounts: Vec<UserAccount>,
|
||||||
key_store: KeyStorage,
|
key_store: KeyStorage,
|
||||||
relay_generator: RelayGenerator,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AccountManager {
|
impl AccountManager {
|
||||||
pub fn new(
|
pub fn new(currently_selected_account: Option<usize>, key_store: KeyStorage) -> Self {
|
||||||
currently_selected_account: Option<usize>,
|
let accounts = key_store.get_keys().unwrap_or_default();
|
||||||
key_store: KeyStorage,
|
|
||||||
// TODO: right now, there is only one way of generating relays for all accounts. In the future
|
|
||||||
// each account should have the option of generating relays differently
|
|
||||||
relay_generator: RelayGenerator,
|
|
||||||
wakeup: impl Fn() + Send + Sync + Clone + 'static,
|
|
||||||
) -> Self {
|
|
||||||
let accounts = if let Ok(keys) = key_store.get_keys() {
|
|
||||||
keys.into_iter()
|
|
||||||
.map(|key| {
|
|
||||||
let relays = relay_generator.generate_relays_for(&key.pubkey, wakeup.clone());
|
|
||||||
UserAccount { key, relays }
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
} else {
|
|
||||||
Vec::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
AccountManager {
|
AccountManager {
|
||||||
currently_selected_account,
|
currently_selected_account,
|
||||||
accounts,
|
accounts,
|
||||||
key_store,
|
key_store,
|
||||||
relay_generator,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,13 +28,17 @@ impl AccountManager {
|
|||||||
&self.accounts
|
&self.accounts
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_account(&self, index: usize) -> Option<&UserAccount> {
|
pub fn get_account(&self, ind: usize) -> Option<&UserAccount> {
|
||||||
self.accounts.get(index)
|
self.accounts.get(ind)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_account(&self, pk: &[u8; 32]) -> Option<&UserAccount> {
|
||||||
|
self.accounts.iter().find(|acc| acc.pubkey.bytes() == pk)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_account(&mut self, index: usize) {
|
pub fn remove_account(&mut self, index: usize) {
|
||||||
if let Some(account) = self.accounts.get(index) {
|
if let Some(account) = self.accounts.get(index) {
|
||||||
let _ = self.key_store.remove_key(&account.key);
|
let _ = self.key_store.remove_key(account);
|
||||||
self.accounts.remove(index);
|
self.accounts.remove(index);
|
||||||
|
|
||||||
if let Some(selected_index) = self.currently_selected_account {
|
if let Some(selected_index) = self.currently_selected_account {
|
||||||
@@ -69,17 +55,8 @@ impl AccountManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_account(
|
pub fn add_account(&mut self, account: Keypair) {
|
||||||
&mut self,
|
let _ = self.key_store.add_key(&account);
|
||||||
key: FullKeypair,
|
|
||||||
wakeup: impl Fn() + Send + Sync + Clone + 'static,
|
|
||||||
) {
|
|
||||||
let _ = self.key_store.add_key(&key);
|
|
||||||
let relays = self
|
|
||||||
.relay_generator
|
|
||||||
.generate_relays_for(&key.pubkey, wakeup);
|
|
||||||
let account = UserAccount { key, relays };
|
|
||||||
|
|
||||||
self.accounts.push(account)
|
self.accounts.push(account)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
47
src/app.rs
47
src/app.rs
@@ -5,11 +5,11 @@ use crate::error::Error;
|
|||||||
use crate::frame_history::FrameHistory;
|
use crate::frame_history::FrameHistory;
|
||||||
use crate::imgcache::ImageCache;
|
use crate::imgcache::ImageCache;
|
||||||
use crate::notecache::{CachedNote, NoteCache};
|
use crate::notecache::{CachedNote, NoteCache};
|
||||||
use crate::relay_pool_manager::create_wakeup;
|
use crate::route::Route;
|
||||||
use crate::timeline;
|
use crate::timeline;
|
||||||
use crate::timeline::{NoteRef, Timeline, ViewFilter};
|
use crate::timeline::{NoteRef, Timeline, ViewFilter};
|
||||||
use crate::ui::profile::SimpleProfilePreviewController;
|
use crate::ui::profile::SimpleProfilePreviewController;
|
||||||
use crate::ui::{is_mobile, DesktopGlobalPopup, DesktopSidePanel, View};
|
use crate::ui::{is_mobile, DesktopSidePanel};
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
|
|
||||||
use egui::{Context, Frame, Style};
|
use egui::{Context, Frame, Style};
|
||||||
@@ -39,6 +39,8 @@ pub struct Damus {
|
|||||||
note_cache: NoteCache,
|
note_cache: NoteCache,
|
||||||
pool: RelayPool,
|
pool: RelayPool,
|
||||||
|
|
||||||
|
/// global navigation for account management popups, etc.
|
||||||
|
nav: Vec<Route>,
|
||||||
pub textmode: bool,
|
pub textmode: bool,
|
||||||
|
|
||||||
pub timelines: Vec<Timeline>,
|
pub timelines: Vec<Timeline>,
|
||||||
@@ -645,6 +647,7 @@ impl Damus {
|
|||||||
img_cache: ImageCache::new(imgcache_dir),
|
img_cache: ImageCache::new(imgcache_dir),
|
||||||
note_cache: NoteCache::default(),
|
note_cache: NoteCache::default(),
|
||||||
selected_timeline: 0,
|
selected_timeline: 0,
|
||||||
|
nav: Vec::with_capacity(6),
|
||||||
timelines,
|
timelines,
|
||||||
textmode: false,
|
textmode: false,
|
||||||
ndb: Ndb::new(data_path.as_ref().to_str().expect("db path ok"), &config).expect("ndb"),
|
ndb: Ndb::new(data_path.as_ref().to_str().expect("db path ok"), &config).expect("ndb"),
|
||||||
@@ -653,9 +656,6 @@ impl Damus {
|
|||||||
None,
|
None,
|
||||||
// TODO: use correct KeyStorage mechanism for current OS arch
|
// TODO: use correct KeyStorage mechanism for current OS arch
|
||||||
crate::key_storage::KeyStorage::None,
|
crate::key_storage::KeyStorage::None,
|
||||||
// TODO: setting for relay generator
|
|
||||||
crate::relay_generation::RelayGenerator::Constant,
|
|
||||||
create_wakeup(&cc.egui_ctx),
|
|
||||||
),
|
),
|
||||||
//compose: "".to_string(),
|
//compose: "".to_string(),
|
||||||
frame_history: FrameHistory::default(),
|
frame_history: FrameHistory::default(),
|
||||||
@@ -680,14 +680,10 @@ impl Damus {
|
|||||||
note_cache: NoteCache::default(),
|
note_cache: NoteCache::default(),
|
||||||
selected_timeline: 0,
|
selected_timeline: 0,
|
||||||
timelines,
|
timelines,
|
||||||
|
nav: vec![],
|
||||||
textmode: false,
|
textmode: false,
|
||||||
ndb: Ndb::new(data_path.as_ref().to_str().expect("db path ok"), &config).expect("ndb"),
|
ndb: Ndb::new(data_path.as_ref().to_str().expect("db path ok"), &config).expect("ndb"),
|
||||||
account_manager: AccountManager::new(
|
account_manager: AccountManager::new(None, crate::key_storage::KeyStorage::None),
|
||||||
None,
|
|
||||||
crate::key_storage::KeyStorage::None,
|
|
||||||
crate::relay_generation::RelayGenerator::Constant,
|
|
||||||
|| {},
|
|
||||||
),
|
|
||||||
frame_history: FrameHistory::default(),
|
frame_history: FrameHistory::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -852,25 +848,8 @@ fn render_damus_desktop(ctx: &egui::Context, app: &mut Damus) {
|
|||||||
Size::remainder()
|
Size::remainder()
|
||||||
};
|
};
|
||||||
|
|
||||||
if app.timelines.len() == 1 {
|
|
||||||
DesktopSidePanel::panel().show(ctx, |ui| {
|
|
||||||
DesktopSidePanel::new(
|
|
||||||
&mut app.account_manager,
|
|
||||||
SimpleProfilePreviewController::new(&app.ndb, &mut app.img_cache),
|
|
||||||
)
|
|
||||||
.inner(ui);
|
|
||||||
});
|
|
||||||
main_panel(&ctx.style()).show(ctx, |ui| {
|
|
||||||
DesktopGlobalPopup::new(app).ui(ui);
|
|
||||||
timeline::timeline_view(ui, app, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
main_panel(&ctx.style()).show(ctx, |ui| {
|
main_panel(&ctx.style()).show(ctx, |ui| {
|
||||||
ui.spacing_mut().item_spacing.x = 0.0;
|
ui.spacing_mut().item_spacing.x = 0.0;
|
||||||
DesktopGlobalPopup::new(app).ui(ui);
|
|
||||||
if need_scroll {
|
if need_scroll {
|
||||||
egui::ScrollArea::horizontal().show(ui, |ui| {
|
egui::ScrollArea::horizontal().show(ui, |ui| {
|
||||||
timelines_view(ui, panel_sizes, app, app.timelines.len());
|
timelines_view(ui, panel_sizes, app, app.timelines.len());
|
||||||
@@ -888,11 +867,17 @@ fn timelines_view(ui: &mut egui::Ui, sizes: Size, app: &mut Damus, timelines: us
|
|||||||
.clip(true)
|
.clip(true)
|
||||||
.horizontal(|mut strip| {
|
.horizontal(|mut strip| {
|
||||||
strip.cell(|ui| {
|
strip.cell(|ui| {
|
||||||
DesktopSidePanel::new(
|
if DesktopSidePanel::new(
|
||||||
&mut app.account_manager,
|
app.account_manager
|
||||||
|
.get_selected_account()
|
||||||
|
.map(|a| a.pubkey.bytes()),
|
||||||
SimpleProfilePreviewController::new(&app.ndb, &mut app.img_cache),
|
SimpleProfilePreviewController::new(&app.ndb, &mut app.img_cache),
|
||||||
)
|
)
|
||||||
.inner(ui)
|
.show(ui)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
// clicked pfp
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
for timeline_ind in 0..timelines {
|
for timeline_ind in 0..timelines {
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ pub async fn get_login_key(key: &str) -> Result<Keypair, LoginError> {
|
|||||||
} else if let Ok(pubkey) = Pubkey::try_from_hex_str_with_verify(tmp_key) {
|
} else if let Ok(pubkey) = Pubkey::try_from_hex_str_with_verify(tmp_key) {
|
||||||
Ok(Keypair::only_pubkey(pubkey))
|
Ok(Keypair::only_pubkey(pubkey))
|
||||||
} else if let Ok(secret_key) = SecretKey::from_str(tmp_key) {
|
} else if let Ok(secret_key) = SecretKey::from_str(tmp_key) {
|
||||||
Ok(Keypair::new(secret_key))
|
Ok(Keypair::from_secret(secret_key))
|
||||||
} else {
|
} else {
|
||||||
Err(LoginError::InvalidKey)
|
Err(LoginError::InvalidKey)
|
||||||
}
|
}
|
||||||
@@ -181,7 +181,7 @@ mod tests {
|
|||||||
|
|
||||||
promise_assert!(
|
promise_assert!(
|
||||||
assert_eq,
|
assert_eq,
|
||||||
Ok(Keypair::new(expected_privkey)),
|
Ok(Keypair::from_secret(expected_privkey)),
|
||||||
&login_key_result
|
&login_key_result
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -194,7 +194,7 @@ mod tests {
|
|||||||
|
|
||||||
promise_assert!(
|
promise_assert!(
|
||||||
assert_eq,
|
assert_eq,
|
||||||
Ok(Keypair::new(expected_privkey)),
|
Ok(Keypair::from_secret(expected_privkey)),
|
||||||
&login_key_result
|
&login_key_result
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use enostr::FullKeypair;
|
use enostr::Keypair;
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use crate::macos_key_storage::MacOSKeyStorage;
|
use crate::macos_key_storage::MacOSKeyStorage;
|
||||||
@@ -17,15 +17,15 @@ pub enum KeyStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl KeyStorage {
|
impl KeyStorage {
|
||||||
pub fn get_keys(&self) -> Result<Vec<FullKeypair>, KeyStorageError> {
|
pub fn get_keys(&self) -> Result<Vec<Keypair>, KeyStorageError> {
|
||||||
match self {
|
match self {
|
||||||
Self::None => Ok(Vec::new()),
|
Self::None => Ok(Vec::new()),
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
Self::MacOS => Ok(MacOSKeyStorage::new(SERVICE_NAME).get_all_fullkeypairs()),
|
Self::MacOS => Ok(MacOSKeyStorage::new(SERVICE_NAME).get_all_keypairs()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_key(&self, key: &FullKeypair) -> Result<(), KeyStorageError> {
|
pub fn add_key(&self, key: &Keypair) -> Result<(), KeyStorageError> {
|
||||||
let _ = key;
|
let _ = key;
|
||||||
match self {
|
match self {
|
||||||
Self::None => Ok(()),
|
Self::None => Ok(()),
|
||||||
@@ -34,7 +34,7 @@ impl KeyStorage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_key(&self, key: &FullKeypair) -> Result<(), KeyStorageError> {
|
pub fn remove_key(&self, key: &Keypair) -> Result<(), KeyStorageError> {
|
||||||
let _ = key;
|
let _ = key;
|
||||||
match self {
|
match self {
|
||||||
Self::None => Ok(()),
|
Self::None => Ok(()),
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ mod profile;
|
|||||||
mod relay_generation;
|
mod relay_generation;
|
||||||
pub mod relay_pool_manager;
|
pub mod relay_pool_manager;
|
||||||
mod result;
|
mod result;
|
||||||
|
mod route;
|
||||||
mod test_data;
|
mod test_data;
|
||||||
mod time;
|
mod time;
|
||||||
mod timecache;
|
mod timecache;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#![cfg(target_os = "macos")]
|
#![cfg(target_os = "macos")]
|
||||||
|
|
||||||
use enostr::{FullKeypair, Pubkey, SecretKey};
|
use enostr::{Keypair, Pubkey, SecretKey};
|
||||||
|
|
||||||
use security_framework::item::{ItemClass, ItemSearchOptions, Limit, SearchResult};
|
use security_framework::item::{ItemClass, ItemSearchOptions, Limit, SearchResult};
|
||||||
use security_framework::passwords::{delete_generic_password, set_generic_password};
|
use security_framework::passwords::{delete_generic_password, set_generic_password};
|
||||||
@@ -16,11 +16,13 @@ impl<'a> MacOSKeyStorage<'a> {
|
|||||||
MacOSKeyStorage { service_name }
|
MacOSKeyStorage { service_name }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_key(&self, key: &FullKeypair) -> Result<(), KeyStorageError> {
|
pub fn add_key(&self, key: &Keypair) -> Result<(), KeyStorageError> {
|
||||||
match set_generic_password(
|
match set_generic_password(
|
||||||
self.service_name,
|
self.service_name,
|
||||||
key.pubkey.hex().as_str(),
|
key.pubkey.hex().as_str(),
|
||||||
key.secret_key.as_secret_bytes(),
|
key.secret_key
|
||||||
|
.as_ref()
|
||||||
|
.map_or_else(|| &[] as &[u8], |sc| sc.as_secret_bytes()),
|
||||||
) {
|
) {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
Err(_) => Err(KeyStorageError::Addition(key.pubkey.hex())),
|
Err(_) => Err(KeyStorageError::Addition(key.pubkey.hex())),
|
||||||
@@ -82,12 +84,12 @@ impl<'a> MacOSKeyStorage<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all_fullkeypairs(&self) -> Vec<FullKeypair> {
|
pub fn get_all_keypairs(&self) -> Vec<Keypair> {
|
||||||
self.get_pubkeys()
|
self.get_pubkeys()
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|pubkey| {
|
.map(|pubkey| {
|
||||||
let maybe_secret = self.get_secret_key_for_pubkey(pubkey);
|
let maybe_secret = self.get_secret_key_for_pubkey(pubkey);
|
||||||
maybe_secret.map(|secret| FullKeypair::new(pubkey.clone(), secret))
|
Keypair::new(pubkey.clone(), maybe_secret)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@@ -106,6 +108,8 @@ impl<'a> MacOSKeyStorage<'a> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use enostr::FullKeypair;
|
||||||
|
|
||||||
static TEST_SERVICE_NAME: &str = "NOTEDECKTEST";
|
static TEST_SERVICE_NAME: &str = "NOTEDECKTEST";
|
||||||
static STORAGE: MacOSKeyStorage = MacOSKeyStorage {
|
static STORAGE: MacOSKeyStorage = MacOSKeyStorage {
|
||||||
service_name: TEST_SERVICE_NAME,
|
service_name: TEST_SERVICE_NAME,
|
||||||
@@ -119,7 +123,7 @@ mod tests {
|
|||||||
fn add_and_remove_test_pubkey_only() {
|
fn add_and_remove_test_pubkey_only() {
|
||||||
let num_keys_before_test = STORAGE.get_pubkeys().len();
|
let num_keys_before_test = STORAGE.get_pubkeys().len();
|
||||||
|
|
||||||
let keypair = FullKeypair::generate();
|
let keypair = FullKeypair::generate().to_keypair();
|
||||||
let add_result = STORAGE.add_key(&keypair);
|
let add_result = STORAGE.add_key(&keypair);
|
||||||
assert_eq!(add_result, Ok(()));
|
assert_eq!(add_result, Ok(()));
|
||||||
|
|
||||||
@@ -134,18 +138,20 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn add_and_remove_full_n(n: usize) {
|
fn add_and_remove_full_n(n: usize) {
|
||||||
let num_keys_before_test = STORAGE.get_all_fullkeypairs().len();
|
let num_keys_before_test = STORAGE.get_all_keypairs().len();
|
||||||
// there must be zero keys in storage for the test to work as intended
|
// there must be zero keys in storage for the test to work as intended
|
||||||
assert_eq!(num_keys_before_test, 0);
|
assert_eq!(num_keys_before_test, 0);
|
||||||
|
|
||||||
let expected_keypairs: Vec<FullKeypair> = (0..n).map(|_| FullKeypair::generate()).collect();
|
let expected_keypairs: Vec<Keypair> = (0..n)
|
||||||
|
.map(|_| FullKeypair::generate().to_keypair())
|
||||||
|
.collect();
|
||||||
|
|
||||||
expected_keypairs.iter().for_each(|keypair| {
|
expected_keypairs.iter().for_each(|keypair| {
|
||||||
let add_result = STORAGE.add_key(keypair);
|
let add_result = STORAGE.add_key(keypair);
|
||||||
assert_eq!(add_result, Ok(()));
|
assert_eq!(add_result, Ok(()));
|
||||||
});
|
});
|
||||||
|
|
||||||
let asserted_keypairs = STORAGE.get_all_fullkeypairs();
|
let asserted_keypairs = STORAGE.get_all_keypairs();
|
||||||
assert_eq!(expected_keypairs, asserted_keypairs);
|
assert_eq!(expected_keypairs, asserted_keypairs);
|
||||||
|
|
||||||
expected_keypairs.iter().for_each(|keypair| {
|
expected_keypairs.iter().for_each(|keypair| {
|
||||||
@@ -153,7 +159,7 @@ mod tests {
|
|||||||
assert_eq!(remove_result, Ok(()));
|
assert_eq!(remove_result, Ok(()));
|
||||||
});
|
});
|
||||||
|
|
||||||
let num_keys_after_test = STORAGE.get_all_fullkeypairs().len();
|
let num_keys_after_test = STORAGE.get_all_keypairs().len();
|
||||||
assert_eq!(num_keys_after_test, 0);
|
assert_eq!(num_keys_after_test, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,45 +1,7 @@
|
|||||||
use enostr::{Pubkey, RelayPool};
|
use enostr::RelayPool;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
pub enum RelayGenerator {
|
fn test_relay_pool(wakeup: impl Fn() + Send + Sync + Clone + 'static) -> RelayPool {
|
||||||
GossipModel,
|
|
||||||
Nip65,
|
|
||||||
Constant,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RelayGenerator {
|
|
||||||
pub fn generate_relays_for(
|
|
||||||
&self,
|
|
||||||
key: &Pubkey,
|
|
||||||
wakeup: impl Fn() + Send + Sync + Clone + 'static,
|
|
||||||
) -> RelayPool {
|
|
||||||
match self {
|
|
||||||
Self::GossipModel => generate_relays_gossip(key, wakeup),
|
|
||||||
Self::Nip65 => generate_relays_nip65(key, wakeup),
|
|
||||||
Self::Constant => generate_constant_relays(wakeup),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_relays_gossip(
|
|
||||||
key: &Pubkey,
|
|
||||||
wakeup: impl Fn() + Send + Sync + Clone + 'static,
|
|
||||||
) -> RelayPool {
|
|
||||||
let _ = wakeup;
|
|
||||||
let _ = key;
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_relays_nip65(
|
|
||||||
key: &Pubkey,
|
|
||||||
wakeup: impl Fn() + Send + Sync + Clone + 'static,
|
|
||||||
) -> RelayPool {
|
|
||||||
let _ = wakeup;
|
|
||||||
let _ = key;
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_constant_relays(wakeup: impl Fn() + Send + Sync + Clone + 'static) -> RelayPool {
|
|
||||||
let mut pool = RelayPool::new();
|
let mut pool = RelayPool::new();
|
||||||
|
|
||||||
if let Err(e) = pool.add_url("ws://localhost:8080".to_string(), wakeup.clone()) {
|
if let Err(e) = pool.add_url("ws://localhost:8080".to_string(), wakeup.clone()) {
|
||||||
|
|||||||
7
src/route.rs
Normal file
7
src/route.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
use nostrdb::NoteKey;
|
||||||
|
|
||||||
|
/// App routing. These describe different places you can go inside Notedeck.
|
||||||
|
pub enum Route {
|
||||||
|
ManageAccount,
|
||||||
|
Thread(NoteKey),
|
||||||
|
}
|
||||||
@@ -7,7 +7,6 @@ use crate::{
|
|||||||
account_manager::{AccountManager, UserAccount},
|
account_manager::{AccountManager, UserAccount},
|
||||||
imgcache::ImageCache,
|
imgcache::ImageCache,
|
||||||
key_storage::KeyStorage,
|
key_storage::KeyStorage,
|
||||||
relay_generation::RelayGenerator,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(unused_must_use)]
|
#[allow(unused_must_use)]
|
||||||
@@ -81,26 +80,19 @@ pub fn get_test_accounts() -> Vec<UserAccount> {
|
|||||||
TEN_ACCOUNT_HEXES
|
TEN_ACCOUNT_HEXES
|
||||||
.iter()
|
.iter()
|
||||||
.map(|account_hex| {
|
.map(|account_hex| {
|
||||||
let key = FullKeypair::new(
|
let mut kp = FullKeypair::generate().to_keypair();
|
||||||
Pubkey::from_hex(account_hex).unwrap(),
|
kp.pubkey = Pubkey::from_hex(account_hex).unwrap();
|
||||||
FullKeypair::generate().secret_key,
|
kp
|
||||||
);
|
|
||||||
|
|
||||||
UserAccount {
|
|
||||||
key,
|
|
||||||
relays: sample_pool(),
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_accmgr_and_ndb_and_imgcache() -> (AccountManager, Ndb, ImageCache) {
|
pub fn get_accmgr_and_ndb_and_imgcache() -> (AccountManager, Ndb, ImageCache) {
|
||||||
let mut account_manager =
|
let mut account_manager = AccountManager::new(None, KeyStorage::None);
|
||||||
AccountManager::new(None, KeyStorage::None, RelayGenerator::Constant, || {});
|
|
||||||
let accounts = get_test_accounts();
|
let accounts = get_test_accounts();
|
||||||
accounts
|
for account in accounts {
|
||||||
.into_iter()
|
account_manager.add_account(account);
|
||||||
.for_each(|acc| account_manager.add_account(acc.key, || {}));
|
}
|
||||||
|
|
||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
config.set_ingester_threads(2);
|
config.set_ingester_threads(2);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use crate::colors::PINK;
|
use crate::colors::PINK;
|
||||||
use crate::ui::global_popup::FromApp;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
account_manager::AccountManager,
|
account_manager::AccountManager,
|
||||||
app_style::NotedeckTextStyle,
|
app_style::NotedeckTextStyle,
|
||||||
@@ -7,7 +6,6 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use egui::{Align, Button, Frame, Image, Layout, RichText, ScrollArea, Vec2};
|
use egui::{Align, Button, Frame, Image, Layout, RichText, ScrollArea, Vec2};
|
||||||
|
|
||||||
use super::global_popup::GlobalPopupType;
|
|
||||||
use super::profile::preview::SimpleProfilePreview;
|
use super::profile::preview::SimpleProfilePreview;
|
||||||
use super::profile::{ProfilePreviewOp, SimpleProfilePreviewController};
|
use super::profile::{ProfilePreviewOp, SimpleProfilePreviewController};
|
||||||
|
|
||||||
@@ -39,7 +37,7 @@ impl<'a> AccountManagementView<'a> {
|
|||||||
|
|
||||||
fn show(&mut self, ui: &mut egui::Ui) {
|
fn show(&mut self, ui: &mut egui::Ui) {
|
||||||
Frame::none().outer_margin(24.0).show(ui, |ui| {
|
Frame::none().outer_margin(24.0).show(ui, |ui| {
|
||||||
ui.add(self.top_section_buttons_widget());
|
self.top_section_buttons_widget(ui);
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
scroll_area().show(ui, |ui| {
|
scroll_area().show(ui, |ui| {
|
||||||
self.show_accounts(ui);
|
self.show_accounts(ui);
|
||||||
@@ -86,8 +84,9 @@ impl<'a> AccountManagementView<'a> {
|
|||||||
fn show_mobile(&mut self, ui: &mut egui::Ui) -> egui::Response {
|
fn show_mobile(&mut self, ui: &mut egui::Ui) -> egui::Response {
|
||||||
egui::CentralPanel::default()
|
egui::CentralPanel::default()
|
||||||
.show(ui.ctx(), |ui| {
|
.show(ui.ctx(), |ui| {
|
||||||
ui.add(mobile_title());
|
mobile_title(ui);
|
||||||
ui.add(self.top_section_buttons_widget());
|
self.top_section_buttons_widget(ui);
|
||||||
|
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
scroll_area().show(ui, |ui| {
|
scroll_area().show(ui, |ui| {
|
||||||
self.show_accounts_mobile(ui);
|
self.show_accounts_mobile(ui);
|
||||||
@@ -96,34 +95,32 @@ impl<'a> AccountManagementView<'a> {
|
|||||||
.response
|
.response
|
||||||
}
|
}
|
||||||
|
|
||||||
fn top_section_buttons_widget(&mut self) -> impl egui::Widget + '_ {
|
fn top_section_buttons_widget(&mut self, ui: &mut egui::Ui) -> egui::Response {
|
||||||
|ui: &mut egui::Ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.allocate_ui_with_layout(
|
||||||
ui.allocate_ui_with_layout(
|
Vec2::new(ui.available_size_before_wrap().x, 32.0),
|
||||||
Vec2::new(ui.available_size_before_wrap().x, 32.0),
|
Layout::left_to_right(egui::Align::Center),
|
||||||
Layout::left_to_right(egui::Align::Center),
|
|ui| {
|
||||||
|ui| {
|
if ui.add(add_account_button()).clicked() {
|
||||||
if ui.add(add_account_button()).clicked() {
|
// TODO: route to AccountLoginView
|
||||||
// TODO: route to AccountLoginView
|
}
|
||||||
}
|
},
|
||||||
},
|
);
|
||||||
);
|
|
||||||
|
|
||||||
// UNCOMMENT FOR LOGOUTALL BUTTON
|
// UNCOMMENT FOR LOGOUTALL BUTTON
|
||||||
// ui.allocate_ui_with_layout(
|
// ui.allocate_ui_with_layout(
|
||||||
// Vec2::new(ui.available_size_before_wrap().x, 32.0),
|
// Vec2::new(ui.available_size_before_wrap().x, 32.0),
|
||||||
// Layout::right_to_left(egui::Align::Center),
|
// Layout::right_to_left(egui::Align::Center),
|
||||||
// |ui| {
|
// |ui| {
|
||||||
// if ui.add(logout_all_button()).clicked() {
|
// if ui.add(logout_all_button()).clicked() {
|
||||||
// for index in (0..self.account_manager.num_accounts()).rev() {
|
// for index in (0..self.account_manager.num_accounts()).rev() {
|
||||||
// self.account_manager.remove_account(index);
|
// self.account_manager.remove_account(index);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// },
|
// },
|
||||||
// );
|
// );
|
||||||
})
|
})
|
||||||
.response
|
.response
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,26 +163,15 @@ fn account_card_ui() -> fn(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromApp<'a> for AccountManagementView<'a> {
|
fn mobile_title(ui: &mut egui::Ui) -> egui::Response {
|
||||||
fn from_app(app: &'a mut crate::Damus) -> Self {
|
ui.vertical_centered(|ui| {
|
||||||
AccountManagementView::new(
|
ui.label(
|
||||||
&mut app.account_manager,
|
RichText::new("Account Management")
|
||||||
SimpleProfilePreviewController::new(&app.ndb, &mut app.img_cache),
|
.text_style(NotedeckTextStyle::Heading2.text_style())
|
||||||
)
|
.strong(),
|
||||||
}
|
);
|
||||||
}
|
})
|
||||||
|
.response
|
||||||
fn mobile_title() -> impl egui::Widget {
|
|
||||||
|ui: &mut egui::Ui| {
|
|
||||||
ui.vertical_centered(|ui| {
|
|
||||||
ui.label(
|
|
||||||
RichText::new(GlobalPopupType::AccountManagement.title())
|
|
||||||
.text_style(NotedeckTextStyle::Heading2.text_style())
|
|
||||||
.strong(),
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.response
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scroll_area() -> ScrollArea {
|
fn scroll_area() -> ScrollArea {
|
||||||
|
|||||||
@@ -1,22 +1,43 @@
|
|||||||
use crate::{account_manager::UserAccount, colors::PINK, ui};
|
use crate::{
|
||||||
|
account_manager::{AccountManager, UserAccount},
|
||||||
|
colors::PINK,
|
||||||
|
profile::DisplayName,
|
||||||
|
ui, Result,
|
||||||
|
};
|
||||||
use egui::{
|
use egui::{
|
||||||
Align, Button, Color32, Frame, Id, Image, Layout, Margin, RichText, Rounding, ScrollArea,
|
Align, Button, Color32, Frame, Id, Image, Layout, Margin, RichText, Rounding, ScrollArea,
|
||||||
Sense, Vec2,
|
Sense, Vec2,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::account_manager::AccountManager;
|
use super::profile::{preview::SimpleProfilePreview, SimpleProfilePreviewController};
|
||||||
|
|
||||||
use super::{
|
|
||||||
profile::{preview::SimpleProfilePreview, SimpleProfilePreviewController},
|
|
||||||
state_in_memory::{STATE_ACCOUNT_MANAGEMENT, STATE_ACCOUNT_SWITCHER, STATE_SIDE_PANEL},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct AccountSelectionWidget<'a> {
|
pub struct AccountSelectionWidget<'a> {
|
||||||
account_manager: &'a mut AccountManager,
|
account_manager: &'a AccountManager,
|
||||||
simple_preview_controller: SimpleProfilePreviewController<'a>,
|
simple_preview_controller: SimpleProfilePreviewController<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum AccountSelectAction {
|
||||||
|
RemoveAccount { index: usize },
|
||||||
|
SelectAccount { index: usize },
|
||||||
|
OpenAccountManagement,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct AccountSelectResponse {
|
||||||
|
action: Option<AccountSelectAction>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> AccountSelectionWidget<'a> {
|
impl<'a> AccountSelectionWidget<'a> {
|
||||||
|
pub fn new(
|
||||||
|
account_manager: &'a AccountManager,
|
||||||
|
simple_preview_controller: SimpleProfilePreviewController<'a>,
|
||||||
|
) -> Self {
|
||||||
|
AccountSelectionWidget {
|
||||||
|
account_manager,
|
||||||
|
simple_preview_controller,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn ui(&'a mut self, ui: &mut egui::Ui) {
|
pub fn ui(&'a mut self, ui: &mut egui::Ui) {
|
||||||
if ui::is_mobile() {
|
if ui::is_mobile() {
|
||||||
self.show_mobile(ui);
|
self.show_mobile(ui);
|
||||||
@@ -25,33 +46,43 @@ impl<'a> AccountSelectionWidget<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, ui: &mut egui::Ui) {
|
fn show(&mut self, ui: &mut egui::Ui) -> AccountSelectResponse {
|
||||||
|
let mut res = AccountSelectResponse::default();
|
||||||
|
let mut selected_index = self.account_manager.get_selected_account_index();
|
||||||
|
|
||||||
Frame::none().outer_margin(8.0).show(ui, |ui| {
|
Frame::none().outer_margin(8.0).show(ui, |ui| {
|
||||||
ui.add(top_section_widget());
|
res = top_section_widget(ui);
|
||||||
|
|
||||||
scroll_area().show(ui, |ui| {
|
scroll_area().show(ui, |ui| {
|
||||||
self.show_accounts(ui);
|
if let Some(index) = self.show_accounts(ui) {
|
||||||
|
selected_index = Some(index);
|
||||||
|
res.action = Some(AccountSelectAction::SelectAccount { index });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
ui.add(add_account_button());
|
ui.add(add_account_button());
|
||||||
|
|
||||||
if let Some(account_index) = self.account_manager.get_selected_account_index() {
|
if let Some(index) = selected_index {
|
||||||
ui.add_space(8.0);
|
if let Some(account) = self.account_manager.get_account(index) {
|
||||||
if self.handle_sign_out(ui, account_index) {
|
ui.add_space(8.0);
|
||||||
self.account_manager.remove_account(account_index);
|
if self.handle_sign_out(ui, account) {
|
||||||
|
res.action = Some(AccountSelectAction::RemoveAccount { index })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_sign_out(&mut self, ui: &mut egui::Ui, account_index: usize) -> bool {
|
fn handle_sign_out(&mut self, ui: &mut egui::Ui, account: &UserAccount) -> bool {
|
||||||
if let Some(account) = self.account_manager.get_account(account_index) {
|
if let Ok(response) = self.sign_out_button(ui, account) {
|
||||||
if let Some(response) = self.sign_out_button(ui, account) {
|
response.clicked()
|
||||||
return response.clicked();
|
} else {
|
||||||
}
|
false
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_mobile(&mut self, ui: &mut egui::Ui) -> egui::Response {
|
fn show_mobile(&mut self, ui: &mut egui::Ui) -> egui::Response {
|
||||||
@@ -59,19 +90,19 @@ impl<'a> AccountSelectionWidget<'a> {
|
|||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_accounts(&mut self, ui: &mut egui::Ui) {
|
fn show_accounts(&mut self, ui: &mut egui::Ui) -> Option<usize> {
|
||||||
self.simple_preview_controller.view_profile_previews(
|
self.simple_preview_controller.view_profile_previews(
|
||||||
self.account_manager,
|
self.account_manager,
|
||||||
ui,
|
ui,
|
||||||
account_switcher_card_ui(),
|
account_switcher_card_ui(),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign_out_button(&self, ui: &mut egui::Ui, account: &UserAccount) -> Option<egui::Response> {
|
fn sign_out_button(&self, ui: &mut egui::Ui, account: &UserAccount) -> Result<egui::Response> {
|
||||||
self.simple_preview_controller.show_with_nickname(
|
self.simple_preview_controller.show_with_nickname(
|
||||||
ui,
|
ui,
|
||||||
&account.key.pubkey,
|
account.pubkey.bytes(),
|
||||||
|ui, username| {
|
|ui: &mut egui::Ui, username: &DisplayName| {
|
||||||
let img_data = egui::include_image!("../../assets/icons/signout_icon_4x.png");
|
let img_data = egui::include_image!("../../assets/icons/signout_icon_4x.png");
|
||||||
let img = Image::new(img_data).fit_to_exact_size(Vec2::new(16.0, 16.0));
|
let img = Image::new(img_data).fit_to_exact_size(Vec2::new(16.0, 16.0));
|
||||||
let button = egui::Button::image_and_text(
|
let button = egui::Button::image_and_text(
|
||||||
@@ -88,18 +119,6 @@ impl<'a> AccountSelectionWidget<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> AccountSelectionWidget<'a> {
|
|
||||||
pub fn new(
|
|
||||||
account_manager: &'a mut AccountManager,
|
|
||||||
simple_preview_controller: SimpleProfilePreviewController<'a>,
|
|
||||||
) -> Self {
|
|
||||||
AccountSelectionWidget {
|
|
||||||
account_manager,
|
|
||||||
simple_preview_controller,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn account_switcher_card_ui() -> fn(
|
fn account_switcher_card_ui() -> fn(
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
preview: SimpleProfilePreview,
|
preview: SimpleProfilePreview,
|
||||||
@@ -147,32 +166,29 @@ fn selection_widget() -> impl egui::Widget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn top_section_widget() -> impl egui::Widget {
|
fn top_section_widget(ui: &mut egui::Ui) -> AccountSelectResponse {
|
||||||
|ui: &mut egui::Ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.horizontal(|ui| {
|
let mut resp = AccountSelectResponse::default();
|
||||||
ui.allocate_ui_with_layout(
|
|
||||||
Vec2::new(ui.available_size_before_wrap().x, 32.0),
|
|
||||||
Layout::left_to_right(egui::Align::Center),
|
|
||||||
|ui| ui.add(account_switcher_title()),
|
|
||||||
);
|
|
||||||
|
|
||||||
ui.allocate_ui_with_layout(
|
ui.allocate_ui_with_layout(
|
||||||
Vec2::new(ui.available_size_before_wrap().x, 32.0),
|
Vec2::new(ui.available_size_before_wrap().x, 32.0),
|
||||||
Layout::right_to_left(egui::Align::Center),
|
Layout::left_to_right(egui::Align::Center),
|
||||||
|ui| {
|
|ui| ui.add(account_switcher_title()),
|
||||||
if ui.add(manage_accounts_button()).clicked() {
|
);
|
||||||
STATE_ACCOUNT_SWITCHER.set_state(ui.ctx(), false);
|
|
||||||
STATE_SIDE_PANEL.set_state(
|
ui.allocate_ui_with_layout(
|
||||||
ui.ctx(),
|
Vec2::new(ui.available_size_before_wrap().x, 32.0),
|
||||||
Some(ui::global_popup::GlobalPopupType::AccountManagement),
|
Layout::right_to_left(egui::Align::Center),
|
||||||
);
|
|ui| {
|
||||||
STATE_ACCOUNT_MANAGEMENT.set_state(ui.ctx(), true);
|
if ui.add(manage_accounts_button()).clicked() {
|
||||||
}
|
resp.action = Some(AccountSelectAction::OpenAccountManagement);
|
||||||
},
|
}
|
||||||
);
|
},
|
||||||
})
|
);
|
||||||
.response
|
|
||||||
}
|
resp
|
||||||
|
})
|
||||||
|
.inner
|
||||||
}
|
}
|
||||||
|
|
||||||
fn manage_accounts_button() -> egui::Button<'static> {
|
fn manage_accounts_button() -> egui::Button<'static> {
|
||||||
@@ -227,7 +243,7 @@ mod previews {
|
|||||||
impl View for AccountSelectionPreview {
|
impl View for AccountSelectionPreview {
|
||||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||||
AccountSelectionWidget::new(
|
AccountSelectionWidget::new(
|
||||||
&mut self.account_manager,
|
&self.account_manager,
|
||||||
SimpleProfilePreviewController::new(&self.ndb, &mut self.img_cache),
|
SimpleProfilePreviewController::new(&self.ndb, &mut self.img_cache),
|
||||||
)
|
)
|
||||||
.ui(ui);
|
.ui(ui);
|
||||||
|
|||||||
@@ -1,168 +0,0 @@
|
|||||||
use egui::{Align2, CentralPanel, RichText, Vec2, Window};
|
|
||||||
|
|
||||||
use crate::Damus;
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
profile::SimpleProfilePreviewController,
|
|
||||||
state_in_memory::{STATE_ACCOUNT_MANAGEMENT, STATE_ACCOUNT_SWITCHER, STATE_SIDE_PANEL},
|
|
||||||
AccountManagementView, AccountSelectionWidget, View,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub enum GlobalPopupType {
|
|
||||||
AccountManagement,
|
|
||||||
AccountSwitcher,
|
|
||||||
}
|
|
||||||
|
|
||||||
static ACCOUNT_MANAGEMENT_TITLE: &str = "Manage accounts";
|
|
||||||
static ACCOUNT_SWITCHER_TITLE: &str = "Account switcher";
|
|
||||||
|
|
||||||
impl GlobalPopupType {
|
|
||||||
pub fn title(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
Self::AccountManagement => ACCOUNT_MANAGEMENT_TITLE,
|
|
||||||
Self::AccountSwitcher => ACCOUNT_SWITCHER_TITLE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait FromApp<'a> {
|
|
||||||
fn from_app(app: &'a mut crate::Damus) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn title(title_str: &'static str) -> RichText {
|
|
||||||
RichText::new(title_str).size(24.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn overlay_window<'a>(
|
|
||||||
open: &'a mut bool,
|
|
||||||
window_size: Vec2,
|
|
||||||
title_str: &'static str,
|
|
||||||
) -> Window<'a> {
|
|
||||||
egui::Window::new(title(title_str))
|
|
||||||
.anchor(Align2::CENTER_CENTER, [0.0, 0.0])
|
|
||||||
.collapsible(false)
|
|
||||||
.auto_sized()
|
|
||||||
.movable(false)
|
|
||||||
.open(open)
|
|
||||||
.default_size(window_size)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn account_switcher_window(open: &'_ mut bool) -> Window<'_> {
|
|
||||||
egui::Window::new("account switcher")
|
|
||||||
.title_bar(false)
|
|
||||||
.collapsible(false)
|
|
||||||
.anchor(Align2::LEFT_BOTTOM, Vec2::new(0.0, -52.0))
|
|
||||||
.fixed_size(Vec2::new(360.0, 406.0))
|
|
||||||
.open(open)
|
|
||||||
.movable(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
static MARGIN: Vec2 = Vec2 { x: 100.0, y: 100.0 };
|
|
||||||
|
|
||||||
pub struct DesktopGlobalPopup<'a> {
|
|
||||||
app: &'a mut Damus,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> View for DesktopGlobalPopup<'a> {
|
|
||||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
|
||||||
DesktopGlobalPopup::global_popup(self.app, ui.ctx())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> DesktopGlobalPopup<'a> {
|
|
||||||
pub fn new(app: &'a mut Damus) -> Self {
|
|
||||||
DesktopGlobalPopup { app }
|
|
||||||
}
|
|
||||||
pub fn global_popup(app: &mut Damus, ctx: &egui::Context) {
|
|
||||||
CentralPanel::default().show(ctx, |ui| {
|
|
||||||
if let Some(popup) = STATE_SIDE_PANEL.get_state(ctx) {
|
|
||||||
match popup {
|
|
||||||
GlobalPopupType::AccountManagement => {
|
|
||||||
Self::account_management(app, ctx, ui, popup.title());
|
|
||||||
}
|
|
||||||
GlobalPopupType::AccountSwitcher => {
|
|
||||||
let mut show_account_switcher = STATE_ACCOUNT_SWITCHER.get_state(ctx);
|
|
||||||
if show_account_switcher {
|
|
||||||
STATE_ACCOUNT_MANAGEMENT.set_state(ctx, false);
|
|
||||||
account_switcher_window(&mut show_account_switcher).show(ctx, |ui| {
|
|
||||||
AccountSelectionWidget::new(
|
|
||||||
&mut app.account_manager,
|
|
||||||
SimpleProfilePreviewController::new(
|
|
||||||
&app.ndb,
|
|
||||||
&mut app.img_cache,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.ui(ui);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn account_management(
|
|
||||||
app: &mut Damus,
|
|
||||||
ctx: &egui::Context,
|
|
||||||
ui: &mut egui::Ui,
|
|
||||||
title: &'static str,
|
|
||||||
) {
|
|
||||||
let available_size = ui.available_size();
|
|
||||||
let window_size = available_size - MARGIN;
|
|
||||||
let mut show_account_management = STATE_ACCOUNT_MANAGEMENT.get_state(ctx);
|
|
||||||
if show_account_management {
|
|
||||||
overlay_window(&mut show_account_management, window_size, title).show(ctx, |ui| {
|
|
||||||
AccountManagementView::from_app(app).ui(ui);
|
|
||||||
});
|
|
||||||
// user could have closed the window, set the new state in egui memory
|
|
||||||
STATE_ACCOUNT_MANAGEMENT.set_state(ctx, show_account_management);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod preview {
|
|
||||||
use crate::{
|
|
||||||
test_data,
|
|
||||||
ui::{profile::SimpleProfilePreviewController, DesktopSidePanel, Preview, View},
|
|
||||||
Damus,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::DesktopGlobalPopup;
|
|
||||||
|
|
||||||
pub struct GlobalPopupPreview {
|
|
||||||
app: Damus,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Preview for DesktopGlobalPopup<'a> {
|
|
||||||
type Prev = GlobalPopupPreview;
|
|
||||||
|
|
||||||
fn preview() -> Self::Prev {
|
|
||||||
GlobalPopupPreview::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GlobalPopupPreview {
|
|
||||||
fn new() -> Self {
|
|
||||||
let mut app = Damus::mock(".");
|
|
||||||
let accounts = test_data::get_test_accounts();
|
|
||||||
accounts
|
|
||||||
.into_iter()
|
|
||||||
.for_each(|acc| app.account_manager.add_account(acc.key, || {}));
|
|
||||||
GlobalPopupPreview { app }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl View for GlobalPopupPreview {
|
|
||||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
|
||||||
let mut panel = DesktopSidePanel::new(
|
|
||||||
&mut self.app.account_manager,
|
|
||||||
SimpleProfilePreviewController::new(&self.app.ndb, &mut self.app.img_cache),
|
|
||||||
);
|
|
||||||
DesktopSidePanel::panel().show(ui.ctx(), |ui| panel.ui(ui));
|
|
||||||
DesktopGlobalPopup::new(&mut self.app).ui(ui);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,19 +2,16 @@ pub mod account_login_view;
|
|||||||
pub mod account_management;
|
pub mod account_management;
|
||||||
pub mod account_switcher;
|
pub mod account_switcher;
|
||||||
pub mod anim;
|
pub mod anim;
|
||||||
pub mod global_popup;
|
|
||||||
pub mod mention;
|
pub mod mention;
|
||||||
pub mod note;
|
pub mod note;
|
||||||
pub mod preview;
|
pub mod preview;
|
||||||
pub mod profile;
|
pub mod profile;
|
||||||
pub mod relay;
|
pub mod relay;
|
||||||
pub mod side_panel;
|
pub mod side_panel;
|
||||||
pub mod state_in_memory;
|
|
||||||
pub mod username;
|
pub mod username;
|
||||||
|
|
||||||
pub use account_management::AccountManagementView;
|
pub use account_management::AccountManagementView;
|
||||||
pub use account_switcher::AccountSelectionWidget;
|
pub use account_switcher::AccountSelectionWidget;
|
||||||
pub use global_popup::DesktopGlobalPopup;
|
|
||||||
pub use mention::Mention;
|
pub use mention::Mention;
|
||||||
pub use note::Note;
|
pub use note::Note;
|
||||||
pub use preview::{Preview, PreviewApp};
|
pub use preview::{Preview, PreviewApp};
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
use enostr::Pubkey;
|
|
||||||
use nostrdb::{Ndb, Transaction};
|
use nostrdb::{Ndb, Transaction};
|
||||||
|
|
||||||
use crate::{
|
use crate::{account_manager::AccountManager, imgcache::ImageCache, DisplayName, Result};
|
||||||
account_manager::AccountManager, imgcache::ImageCache,
|
|
||||||
ui::state_in_memory::STATE_ACCOUNT_SWITCHER, DisplayName,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
preview::{get_display_name, get_profile_url, SimpleProfilePreview},
|
preview::{get_display_name, get_profile_url, SimpleProfilePreview},
|
||||||
@@ -42,36 +38,48 @@ impl<'a> SimpleProfilePreviewController<'a> {
|
|||||||
|
|
||||||
let width = ui.available_width();
|
let width = ui.available_width();
|
||||||
|
|
||||||
|
let txn = if let Ok(txn) = Transaction::new(self.ndb) {
|
||||||
|
txn
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
for i in 0..account_manager.num_accounts() {
|
for i in 0..account_manager.num_accounts() {
|
||||||
if let Some(account) = account_manager.get_account(i) {
|
let account = if let Some(account) = account_manager.get_account(i) {
|
||||||
if let Ok(txn) = Transaction::new(self.ndb) {
|
account
|
||||||
let profile = self
|
} else {
|
||||||
.ndb
|
continue;
|
||||||
.get_profile_by_pubkey(&txn, account.key.pubkey.bytes());
|
};
|
||||||
|
|
||||||
if let Ok(profile) = profile {
|
let profile =
|
||||||
let preview = SimpleProfilePreview::new(&profile, self.img_cache);
|
if let Ok(profile) = self.ndb.get_profile_by_pubkey(&txn, account.pubkey.bytes()) {
|
||||||
|
profile
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
let is_selected =
|
let preview = SimpleProfilePreview::new(&profile, self.img_cache);
|
||||||
if let Some(selected) = account_manager.get_selected_account_index() {
|
|
||||||
i == selected
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(op) = add_preview_ui(ui, preview, width, is_selected) {
|
let is_selected = if let Some(selected) = account_manager.get_selected_account_index() {
|
||||||
match op {
|
i == selected
|
||||||
ProfilePreviewOp::RemoveAccount => {
|
} else {
|
||||||
if to_remove.is_none() {
|
false
|
||||||
to_remove = Some(Vec::new());
|
};
|
||||||
}
|
|
||||||
to_remove.as_mut().unwrap().push(i);
|
let op = if let Some(op) = add_preview_ui(ui, preview, width, is_selected) {
|
||||||
}
|
op
|
||||||
ProfilePreviewOp::SwitchTo => account_manager.select_account(i),
|
} else {
|
||||||
}
|
continue;
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
match op {
|
||||||
|
ProfilePreviewOp::RemoveAccount => {
|
||||||
|
if to_remove.is_none() {
|
||||||
|
to_remove = Some(Vec::new());
|
||||||
|
}
|
||||||
|
to_remove.as_mut().unwrap().push(i);
|
||||||
}
|
}
|
||||||
|
ProfilePreviewOp::SwitchTo => account_manager.select_account(i),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +88,7 @@ impl<'a> SimpleProfilePreviewController<'a> {
|
|||||||
|
|
||||||
pub fn view_profile_previews(
|
pub fn view_profile_previews(
|
||||||
&mut self,
|
&mut self,
|
||||||
account_manager: &mut AccountManager,
|
account_manager: &AccountManager,
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
add_preview_ui: fn(
|
add_preview_ui: fn(
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
@@ -89,60 +97,64 @@ impl<'a> SimpleProfilePreviewController<'a> {
|
|||||||
is_selected: bool,
|
is_selected: bool,
|
||||||
index: usize,
|
index: usize,
|
||||||
) -> bool,
|
) -> bool,
|
||||||
) {
|
) -> Option<usize> {
|
||||||
let width = ui.available_width();
|
let width = ui.available_width();
|
||||||
|
|
||||||
|
let txn = if let Ok(txn) = Transaction::new(self.ndb) {
|
||||||
|
txn
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
for i in 0..account_manager.num_accounts() {
|
for i in 0..account_manager.num_accounts() {
|
||||||
if let Some(account) = account_manager.get_account(i) {
|
let account = if let Some(account) = account_manager.get_account(i) {
|
||||||
if let Ok(txn) = Transaction::new(self.ndb) {
|
account
|
||||||
let profile = self
|
} else {
|
||||||
.ndb
|
continue;
|
||||||
.get_profile_by_pubkey(&txn, account.key.pubkey.bytes());
|
};
|
||||||
|
|
||||||
if let Ok(profile) = profile {
|
let profile =
|
||||||
let preview = SimpleProfilePreview::new(&profile, self.img_cache);
|
if let Ok(profile) = self.ndb.get_profile_by_pubkey(&txn, account.pubkey.bytes()) {
|
||||||
|
profile
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
let is_selected =
|
let preview = SimpleProfilePreview::new(&profile, self.img_cache);
|
||||||
if let Some(selected) = account_manager.get_selected_account_index() {
|
|
||||||
i == selected
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
if add_preview_ui(ui, preview, width, is_selected, i) {
|
let is_selected = if let Some(selected) = account_manager.get_selected_account_index() {
|
||||||
account_manager.select_account(i);
|
i == selected
|
||||||
STATE_ACCOUNT_SWITCHER.set_state(ui.ctx(), false);
|
} else {
|
||||||
}
|
false
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
if add_preview_ui(ui, preview, width, is_selected, i) {
|
||||||
|
return Some(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_with_nickname(
|
pub fn show_with_nickname(
|
||||||
&self,
|
&self,
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
key: &Pubkey,
|
key: &[u8; 32],
|
||||||
ui_element: fn(ui: &mut egui::Ui, username: &DisplayName) -> egui::Response,
|
ui_element: fn(ui: &mut egui::Ui, username: &DisplayName) -> egui::Response,
|
||||||
) -> Option<egui::Response> {
|
) -> Result<egui::Response> {
|
||||||
if let Ok(txn) = Transaction::new(self.ndb) {
|
let txn = Transaction::new(self.ndb)?;
|
||||||
let profile = self.ndb.get_profile_by_pubkey(&txn, key.bytes());
|
let profile = self.ndb.get_profile_by_pubkey(&txn, key)?;
|
||||||
|
Ok(ui_element(ui, &get_display_name(&profile)))
|
||||||
if let Ok(profile) = profile {
|
|
||||||
return Some(ui_element(ui, &get_display_name(&profile)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_with_pfp(
|
pub fn show_with_pfp(
|
||||||
&mut self,
|
self,
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
key: &Pubkey,
|
key: &[u8; 32],
|
||||||
ui_element: fn(ui: &mut egui::Ui, pfp: ProfilePic) -> egui::Response,
|
ui_element: fn(ui: &mut egui::Ui, pfp: ProfilePic) -> egui::Response,
|
||||||
) -> Option<egui::Response> {
|
) -> Option<egui::Response> {
|
||||||
if let Ok(txn) = Transaction::new(self.ndb) {
|
if let Ok(txn) = Transaction::new(self.ndb) {
|
||||||
let profile = self.ndb.get_profile_by_pubkey(&txn, key.bytes());
|
let profile = self.ndb.get_profile_by_pubkey(&txn, key);
|
||||||
|
|
||||||
if let Ok(profile) = profile {
|
if let Ok(profile) = profile {
|
||||||
return Some(ui_element(
|
return Some(ui_element(
|
||||||
|
|||||||
@@ -1,72 +1,75 @@
|
|||||||
use egui::{Button, Layout, SidePanel, Vec2, Widget};
|
use egui::{Button, Layout, SidePanel, Vec2, Widget};
|
||||||
|
|
||||||
use crate::{account_manager::AccountManager, ui::global_popup::GlobalPopupType};
|
use crate::account_manager::AccountManager;
|
||||||
|
|
||||||
use super::{
|
use super::{profile::SimpleProfilePreviewController, ProfilePic, View};
|
||||||
profile::SimpleProfilePreviewController,
|
|
||||||
state_in_memory::{STATE_ACCOUNT_SWITCHER, STATE_SIDE_PANEL},
|
|
||||||
ProfilePic, View,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct DesktopSidePanel<'a> {
|
pub struct DesktopSidePanel<'a> {
|
||||||
account_manager: &'a mut AccountManager,
|
selected_account: Option<&'a [u8; 32]>,
|
||||||
simple_preview_controller: SimpleProfilePreviewController<'a>,
|
simple_preview_controller: SimpleProfilePreviewController<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
static ID: &str = "left panel";
|
impl<'a> Widget for DesktopSidePanel<'a> {
|
||||||
|
fn ui(self, ui: &mut egui::Ui) -> egui::Response {
|
||||||
impl<'a> View for DesktopSidePanel<'a> {
|
self.show(ui)
|
||||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
|
||||||
self.inner(ui);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DesktopSidePanel<'a> {
|
impl<'a> DesktopSidePanel<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
account_manager: &'a mut AccountManager,
|
selected_account: Option<&'a [u8; 32]>,
|
||||||
simple_preview_controller: SimpleProfilePreviewController<'a>,
|
simple_preview_controller: SimpleProfilePreviewController<'a>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
DesktopSidePanel {
|
DesktopSidePanel {
|
||||||
account_manager,
|
selected_account,
|
||||||
simple_preview_controller,
|
simple_preview_controller,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inner(&mut self, ui: &mut egui::Ui) {
|
pub fn panel() -> SidePanel {
|
||||||
let dark_mode = ui.ctx().style().visuals.dark_mode;
|
egui::SidePanel::left("side_panel")
|
||||||
let spacing_amt = 16.0;
|
.resizable(false)
|
||||||
ui.with_layout(Layout::bottom_up(egui::Align::Center), |ui| {
|
.exact_width(40.0)
|
||||||
ui.add_space(spacing_amt);
|
|
||||||
if self.pfp_button(ui).clicked() {
|
|
||||||
STATE_SIDE_PANEL.set_state(ui.ctx(), Some(GlobalPopupType::AccountSwitcher));
|
|
||||||
let previous_val = STATE_ACCOUNT_SWITCHER.get_state(ui.ctx());
|
|
||||||
STATE_ACCOUNT_SWITCHER.set_state(ui.ctx(), !previous_val);
|
|
||||||
}
|
|
||||||
ui.add_space(spacing_amt);
|
|
||||||
ui.add(settings_button(dark_mode));
|
|
||||||
ui.add_space(spacing_amt);
|
|
||||||
ui.add(add_column_button(dark_mode));
|
|
||||||
ui.add_space(spacing_amt);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pfp_button(&mut self, ui: &mut egui::Ui) -> egui::Response {
|
pub fn show(self, ui: &mut egui::Ui) -> egui::Response {
|
||||||
if let Some(selected_account) = self.account_manager.get_selected_account() {
|
let dark_mode = ui.ctx().style().visuals.dark_mode;
|
||||||
if let Some(response) = self.simple_preview_controller.show_with_pfp(
|
let spacing_amt = 16.0;
|
||||||
ui,
|
|
||||||
&selected_account.key.pubkey,
|
let inner = ui
|
||||||
show_pfp(),
|
.with_layout(Layout::bottom_up(egui::Align::Center), |ui| {
|
||||||
) {
|
ui.spacing_mut().item_spacing.y = spacing_amt;
|
||||||
|
let pfp_resp = self.pfp_button(ui);
|
||||||
|
let settings_resp = ui.add(settings_button(dark_mode));
|
||||||
|
let column_resp = ui.add(add_column_button(dark_mode));
|
||||||
|
|
||||||
|
if pfp_resp.clicked() || pfp_resp.hovered() {
|
||||||
|
egui::InnerResponse::new(SidePanelAction::Account, pfp_resp)
|
||||||
|
} else if settings_resp.clicked() || settings_resp.hovered() {
|
||||||
|
egui::InnerResponse::new(SidePanelAction::Settings, settings_resp)
|
||||||
|
} else if column_resp.clicked() || column_resp.hovered() {
|
||||||
|
egui::InnerResponse::new(SidePanelAction::Columns, column_resp)
|
||||||
|
} else {
|
||||||
|
egui::InnerResponse::new(SidePanelAction::Panel, pfp_resp)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.inner;
|
||||||
|
|
||||||
|
SidePanelResponse::new(inner.inner, inner.response)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pfp_button(self, ui: &mut egui::Ui) -> egui::Response {
|
||||||
|
if let Some(selected_account) = self.selected_account {
|
||||||
|
if let Some(response) =
|
||||||
|
self.simple_preview_controller
|
||||||
|
.show_with_pfp(ui, selected_account, show_pfp())
|
||||||
|
{
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
add_button_to_ui(ui, no_account_pfp())
|
add_button_to_ui(ui, no_account_pfp())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn panel() -> SidePanel {
|
|
||||||
egui::SidePanel::left(ID).resizable(false).exact_width(40.0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_pfp() -> fn(ui: &mut egui::Ui, pfp: ProfilePic) -> egui::Response {
|
fn show_pfp() -> fn(ui: &mut egui::Ui, pfp: ProfilePic) -> egui::Response {
|
||||||
@@ -126,10 +129,16 @@ mod preview {
|
|||||||
|
|
||||||
impl View for DesktopSidePanelPreview {
|
impl View for DesktopSidePanelPreview {
|
||||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||||
let mut panel = DesktopSidePanel::new(
|
let selected_account = self
|
||||||
&mut self.account_manager,
|
.account_manager
|
||||||
|
.get_selected_account()
|
||||||
|
.map(|x| x.pubkey.bytes());
|
||||||
|
|
||||||
|
let panel = DesktopSidePanel::new(
|
||||||
|
selected_account,
|
||||||
SimpleProfilePreviewController::new(&self.ndb, &mut self.img_cache),
|
SimpleProfilePreviewController::new(&self.ndb, &mut self.img_cache),
|
||||||
);
|
);
|
||||||
|
|
||||||
DesktopSidePanel::panel().show(ui.ctx(), |ui| panel.ui(ui));
|
DesktopSidePanel::panel().show(ui.ctx(), |ui| panel.ui(ui));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
use std::any::Any;
|
|
||||||
|
|
||||||
use super::global_popup::GlobalPopupType;
|
|
||||||
|
|
||||||
/// StateInMemory is a helper struct for interacting with egui memory persisted data
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct StateInMemory<T: 'static + Clone + Send> {
|
|
||||||
id: &'static str,
|
|
||||||
default_state: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: 'static + Any + Clone + Send + Sync> StateInMemory<T> {
|
|
||||||
pub fn get_state(&self, ctx: &egui::Context) -> T {
|
|
||||||
ctx.data_mut(|d| {
|
|
||||||
d.get_temp(egui::Id::new(self.id))
|
|
||||||
.unwrap_or(self.default_state.clone())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_state(&self, ctx: &egui::Context, new_val: T) {
|
|
||||||
ctx.data_mut(|d| d.insert_temp(egui::Id::new(self.id), new_val));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub static STATE_ACCOUNT_MANAGEMENT: StateInMemory<bool> = StateInMemory::<bool> {
|
|
||||||
id: ACCOUNT_MANAGEMENT_VIEW_STATE_ID,
|
|
||||||
default_state: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub static STATE_SIDE_PANEL: StateInMemory<Option<GlobalPopupType>> =
|
|
||||||
StateInMemory::<Option<GlobalPopupType>> {
|
|
||||||
id: SIDE_PANEL_VIEW_STATE_ID,
|
|
||||||
default_state: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub static STATE_ACCOUNT_SWITCHER: StateInMemory<bool> = StateInMemory::<bool> {
|
|
||||||
id: ACCOUNT_SWITCHER_VIEW_STATE_ID,
|
|
||||||
default_state: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
static ACCOUNT_MANAGEMENT_VIEW_STATE_ID: &str = "account management view state";
|
|
||||||
static SIDE_PANEL_VIEW_STATE_ID: &str = "side panel view state";
|
|
||||||
static ACCOUNT_SWITCHER_VIEW_STATE_ID: &str = "account switcher view state";
|
|
||||||
@@ -3,8 +3,8 @@ use notedeck::app_creation::{
|
|||||||
};
|
};
|
||||||
use notedeck::ui::account_login_view::AccountLoginView;
|
use notedeck::ui::account_login_view::AccountLoginView;
|
||||||
use notedeck::ui::{
|
use notedeck::ui::{
|
||||||
AccountManagementView, AccountSelectionWidget, DesktopGlobalPopup, DesktopSidePanel, Preview,
|
AccountManagementView, AccountSelectionWidget, DesktopSidePanel, Preview, PreviewApp,
|
||||||
PreviewApp, ProfilePic, ProfilePreview, RelayView,
|
ProfilePic, ProfilePreview, RelayView,
|
||||||
};
|
};
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
@@ -89,6 +89,5 @@ async fn main() {
|
|||||||
AccountManagementView,
|
AccountManagementView,
|
||||||
AccountSelectionWidget,
|
AccountSelectionWidget,
|
||||||
DesktopSidePanel,
|
DesktopSidePanel,
|
||||||
DesktopGlobalPopup,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
use enostr::{FullKeypair, RelayPool};
|
use enostr::Keypair;
|
||||||
|
|
||||||
pub struct UserAccount {
|
//pub struct UserAccount {
|
||||||
pub key: FullKeypair,
|
//pub key: Keypair,
|
||||||
pub relays: RelayPool,
|
//pub relays: RelayPool,
|
||||||
}
|
//pub relays: Vec<String>,
|
||||||
|
//}
|
||||||
|
|
||||||
|
pub type UserAccount = Keypair;
|
||||||
|
|||||||
Reference in New Issue
Block a user