Merge remote-tracking branch 'github/pr/1087' into notifications
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -3527,6 +3527,7 @@ dependencies = [
|
|||||||
"hashbrown 0.15.4",
|
"hashbrown 0.15.4",
|
||||||
"hex",
|
"hex",
|
||||||
"image",
|
"image",
|
||||||
|
"indexmap 2.9.0",
|
||||||
"jni 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jni 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lightning-invoice",
|
"lightning-invoice",
|
||||||
"md5",
|
"md5",
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ md5 = { workspace = true }
|
|||||||
bitflags = { workspace = true }
|
bitflags = { workspace = true }
|
||||||
regex = "1"
|
regex = "1"
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
|
indexmap = {workspace = true}
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use enostr::{Pubkey, RelayPool};
|
use enostr::{Pubkey, RelayPool};
|
||||||
|
use indexmap::IndexMap;
|
||||||
use nostrdb::{Filter, Ndb, Note, Transaction};
|
use nostrdb::{Filter, Ndb, Note, Transaction};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@@ -10,7 +9,7 @@ use crate::{UnifiedSubscription, UnknownIds};
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Nip51SetCache {
|
pub struct Nip51SetCache {
|
||||||
pub sub: UnifiedSubscription,
|
pub sub: UnifiedSubscription,
|
||||||
cached_notes: HashMap<PackId, Nip51Set>,
|
cached_notes: IndexMap<PackId, Nip51Set>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type PackId = String;
|
type PackId = String;
|
||||||
@@ -24,7 +23,7 @@ impl Nip51SetCache {
|
|||||||
nip51_set_filter: Vec<Filter>,
|
nip51_set_filter: Vec<Filter>,
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
let subid = Uuid::new_v4().to_string();
|
let subid = Uuid::new_v4().to_string();
|
||||||
let mut cached_notes = HashMap::default();
|
let mut cached_notes = IndexMap::default();
|
||||||
|
|
||||||
let notes: Option<Vec<Note>> = if let Ok(results) = ndb.query(txn, &nip51_set_filter, 500) {
|
let notes: Option<Vec<Note>> = if let Ok(results) = ndb.query(txn, &nip51_set_filter, 500) {
|
||||||
Some(results.into_iter().map(|r| r.note).collect())
|
Some(results.into_iter().map(|r| r.note).collect())
|
||||||
@@ -73,11 +72,23 @@ impl Nip51SetCache {
|
|||||||
pub fn iter(&self) -> impl IntoIterator<Item = &Nip51Set> {
|
pub fn iter(&self) -> impl IntoIterator<Item = &Nip51Set> {
|
||||||
self.cached_notes.values()
|
self.cached_notes.values()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.cached_notes.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.cached_notes.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn at_index(&self, index: usize) -> Option<&Nip51Set> {
|
||||||
|
self.cached_notes.get_index(index).map(|(_, s)| s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add(
|
fn add(
|
||||||
notes: Vec<Note>,
|
notes: Vec<Note>,
|
||||||
cache: &mut HashMap<PackId, Nip51Set>,
|
cache: &mut IndexMap<PackId, Nip51Set>,
|
||||||
ndb: &Ndb,
|
ndb: &Ndb,
|
||||||
txn: &Transaction,
|
txn: &Transaction,
|
||||||
unknown_ids: &mut UnknownIds,
|
unknown_ids: &mut UnknownIds,
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ pub fn render_accounts_route(
|
|||||||
app_ctx: &mut AppContext,
|
app_ctx: &mut AppContext,
|
||||||
jobs: &mut JobsCache,
|
jobs: &mut JobsCache,
|
||||||
login_state: &mut AcquireKeyState,
|
login_state: &mut AcquireKeyState,
|
||||||
onboarding: &Onboarding,
|
onboarding: &mut Onboarding,
|
||||||
follow_packs_ui: &mut Nip51SetUiCache,
|
follow_packs_ui: &mut Nip51SetUiCache,
|
||||||
route: AccountsRoute,
|
route: AccountsRoute,
|
||||||
) -> Option<AccountsResponse> {
|
) -> Option<AccountsResponse> {
|
||||||
|
|||||||
@@ -591,7 +591,7 @@ fn render_nav_body(
|
|||||||
ctx,
|
ctx,
|
||||||
&mut app.jobs,
|
&mut app.jobs,
|
||||||
&mut app.view_state.login,
|
&mut app.view_state.login,
|
||||||
&app.onboarding,
|
&mut app.onboarding,
|
||||||
&mut app.view_state.follow_packs,
|
&mut app.view_state.follow_packs,
|
||||||
*amr,
|
*amr,
|
||||||
) else {
|
) else {
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use egui_virtual_list::VirtualList;
|
||||||
use enostr::{Pubkey, RelayPool};
|
use enostr::{Pubkey, RelayPool};
|
||||||
use nostrdb::{Filter, Ndb, NoteKey, Transaction};
|
use nostrdb::{Filter, Ndb, NoteKey, Transaction};
|
||||||
use notedeck::{create_nip51_set, filter::default_limit, Nip51SetCache, UnknownIds};
|
use notedeck::{create_nip51_set, filter::default_limit, Nip51SetCache, UnknownIds};
|
||||||
@@ -16,6 +19,7 @@ enum OnboardingState {
|
|||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Onboarding {
|
pub struct Onboarding {
|
||||||
state: Option<Result<OnboardingState, OnboardingError>>,
|
state: Option<Result<OnboardingState, OnboardingError>>,
|
||||||
|
pub list: Rc<RefCell<VirtualList>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Onboarding {
|
impl Onboarding {
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ use nostrdb::Ndb;
|
|||||||
use notedeck::{Images, JobPool, JobsCache, Localization};
|
use notedeck::{Images, JobPool, JobsCache, Localization};
|
||||||
use notedeck_ui::{
|
use notedeck_ui::{
|
||||||
colors,
|
colors,
|
||||||
nip51_set::{Nip51SetUiCache, Nip51SetWidget, Nip51SetWidgetFlags, Nip51SetWidgetResponse},
|
nip51_set::{Nip51SetUiCache, Nip51SetWidget, Nip51SetWidgetAction, Nip51SetWidgetFlags},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{onboarding::Onboarding, ui::widgets::styled_button};
|
use crate::{onboarding::Onboarding, ui::widgets::styled_button};
|
||||||
|
|
||||||
/// Display Follow Packs for the user to choose from authors trusted by the Damus team
|
/// Display Follow Packs for the user to choose from authors trusted by the Damus team
|
||||||
pub struct FollowPackOnboardingView<'a> {
|
pub struct FollowPackOnboardingView<'a> {
|
||||||
onboarding: &'a Onboarding,
|
onboarding: &'a mut Onboarding,
|
||||||
ui_state: &'a mut Nip51SetUiCache,
|
ui_state: &'a mut Nip51SetUiCache,
|
||||||
ndb: &'a Ndb,
|
ndb: &'a Ndb,
|
||||||
images: &'a mut Images,
|
images: &'a mut Images,
|
||||||
@@ -33,7 +33,7 @@ pub enum FollowPacksResponse {
|
|||||||
|
|
||||||
impl<'a> FollowPackOnboardingView<'a> {
|
impl<'a> FollowPackOnboardingView<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
onboarding: &'a Onboarding,
|
onboarding: &'a mut Onboarding,
|
||||||
ui_state: &'a mut Nip51SetUiCache,
|
ui_state: &'a mut Nip51SetUiCache,
|
||||||
ndb: &'a Ndb,
|
ndb: &'a Ndb,
|
||||||
images: &'a mut Images,
|
images: &'a mut Images,
|
||||||
@@ -71,7 +71,11 @@ impl<'a> FollowPackOnboardingView<'a> {
|
|||||||
.max_height(max_height)
|
.max_height(max_height)
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
egui::Frame::new().inner_margin(8.0).show(ui, |ui| {
|
egui::Frame::new().inner_margin(8.0).show(ui, |ui| {
|
||||||
if let Some(resp) = Nip51SetWidget::new(
|
self.onboarding.list.borrow_mut().ui_custom_layout(
|
||||||
|
ui,
|
||||||
|
follow_pack_state.len(),
|
||||||
|
|ui, index| {
|
||||||
|
let resp = Nip51SetWidget::new(
|
||||||
follow_pack_state,
|
follow_pack_state,
|
||||||
self.ui_state,
|
self.ui_state,
|
||||||
self.ndb,
|
self.ndb,
|
||||||
@@ -81,14 +85,23 @@ impl<'a> FollowPackOnboardingView<'a> {
|
|||||||
self.jobs,
|
self.jobs,
|
||||||
)
|
)
|
||||||
.with_flags(Nip51SetWidgetFlags::TRUST_IMAGES)
|
.with_flags(Nip51SetWidgetFlags::TRUST_IMAGES)
|
||||||
.ui(ui)
|
.render_at_index(ui, index);
|
||||||
{
|
|
||||||
match resp {
|
if let Some(cur_action) = resp.action {
|
||||||
Nip51SetWidgetResponse::ViewProfile(pubkey) => {
|
match cur_action {
|
||||||
|
Nip51SetWidgetAction::ViewProfile(pubkey) => {
|
||||||
action = Some(OnboardingResponse::ViewProfile(pubkey));
|
action = Some(OnboardingResponse::ViewProfile(pubkey));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if resp.rendered {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ impl Default for Nip51SetWidgetFlags {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Nip51SetWidgetResponse {
|
pub enum Nip51SetWidgetAction {
|
||||||
ViewProfile(Pubkey),
|
ViewProfile(Pubkey),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,21 +73,22 @@ impl<'a> Nip51SetWidget<'a> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<Nip51SetWidgetResponse> {
|
fn render_set(&mut self, ui: &mut egui::Ui, set: &Nip51Set) -> Nip51SetWidgetResponse {
|
||||||
let mut resp = None;
|
if should_skip(set, &self.flags) {
|
||||||
for pack in self.state.iter() {
|
return Nip51SetWidgetResponse {
|
||||||
if should_skip(pack, &self.flags) {
|
action: None,
|
||||||
continue;
|
rendered: false,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
egui::Frame::new()
|
let action = egui::Frame::new()
|
||||||
.corner_radius(CornerRadius::same(8))
|
.corner_radius(CornerRadius::same(8))
|
||||||
.fill(ui.visuals().extreme_bg_color)
|
.fill(ui.visuals().extreme_bg_color)
|
||||||
.inner_margin(Margin::same(8))
|
.inner_margin(Margin::same(8))
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
if let Some(cur_resp) = render_pack(
|
render_pack(
|
||||||
ui,
|
ui,
|
||||||
pack,
|
set,
|
||||||
self.ui_state,
|
self.ui_state,
|
||||||
self.ndb,
|
self.ndb,
|
||||||
self.images,
|
self.images,
|
||||||
@@ -95,10 +96,39 @@ impl<'a> Nip51SetWidget<'a> {
|
|||||||
self.jobs,
|
self.jobs,
|
||||||
self.loc,
|
self.loc,
|
||||||
self.flags.contains(Nip51SetWidgetFlags::TRUST_IMAGES),
|
self.flags.contains(Nip51SetWidgetFlags::TRUST_IMAGES),
|
||||||
) {
|
)
|
||||||
resp = Some(cur_resp);
|
})
|
||||||
|
.inner;
|
||||||
|
|
||||||
|
Nip51SetWidgetResponse {
|
||||||
|
action,
|
||||||
|
rendered: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_at_index(&mut self, ui: &mut egui::Ui, index: usize) -> Nip51SetWidgetResponse {
|
||||||
|
let Some(set) = self.state.at_index(index) else {
|
||||||
|
return Nip51SetWidgetResponse {
|
||||||
|
action: None,
|
||||||
|
rendered: false,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
self.render_set(ui, set)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<Nip51SetWidgetAction> {
|
||||||
|
let mut resp = None;
|
||||||
|
for pack in self.state.iter() {
|
||||||
|
let res = self.render_set(ui, pack);
|
||||||
|
|
||||||
|
if let Some(action) = res.action {
|
||||||
|
resp = Some(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !res.rendered {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
}
|
}
|
||||||
@@ -107,6 +137,11 @@ impl<'a> Nip51SetWidget<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Nip51SetWidgetResponse {
|
||||||
|
pub action: Option<Nip51SetWidgetAction>,
|
||||||
|
pub rendered: bool,
|
||||||
|
}
|
||||||
|
|
||||||
fn should_skip(set: &Nip51Set, required: &Nip51SetWidgetFlags) -> bool {
|
fn should_skip(set: &Nip51Set, required: &Nip51SetWidgetFlags) -> bool {
|
||||||
(required.contains(Nip51SetWidgetFlags::REQUIRES_TITLE) && set.title.is_none())
|
(required.contains(Nip51SetWidgetFlags::REQUIRES_TITLE) && set.title.is_none())
|
||||||
|| (required.contains(Nip51SetWidgetFlags::REQUIRES_IMAGE) && set.image.is_none())
|
|| (required.contains(Nip51SetWidgetFlags::REQUIRES_IMAGE) && set.image.is_none())
|
||||||
@@ -126,7 +161,7 @@ fn render_pack(
|
|||||||
jobs: &mut JobsCache,
|
jobs: &mut JobsCache,
|
||||||
loc: &mut Localization,
|
loc: &mut Localization,
|
||||||
image_trusted: bool,
|
image_trusted: bool,
|
||||||
) -> Option<Nip51SetWidgetResponse> {
|
) -> Option<Nip51SetWidgetAction> {
|
||||||
let max_img_size = vec2(ui.available_width(), 200.0);
|
let max_img_size = vec2(ui.available_width(), 200.0);
|
||||||
|
|
||||||
ui.allocate_new_ui(UiBuilder::new(), |ui| 's: {
|
ui.allocate_new_ui(UiBuilder::new(), |ui| 's: {
|
||||||
@@ -210,7 +245,7 @@ fn render_pack(
|
|||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
if render_profile_item(ui, images, m_profile.as_ref(), cur_state) {
|
if render_profile_item(ui, images, m_profile.as_ref(), cur_state) {
|
||||||
resp = Some(Nip51SetWidgetResponse::ViewProfile(*pk));
|
resp = Some(Nip51SetWidgetAction::ViewProfile(*pk));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user