feat: add invite management, replies and subgroup support
This commit is contained in:
@@ -12,7 +12,7 @@ use crate::LOCAL_KEYS;
|
||||
use crate::protocol::{
|
||||
ACTIVITY_KINDS, Activity, GroupAdmins, GroupId, GroupKey, GroupMembers, GroupMetadata,
|
||||
GroupPins, GroupRoles, MEMBERSHIP_KINDS, Membership, PIN_LIST, Pin, RENDER_KINDS, STATE_KINDS,
|
||||
TimelineWindow,
|
||||
TimelineWindow, tag_value,
|
||||
};
|
||||
|
||||
const REPLAY_LIMIT: usize = 100;
|
||||
@@ -34,6 +34,13 @@ pub struct Reaction {
|
||||
pub author: PublicKey,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Invite {
|
||||
pub id: EventId,
|
||||
pub code: String,
|
||||
pub created_at: Timestamp,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Row {
|
||||
Message(Box<Message>),
|
||||
@@ -86,6 +93,8 @@ pub struct Group {
|
||||
rows: Vec<Row>,
|
||||
index: HashSet<EventId>,
|
||||
reactions: HashMap<EventId, Vec<Reaction>>,
|
||||
/// Active invite codes; revocations arrive as 9005 deletions.
|
||||
invites: Vec<Invite>,
|
||||
leave_requests: HashMap<PublicKey, Timestamp>,
|
||||
window: TimelineWindow,
|
||||
oldest: Option<Timestamp>,
|
||||
@@ -114,6 +123,7 @@ impl Group {
|
||||
rows: Vec::new(),
|
||||
index: HashSet::new(),
|
||||
reactions: HashMap::new(),
|
||||
invites: Vec::new(),
|
||||
leave_requests: HashMap::new(),
|
||||
window: TimelineWindow::new(),
|
||||
oldest: None,
|
||||
@@ -193,6 +203,10 @@ impl Group {
|
||||
self.reactions.get(id).map(Vec::as_slice).unwrap_or(&[])
|
||||
}
|
||||
|
||||
pub fn invites(&self) -> &[Invite] {
|
||||
&self.invites
|
||||
}
|
||||
|
||||
pub fn loading_more(&self) -> bool {
|
||||
self.loading_more
|
||||
}
|
||||
@@ -375,6 +389,7 @@ impl Group {
|
||||
let filters = [
|
||||
key.id().timeline_filter(REPLAY_LIMIT),
|
||||
key.id().membership_filter(me),
|
||||
key.id().moderation_filter(REPLAY_LIMIT),
|
||||
]
|
||||
.into_iter()
|
||||
.chain(key.id().state_filters())
|
||||
@@ -523,13 +538,22 @@ impl Group {
|
||||
}
|
||||
|
||||
pub(crate) fn forget(&mut self, id: EventId, cx: &mut Context<Self>) {
|
||||
if self.retract(id) {
|
||||
cx.emit(GroupEvent::Updated);
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove an event and everything attached to it.
|
||||
fn retract(&mut self, id: EventId) -> bool {
|
||||
self.window.remove(id);
|
||||
|
||||
if !self.index.remove(&id) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
self.rows.retain(|row| row.id() != id);
|
||||
self.invites.retain(|invite| invite.id != id);
|
||||
self.reactions.remove(&id);
|
||||
|
||||
for reactions in self.reactions.values_mut() {
|
||||
@@ -538,8 +562,7 @@ impl Group {
|
||||
|
||||
self.reactions.retain(|_, reactions| !reactions.is_empty());
|
||||
|
||||
cx.emit(GroupEvent::Updated);
|
||||
cx.notify();
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) fn eose(&mut self, cx: &mut Context<Self>) {
|
||||
@@ -557,7 +580,7 @@ impl Group {
|
||||
|
||||
fn absorb(&mut self, event: &Event, me: PublicKey) -> Absorbed {
|
||||
if STATE_KINDS.contains(&event.kind) {
|
||||
return if self.absorb_state(event) {
|
||||
return if self.absorb_state(event, me) {
|
||||
Absorbed::Updated
|
||||
} else {
|
||||
Absorbed::Ignored
|
||||
@@ -583,6 +606,28 @@ impl Group {
|
||||
};
|
||||
}
|
||||
|
||||
if event.kind == Kind::GroupCreateInvite {
|
||||
return if self.push_invite(event) {
|
||||
Absorbed::Updated
|
||||
} else {
|
||||
Absorbed::Ignored
|
||||
};
|
||||
}
|
||||
|
||||
if event.kind == Kind::GroupDeleteEvent {
|
||||
let retracted = event
|
||||
.tags
|
||||
.event_ids()
|
||||
.next()
|
||||
.is_some_and(|target| self.retract(target));
|
||||
|
||||
return if retracted {
|
||||
Absorbed::Updated
|
||||
} else {
|
||||
Absorbed::Ignored
|
||||
};
|
||||
}
|
||||
|
||||
if RENDER_KINDS.contains(&event.kind) {
|
||||
return Absorbed::Shown {
|
||||
fresh: self.push_row(event),
|
||||
@@ -625,18 +670,24 @@ impl Group {
|
||||
}
|
||||
}
|
||||
|
||||
fn absorb_state(&mut self, event: &Event) -> bool {
|
||||
fn absorb_state(&mut self, event: &Event, me: PublicKey) -> bool {
|
||||
match event.kind {
|
||||
Kind::GroupMetadata => match GroupMetadata::parse(event) {
|
||||
Ok(metadata) => self.metadata = Some(metadata),
|
||||
Err(_) => return false,
|
||||
},
|
||||
Kind::GroupAdmins => match GroupAdmins::parse(event) {
|
||||
Ok(admins) => self.admins = admins,
|
||||
Ok(admins) => {
|
||||
self.admins = admins;
|
||||
self.adopt_roster_membership(me);
|
||||
}
|
||||
Err(_) => return false,
|
||||
},
|
||||
Kind::GroupMembers => match GroupMembers::parse(event) {
|
||||
Ok(members) => self.members = members,
|
||||
Ok(members) => {
|
||||
self.members = members;
|
||||
self.adopt_roster_membership(me);
|
||||
}
|
||||
Err(_) => return false,
|
||||
},
|
||||
Kind::GroupRoles => match GroupRoles::parse(event) {
|
||||
@@ -653,6 +704,21 @@ impl Group {
|
||||
self.state_ids.insert(event.kind, event.id) != Some(event.id)
|
||||
}
|
||||
|
||||
fn adopt_roster_membership(&mut self, me: PublicKey) -> bool {
|
||||
if self.membership != Membership::Unknown {
|
||||
return false;
|
||||
}
|
||||
|
||||
let listed = self.admins.contains(&me) || self.members.iter().any(|member| *member == me);
|
||||
|
||||
if !listed {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.membership = Membership::Member;
|
||||
true
|
||||
}
|
||||
|
||||
fn push_row(&mut self, event: &Event) -> bool {
|
||||
if !self.index.insert(event.id) {
|
||||
return false;
|
||||
@@ -686,6 +752,24 @@ impl Group {
|
||||
true
|
||||
}
|
||||
|
||||
fn push_invite(&mut self, event: &Event) -> bool {
|
||||
let Some(code) = tag_value(&event.tags, "code") else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if !self.index.insert(event.id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.invites.push(Invite {
|
||||
id: event.id,
|
||||
code: code.to_owned(),
|
||||
created_at: event.created_at,
|
||||
});
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn push_activity(&mut self, event: &Event) -> bool {
|
||||
let activity = match Activity::parse(event) {
|
||||
Ok(activity) => activity,
|
||||
|
||||
Reference in New Issue
Block a user