mirror of
https://github.com/mikedilger/gossip.git
synced 2024-09-29 16:31:18 +00:00
Mute
This commit is contained in:
parent
35dde666f4
commit
6d89afdf44
@ -103,13 +103,16 @@ macro_rules! apply_sql {
|
||||
}
|
||||
|
||||
fn upgrade(db: &Connection, mut version: u16) -> Result<(), Error> {
|
||||
let current_version = 15;
|
||||
let current_version = 16;
|
||||
if version > current_version {
|
||||
panic!(
|
||||
"Database version {} is newer than this binary which expects version {}.",
|
||||
version, current_version
|
||||
);
|
||||
}
|
||||
|
||||
// note to developers: we cannot make this into a loop because include_str! included
|
||||
// by apply_sql! requires a static string, not a dynamically formatted one.
|
||||
apply_sql!(db, version, 1, "schema1.sql");
|
||||
apply_sql!(db, version, 2, "schema2.sql");
|
||||
apply_sql!(db, version, 3, "schema3.sql");
|
||||
@ -125,6 +128,7 @@ fn upgrade(db: &Connection, mut version: u16) -> Result<(), Error> {
|
||||
apply_sql!(db, version, 13, "schema13.sql");
|
||||
apply_sql!(db, version, 14, "schema14.sql");
|
||||
apply_sql!(db, version, 15, "schema15.sql");
|
||||
apply_sql!(db, version, 16, "schema16.sql");
|
||||
tracing::info!("Database is at version {}", version);
|
||||
Ok(())
|
||||
}
|
||||
|
2
src/db/schema16.sql
Normal file
2
src/db/schema16.sql
Normal file
@ -0,0 +1,2 @@
|
||||
ALTER TABLE person ADD COLUMN
|
||||
muted INTEGER DEFAULT 0;
|
@ -740,6 +740,7 @@ impl Overlord {
|
||||
}
|
||||
|
||||
// Add all the 'p' tags from the note we are replying to (except our own)
|
||||
// FIXME: Should we avoid taging people who are muted?
|
||||
for tag in &event.tags {
|
||||
if let Tag::Pubkey { pubkey, .. } = tag {
|
||||
if pubkey.0 != public_key.as_hex_string() {
|
||||
|
@ -23,6 +23,7 @@ pub struct DbPerson {
|
||||
pub nip05_last_checked: Option<u64>,
|
||||
pub followed: u8,
|
||||
pub followed_last_updated: i64,
|
||||
pub muted: u8,
|
||||
}
|
||||
|
||||
impl DbPerson {
|
||||
@ -35,6 +36,7 @@ impl DbPerson {
|
||||
nip05_last_checked: None,
|
||||
followed: 0,
|
||||
followed_last_updated: 0,
|
||||
muted: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -263,8 +265,11 @@ impl People {
|
||||
));
|
||||
}
|
||||
|
||||
// NOTE: We also load all muted people, so that we can render the list of people
|
||||
// who are muted, so they can be found and unmuted as necessary.
|
||||
|
||||
let sql = "SELECT pubkey, metadata, metadata_at, nip05_valid, nip05_last_checked, \
|
||||
followed, followed_last_updated FROM person WHERE followed=1"
|
||||
followed, followed_last_updated, muted FROM person WHERE followed=1 OR muted=1"
|
||||
.to_owned();
|
||||
|
||||
let output: Result<Vec<DbPerson>, Error> = task::spawn_blocking(move || {
|
||||
@ -288,6 +293,7 @@ impl People {
|
||||
nip05_last_checked: row.get(4)?,
|
||||
followed: row.get(5)?,
|
||||
followed_last_updated: row.get(6)?,
|
||||
muted: row.get(7)?,
|
||||
});
|
||||
}
|
||||
Ok(output)
|
||||
@ -560,7 +566,7 @@ impl People {
|
||||
}
|
||||
|
||||
pub async fn async_follow(&self, pubkeyhex: &PublicKeyHex, follow: bool) -> Result<(), Error> {
|
||||
let f: u8 = u8::from(follow);
|
||||
let follow: u8 = u8::from(follow);
|
||||
|
||||
// Follow in database
|
||||
let sql = "INSERT INTO PERSON (pubkey, followed) values (?, ?) \
|
||||
@ -570,14 +576,14 @@ impl People {
|
||||
let maybe_db = GLOBALS.db.blocking_lock();
|
||||
let db = maybe_db.as_ref().unwrap();
|
||||
let mut stmt = db.prepare(sql)?;
|
||||
stmt.execute((&pubkeyhex2.0, &f, &f))?;
|
||||
stmt.execute((&pubkeyhex2.0, &follow, &follow))?;
|
||||
Ok::<(), Error>(())
|
||||
})
|
||||
.await??;
|
||||
|
||||
// Make sure memory matches
|
||||
if let Some(mut dbperson) = self.people.get_mut(pubkeyhex) {
|
||||
dbperson.followed = f;
|
||||
dbperson.followed = follow;
|
||||
} else {
|
||||
// load
|
||||
if let Some(person) = Self::fetch_one(pubkeyhex).await? {
|
||||
@ -605,7 +611,8 @@ impl People {
|
||||
|
||||
// Follow in database
|
||||
let sql = format!(
|
||||
"UPDATE person SET followed=1, followed_last_updated=? WHERE pubkey IN ({}) and followed_last_updated<?",
|
||||
"UPDATE person SET followed=1, followed_last_updated=? \
|
||||
WHERE pubkey IN ({}) and followed_last_updated<?",
|
||||
repeat_vars(pubkeys.len())
|
||||
);
|
||||
|
||||
@ -630,7 +637,8 @@ impl People {
|
||||
if !merge {
|
||||
// Unfollow in database
|
||||
let sql = format!(
|
||||
"UPDATE person SET followed=0, followed_last_updated=? WHERE pubkey NOT IN ({}) and followed_last_updated<?",
|
||||
"UPDATE person SET followed=0, followed_last_updated=? \
|
||||
WHERE pubkey NOT IN ({}) and followed_last_updated<?",
|
||||
repeat_vars(pubkeys.len())
|
||||
);
|
||||
|
||||
@ -669,6 +677,45 @@ impl People {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mute(&self, pubkeyhex: &PublicKeyHex, mute: bool) {
|
||||
// We can't do it now, but we spawn a task to do it soon
|
||||
let pubkeyhex = pubkeyhex.to_owned();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = GLOBALS.people.async_mute(&pubkeyhex, mute).await {
|
||||
tracing::error!("{}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub async fn async_mute(&self, pubkeyhex: &PublicKeyHex, mute: bool) -> Result<(), Error> {
|
||||
let mute: u8 = u8::from(mute);
|
||||
|
||||
// Mute in database
|
||||
let sql = "INSERT INTO PERSON (pubkey, muted) values (?, ?) \
|
||||
ON CONFLICT(pubkey) DO UPDATE SET muted=?";
|
||||
let pubkeyhex2 = pubkeyhex.to_owned();
|
||||
task::spawn_blocking(move || {
|
||||
let maybe_db = GLOBALS.db.blocking_lock();
|
||||
let db = maybe_db.as_ref().unwrap();
|
||||
let mut stmt = db.prepare(sql)?;
|
||||
stmt.execute((&pubkeyhex2.0, &mute, &mute))?;
|
||||
Ok::<(), Error>(())
|
||||
})
|
||||
.await??;
|
||||
|
||||
// Make sure memory matches
|
||||
if let Some(mut dbperson) = self.people.get_mut(pubkeyhex) {
|
||||
dbperson.muted = mute;
|
||||
} else {
|
||||
// load
|
||||
if let Some(person) = Self::fetch_one(pubkeyhex).await? {
|
||||
self.people.insert(pubkeyhex.to_owned(), person);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update_nip05_last_checked(&self, pubkeyhex: PublicKeyHex) -> Result<(), Error> {
|
||||
let maybe_db = GLOBALS.db.lock().await;
|
||||
let db = maybe_db.as_ref().unwrap();
|
||||
@ -735,7 +782,7 @@ impl People {
|
||||
async fn fetch(criteria: Option<&str>) -> Result<Vec<DbPerson>, Error> {
|
||||
let sql = "SELECT pubkey, metadata, metadata_at, \
|
||||
nip05_valid, nip05_last_checked, \
|
||||
followed, followed_last_updated FROM person"
|
||||
followed, followed_last_updated, muted FROM person"
|
||||
.to_owned();
|
||||
let sql = match criteria {
|
||||
None => sql,
|
||||
@ -763,6 +810,7 @@ impl People {
|
||||
nip05_last_checked: row.get(4)?,
|
||||
followed: row.get(5)?,
|
||||
followed_last_updated: row.get(6)?,
|
||||
muted: row.get(7)?,
|
||||
});
|
||||
}
|
||||
Ok(output)
|
||||
@ -786,7 +834,7 @@ impl People {
|
||||
let sql = format!(
|
||||
"SELECT pubkey, metadata, metadata_at, \
|
||||
nip05_valid, nip05_last_checked, \
|
||||
followed, followed_last_updated \
|
||||
followed, followed_last_updated, muted \
|
||||
FROM person WHERE pubkey IN ({})",
|
||||
repeat_vars(pubkeys.len())
|
||||
);
|
||||
@ -821,6 +869,7 @@ impl People {
|
||||
nip05_last_checked: row.get(4)?,
|
||||
followed: row.get(5)?,
|
||||
followed_last_updated: row.get(6)?,
|
||||
muted: row.get(7)?,
|
||||
});
|
||||
}
|
||||
|
||||
@ -834,8 +883,8 @@ impl People {
|
||||
/*
|
||||
async fn insert(person: DbPerson) -> Result<(), Error> {
|
||||
let sql = "INSERT OR IGNORE INTO person (pubkey, metadata, metadata_at, \
|
||||
nip05_valid, nip05_last_checked, followed, followed_last_updated) \
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)";
|
||||
nip05_valid, nip05_last_checked, followed, followed_last_updated, muted) \
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)";
|
||||
|
||||
task::spawn_blocking(move || {
|
||||
let maybe_db = GLOBALS.db.blocking_lock();
|
||||
@ -856,6 +905,7 @@ impl People {
|
||||
&person.nip05_last_checked,
|
||||
&person.followed,
|
||||
&person.followed_last_updated,
|
||||
&person.muted,
|
||||
))?;
|
||||
Ok::<(), Error>(())
|
||||
})
|
||||
|
@ -273,6 +273,13 @@ fn render_post_maybe_fake(
|
||||
}
|
||||
let event = maybe_event.unwrap();
|
||||
|
||||
// Do not render muted people, not even fake-renders offscreen
|
||||
if let Some(person) = GLOBALS.people.get(&event.pubkey.into()) {
|
||||
if person.muted > 0 {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let screen_rect = ctx.input().screen_rect; // Rect
|
||||
let pos2 = ui.next_widget_position();
|
||||
|
||||
@ -503,6 +510,14 @@ fn render_post_actual(
|
||||
if ui.button("Dismiss").clicked() {
|
||||
GLOBALS.dismissed.blocking_write().push(event.id);
|
||||
}
|
||||
if ui.button("Mute").clicked() {
|
||||
GLOBALS.people.mute(&event.pubkey.into(), true);
|
||||
}
|
||||
if person.followed == 0 && ui.button("Follow").clicked() {
|
||||
GLOBALS.people.follow(&event.pubkey.into(), true);
|
||||
} else if ui.button("Unfollow").clicked() {
|
||||
GLOBALS.people.follow(&event.pubkey.into(), false);
|
||||
}
|
||||
if ui.button("Update Metadata").clicked() {
|
||||
let _ = GLOBALS
|
||||
.to_overlord
|
||||
|
@ -59,6 +59,7 @@ enum Page {
|
||||
Feed(FeedKind),
|
||||
PeopleList,
|
||||
PeopleFollow,
|
||||
PeopleMuted,
|
||||
Person(PublicKeyHex),
|
||||
YourKeys,
|
||||
YourMetadata,
|
||||
@ -284,6 +285,7 @@ impl eframe::App for GossipUi {
|
||||
.add(SelectableLabel::new(
|
||||
self.page == Page::PeopleList
|
||||
|| self.page == Page::PeopleFollow
|
||||
|| self.page == Page::PeopleMuted
|
||||
|| matches!(self.page, Page::Person(_)),
|
||||
"People",
|
||||
))
|
||||
@ -350,7 +352,7 @@ impl eframe::App for GossipUi {
|
||||
|
||||
egui::CentralPanel::default().show(ctx, |ui| match self.page {
|
||||
Page::Feed(_) => feed::update(self, ctx, frame, ui),
|
||||
Page::PeopleList | Page::PeopleFollow | Page::Person(_) => {
|
||||
Page::PeopleList | Page::PeopleFollow | Page::PeopleMuted | Page::Person(_) => {
|
||||
people::update(self, ctx, frame, ui)
|
||||
}
|
||||
Page::YourKeys | Page::YourMetadata => you::update(self, ctx, frame, ui),
|
||||
|
@ -8,6 +8,7 @@ use egui::{Context, Image, RichText, ScrollArea, Sense, Ui, Vec2};
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
mod follow;
|
||||
mod muted;
|
||||
mod person;
|
||||
|
||||
pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {
|
||||
@ -21,6 +22,8 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Fra
|
||||
ui.separator();
|
||||
ui.selectable_value(&mut app.page, Page::PeopleFollow, "Follow Someone New");
|
||||
ui.separator();
|
||||
ui.selectable_value(&mut app.page, Page::PeopleMuted, "Muted");
|
||||
ui.separator();
|
||||
if let Some(person) = &maybe_person {
|
||||
ui.selectable_value(
|
||||
&mut app.page,
|
||||
@ -106,6 +109,8 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Fra
|
||||
});
|
||||
} else if app.page == Page::PeopleFollow {
|
||||
follow::update(app, ctx, _frame, ui);
|
||||
} else if app.page == Page::PeopleMuted {
|
||||
muted::update(app, ctx, _frame, ui);
|
||||
} else if matches!(app.page, Page::Person(_)) {
|
||||
person::update(app, ctx, _frame, ui);
|
||||
}
|
||||
|
60
src/ui/people/muted.rs
Normal file
60
src/ui/people/muted.rs
Normal file
@ -0,0 +1,60 @@
|
||||
use super::{GossipUi, Page};
|
||||
use crate::globals::GLOBALS;
|
||||
use crate::people::DbPerson;
|
||||
use crate::AVATAR_SIZE_F32;
|
||||
use eframe::egui;
|
||||
use egui::{Context, Image, RichText, ScrollArea, Sense, Ui, Vec2};
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) {
|
||||
ui.add_space(30.0);
|
||||
|
||||
let people: Vec<DbPerson> = GLOBALS
|
||||
.people
|
||||
.get_all()
|
||||
.drain(..)
|
||||
.filter(|p| p.muted == 1)
|
||||
.collect();
|
||||
|
||||
ui.heading(format!("People who are Muted ({})", people.len()));
|
||||
ui.add_space(10.0);
|
||||
|
||||
ScrollArea::vertical().show(ui, |ui| {
|
||||
for person in people.iter() {
|
||||
if person.muted != 1 {
|
||||
continue;
|
||||
}
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
// Avatar first
|
||||
let avatar = if let Some(avatar) = app.try_get_avatar(ctx, &person.pubkey) {
|
||||
avatar
|
||||
} else {
|
||||
app.placeholder_avatar.clone()
|
||||
};
|
||||
let size = AVATAR_SIZE_F32
|
||||
* GLOBALS.pixels_per_point_times_100.load(Ordering::Relaxed) as f32
|
||||
/ 100.0;
|
||||
if ui
|
||||
.add(Image::new(&avatar, Vec2 { x: size, y: size }).sense(Sense::click()))
|
||||
.clicked()
|
||||
{
|
||||
app.set_page(Page::Person(person.pubkey.clone()));
|
||||
};
|
||||
|
||||
ui.vertical(|ui| {
|
||||
ui.label(RichText::new(GossipUi::hex_pubkey_short(&person.pubkey)).weak());
|
||||
GossipUi::render_person_name_line(ui, person);
|
||||
|
||||
if ui.button("UNMUTE").clicked() {
|
||||
GLOBALS.people.mute(&person.pubkey, false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
ui.add_space(4.0);
|
||||
|
||||
ui.separator();
|
||||
}
|
||||
});
|
||||
}
|
@ -60,15 +60,16 @@ fn content(
|
||||
ui.label(RichText::new(GossipUi::hex_pubkey_short(&pubkeyhex)).weak());
|
||||
GossipUi::render_person_name_line(ui, &person);
|
||||
|
||||
#[allow(clippy::collapsible_else_if)]
|
||||
if person.followed == 0 {
|
||||
if ui.button("FOLLOW").clicked() {
|
||||
GLOBALS.people.follow(&pubkeyhex, true);
|
||||
}
|
||||
} else {
|
||||
if ui.button("UNFOLLOW").clicked() {
|
||||
GLOBALS.people.follow(&pubkeyhex, false);
|
||||
}
|
||||
if person.followed == 0 && ui.button("FOLLOW").clicked() {
|
||||
GLOBALS.people.follow(&pubkeyhex, true);
|
||||
} else if person.followed == 1 && ui.button("UNFOLLOW").clicked() {
|
||||
GLOBALS.people.follow(&pubkeyhex, false);
|
||||
}
|
||||
|
||||
if person.muted == 0 && ui.button("MUTE").clicked() {
|
||||
GLOBALS.people.mute(&pubkeyhex, true);
|
||||
} else if person.muted == 1 && ui.button("UNMUTE").clicked() {
|
||||
GLOBALS.people.mute(&pubkeyhex, false);
|
||||
}
|
||||
|
||||
if ui.button("UPDATE METADATA").clicked() {
|
||||
|
Loading…
Reference in New Issue
Block a user