#338: drop the start/end feat marker comments

This commit is contained in:
bflatastic
2026-10-15 18:37:52 +07:00
parent 54f9212a2f
commit 8bcb0f78dd
12 changed files with 1 additions and 55 deletions
-2
View File
@@ -1489,10 +1489,8 @@ impl Backend for Store {
Admin::Blocked => AdminReply::Blocked(self.blocked()).into(),
Admin::Code { file, token } => self.set_code(&file, &token).map_or_else(failed, |code| AdminReply::Code { code }.into()),
Admin::Owned { files } => self.owned(&files).into(),
// start feat: remove-from-session
// The hub removes people itself (it knows who is connected); it never asks the store.
Admin::RemovePeer { .. } => failed("removing someone from a file is done by the server's hub".into()),
// end feat: remove-from-session
Admin::Remove { file } => {
if !self.files.contains_key(&file) {
return failed("this file isn't on the server (it may have been removed)".into());
+1 -5
View File
@@ -11,12 +11,10 @@ use figcraft_collab::{Outgoing, Server, same_secret};
use figcraft_doc::Document;
use figcraft_engine::live::{Event, PEOPLE};
pub use crate::hub::REMOVED;
use crate::hub::{Admission, Backend, Closing, Hosting, Local, clean_name, to_local};
use crate::inbox::Inbox;
pub use crate::join::ENDED;
// start feat: remove-from-session
pub use crate::hub::REMOVED;
// end feat: remove-from-session
use crate::net::NetLimits;
/// What a hosted session needs.
@@ -132,13 +130,11 @@ impl Host {
self.hosting.send(msg);
}
// start feat: remove-from-session
/// Remove client `id` from the session: they are disconnected and can't join again from
/// their address while it runs. The host can't remove themselves.
pub fn remove(&self, id: u32) {
self.hosting.remove(id);
}
// end feat: remove-from-session
/// Cut every connection as a network failure would (no close frame).
#[cfg(test)]
-18
View File
@@ -413,10 +413,8 @@ enum HubIn {
},
Local(Presence),
LocalMsg(ClientMsg),
// start feat: remove-from-session
/// The person hosting removes client `id` from their file (app sessions).
Remove(u32),
// end feat: remove-from-session
/// Cut every connection without a word (tests: a network failure).
#[cfg(test)]
Cut,
@@ -563,13 +561,11 @@ impl Hosting {
let _ = self.hub.send(HubIn::LocalMsg(msg));
}
// start feat: remove-from-session
/// The hosting person removes client `id` from their file (app sessions; see
/// [`HubIn::Remove`]).
pub(crate) fn remove(&self, id: u32) {
let _ = self.hub.send(HubIn::Remove(id));
}
// end feat: remove-from-session
/// Cut every connection as a network failure would (no close frame).
#[cfg(test)]
@@ -1128,7 +1124,6 @@ fn unsupported(protocol: u32) -> String {
/// sharing): the session's end for them.
pub const STOPPED: &str = "Sharing was stopped: the link you joined with no longer works. Your copy stays open";
// start feat: remove-from-session
/// What someone removed from a file is told: the session's end for them (they keep their copy;
/// nothing retries).
pub const REMOVED: &str = "You were removed from this session. Your copy stays open";
@@ -1138,7 +1133,6 @@ const BARRED: &str = "you were removed from this session";
/// The most addresses one file keeps barred.
const MAX_BARRED: usize = 256;
// end feat: remove-from-session
/// What everyone in a file is told when its changes couldn't be saved (they reconnect).
const UNSAVED: &str = "The server couldn't save the latest changes; reconnecting";
@@ -1153,10 +1147,8 @@ struct Hub<B> {
local: Option<String>,
/// What the servers said, sent once the backend made what changed durable.
outbox: Vec<(String, Outgoing)>,
// start feat: remove-from-session
/// Addresses ([`ip_key`]) removed from each file: they can't join it again while the hub runs.
barred: HashMap<String, HashSet<IpAddr>>,
// end feat: remove-from-session
}
impl<B: Backend> Hub<B> {
@@ -1244,7 +1236,6 @@ impl<B: Backend> Hub<B> {
self.client_message(&file, id, msg, None);
}
}
// start feat: remove-from-session
HubIn::Remove(id) => {
if let Some(file) = self.local.clone()
&& let Err(why) = self.remove_peer(&file, id)
@@ -1252,7 +1243,6 @@ impl<B: Backend> Hub<B> {
log::info!("live session: couldn't remove client {id}: {why}");
}
}
// end feat: remove-from-session
#[cfg(test)]
HubIn::Cut => {
for c in self.conns.values() {
@@ -1277,7 +1267,6 @@ impl<B: Backend> Hub<B> {
self.client_message(&file, id, msg, Some(bytes));
}
Some(Kind::Hosting { owns }) => match msg {
// start feat: remove-from-session
ClientMsg::Admin(Admin::RemovePeer { file, client }) => {
let reply = if owns.as_deref() == Some(file.as_str()) {
self.removed_reply(&file, client)
@@ -1286,7 +1275,6 @@ impl<B: Backend> Hub<B> {
};
self.answer(conn, &ServerMsg::Admin(Box::new(reply)));
}
// end feat: remove-from-session
ClientMsg::Admin(req) => {
let owns = owns.clone();
let rooms = &self.rooms;
@@ -1298,12 +1286,10 @@ impl<B: Backend> Hub<B> {
_ => self.kick(&[conn], "only publishing and managing your own files are spoken on this connection"),
},
Some(Kind::Managing) => match msg {
// start feat: remove-from-session
ClientMsg::Admin(Admin::RemovePeer { file, client }) => {
let reply = self.removed_reply(&file, client);
self.answer(conn, &ServerMsg::Admin(Box::new(reply)));
}
// end feat: remove-from-session
ClientMsg::Admin(req) => self.admin(conn, req),
ClientMsg::Ping => self.answer(conn, &ServerMsg::Pong),
_ => self.kick(&[conn], "only requests to manage files are spoken on this connection"),
@@ -1366,12 +1352,10 @@ impl<B: Backend> Hub<B> {
Ok(a) => a,
Err(why) => return refuse(&c.out, &why),
};
// start feat: remove-from-session
if self.barred.get(&hello.file).is_some_and(|b| b.contains(&c.ip)) {
log::info!("event=refused ip={} reason=removed", c.ip);
return refuse(&c.out, BARRED);
}
// end feat: remove-from-session
let hosting = reads_hosting(&hello.client_version);
// The backend checked the secret: the sessions don't keep it.
hello.token.clear();
@@ -1429,7 +1413,6 @@ impl<B: Backend> Hub<B> {
}
}
// start feat: remove-from-session
/// Remove client `id` from `file`: they are told the session is over for them and
/// disconnected, and their address can't join the file again while the hub runs. The person
/// hosting can't be removed.
@@ -1457,7 +1440,6 @@ impl<B: Backend> Hub<B> {
Err(reason) => AdminReply::Failed { reason },
}
}
// end feat: remove-from-session
/// Take in connection `c` as `kind` and send it `first`.
fn take_in(&mut self, conn: u64, c: Pending, kind: Kind, first: &ServerMsg) {
-2
View File
@@ -243,7 +243,6 @@ impl LiveLink for NetLink {
}
}
// start feat: remove-from-session
fn remove(&mut self, session: SessionId, client: u32) -> Result<(), String> {
match self.sessions.get(session) {
Some(Active::Host(h)) if h.me().client_id == client => Err("you can't remove yourself: stop the session instead".into()),
@@ -255,7 +254,6 @@ impl LiveLink for NetLink {
None => Err("this session is over".into()),
}
}
// end feat: remove-from-session
fn leave(&mut self, session: SessionId) {
if let Some(active) = self.sessions.remove(session) {
-2
View File
@@ -612,7 +612,6 @@ fn until_presence_or_pong(r: &mut Raw) -> Option<ServerMsg> {
}
}
// start feat: remove-from-session
#[test]
fn the_host_removes_someone_and_they_cant_come_back() {
let (mut host, mut hp, invite) = start();
@@ -643,4 +642,3 @@ fn the_host_removes_someone_and_they_cant_come_back() {
host.stop();
assert_eq!(host.connections(), 0);
}
// end feat: remove-from-session
-2
View File
@@ -75,9 +75,7 @@ fn samples() -> (Vec<ClientMsg>, Vec<ServerMsg>) {
ClientMsg::Admin(Admin::Block { ip: "203.0.113.9".into() }),
ClientMsg::Admin(Admin::Unblock { ip: "2001:db8::".into() }),
ClientMsg::Admin(Admin::Blocked),
// start feat: remove-from-session
ClientMsg::Admin(Admin::RemovePeer { file: "f".into(), client: 7 }),
// end feat: remove-from-session
ClientMsg::Report { reason: "Spam ☕".into() },
];
let snapshot = Replica::new(Document::new("Snap")).snapshot().unwrap();
-2
View File
@@ -274,12 +274,10 @@ pub enum Admin {
/// these `(file id, owner token)` pairs own, at most [`MAX_OWNED`] of them:
/// [`AdminReply::Owned`]. Servers older than this request can't read it.
Owned { files: Vec<(String, String)> },
// start feat: remove-from-session
/// Remove client `client` from `file`'s session: they are disconnected and their address
/// can't join the file again while the server runs. With the server key, or on an open
/// hosting connection with `file`'s owner token. Servers older than this request can't read it.
RemovePeer { file: String, client: u32 },
// end feat: remove-from-session
}
/// The most files one [`Admin::Owned`] asks about.
-6
View File
@@ -517,13 +517,11 @@ pub trait LiveLink: Send {
fn background(&mut self, _job: u64, _req: NetRequest) -> bool {
false
}
// start feat: remove-from-session
/// Remove client `client` from session `session`, which this link hosts: they are
/// disconnected and can't join again from their address while it runs. Returns at once.
fn remove(&mut self, _session: SessionId, _client: u32) -> std::result::Result<(), String> {
Err("removing people needs the desktop app or the MCP server".into())
}
// end feat: remove-from-session
/// End session `session` (stop hosting or leave); the others go on. Nothing for a session
/// that is over already.
fn leave(&mut self, session: SessionId);
@@ -1896,7 +1894,6 @@ fn follow(s: &mut Session, p: &Value) -> Result<Value> {
Ok(json!({"follow": s.live().follow}))
}
// start feat: remove-from-session
/// Remove someone from the open file's session: in a session this app hosts, the hub drops them;
/// in a file of yours on a server, the server does (with the file's owner token). They are told
/// the session is over for them, keep their copy, and can't join again from their address while
@@ -1949,7 +1946,6 @@ pub fn can_remove_people(s: &Session) -> bool {
None => false,
}
}
// end feat: remove-from-session
fn cursor(s: &mut Session, p: &Value) -> Result<Value> {
let at = match (num(p, "x"), num(p, "y")) {
@@ -2059,7 +2055,6 @@ pub fn specs() -> Vec<CommandSpec> {
|s, _| Ok(s.live_summary())
),
spec!("collab.follow", "Follow", "{client? (a peer's id; omit to stop following)} → {follow}", false, follow),
// start feat: remove-from-session
spec!(
"collab.removePerson",
"Remove from session",
@@ -2067,7 +2062,6 @@ pub fn specs() -> Vec<CommandSpec> {
false,
remove_person
),
// end feat: remove-from-session
spec!(
"collab.cursor",
"Show cursor at",
-6
View File
@@ -28,10 +28,8 @@ pub(crate) struct Script {
/// The session each presence in `sent` and each message in `msgs` was for.
pub(crate) sent_to: Vec<u64>,
pub(crate) msgs_to: Vec<u64>,
// start feat: remove-from-session
/// The people removed: `(session, client)`.
pub(crate) removed: Vec<(u64, u32)>,
// end feat: remove-from-session
}
pub(crate) struct Mock(pub(crate) Arc<Mutex<Script>>);
@@ -66,12 +64,10 @@ impl LiveLink for Mock {
s.published.push(opts);
Ok(())
}
// start feat: remove-from-session
fn remove(&mut self, session: u64, client: u32) -> Result<(), String> {
self.0.lock().unwrap().removed.push((session, client));
Ok(())
}
// end feat: remove-from-session
fn leave(&mut self, session: u64) {
let mut s = self.0.lock().unwrap();
s.left += 1;
@@ -324,7 +320,6 @@ fn the_host_ending_resets_everything() {
assert_eq!(s.message.as_deref(), Some("The host ended the session"));
}
// start feat: remove-from-session
#[test]
fn the_host_removes_people_and_guests_cant() {
let (mut s, script) = session();
@@ -361,7 +356,6 @@ fn the_host_removes_people_and_guests_cant() {
assert!(e.contains("only the person hosting"), "{e}");
assert!(script.lock().unwrap().removed.is_empty());
}
// end feat: remove-from-session
#[test]
fn collab_commands_survive_junk() {
-6
View File
@@ -460,10 +460,8 @@ pub fn people_bar(app: &mut FigApp, ui: &mut Ui, area: Rect) {
let peers: Vec<(u32, String, Color32)> = live.peers.values().map(|p| (p.client_id, p.name.clone(), color(&p.color))).collect();
let follow = live.follow;
let (in_session, spotlighting) = (live.status == Status::Live, live.spotlighting);
// start feat: remove-from-session
let can_remove = figcraft_engine::live::can_remove_people(&app.session);
let mut remove = None;
// end feat: remove-from-session
// Room for the avatars after yours and the chevron.
let fit = (((area.width() - 2.0 * R - 18.0) / STEP).floor() as usize).max(1);
let (show, more) = if peers.len() > fit { (fit - 1, peers.len() - (fit - 1)) } else { (peers.len(), 0) };
@@ -501,7 +499,6 @@ pub fn people_bar(app: &mut FigApp, ui: &mut Ui, area: Rect) {
if resp.clicked() {
clicked = Some(*id);
}
// start feat: remove-from-session
if can_remove {
crate::right::context_popup(&resp).show(|ui| {
if menu_item(ui, &trf("Remove {name} from session", &[("name", name)]), "") {
@@ -510,7 +507,6 @@ pub fn people_bar(app: &mut FigApp, ui: &mut Ui, area: Rect) {
}
});
}
// end feat: remove-from-session
}
if more > 0 {
x += STEP;
@@ -554,12 +550,10 @@ pub fn people_bar(app: &mut FigApp, ui: &mut Ui, area: Rect) {
if spotlight {
app.run("collab.spotlight", json!({"on": !spotlighting}));
}
// start feat: remove-from-session
// A server is asked without the window waiting for it.
if let Some(id) = remove {
app.run_background(&ui.ctx().clone(), "collab.removePerson", json!({"client": id}), Box::new(|_, _, _| {}));
}
// end feat: remove-from-session
}
/// The Share button's popover: the live session (start, invite link, stop or leave) and export.
-2
View File
@@ -386,7 +386,6 @@ resuming with its offline edits, and leaving the session going back to snapshot
outlines in the person's colour, and avatars (initials) at the top of the right panel beside
Share; click an avatar to **follow** that person's viewport (`collab.follow {client}`), click
again, press Escape or pan/zoom yourself to stop. The chevron lists everyone.
<!-- start feat: remove-from-session -->
- **Removing someone.** Right-click their avatar ▸ "Remove <name> from session"
(`collab.removePerson {client}`), when you host the session or own the file on a server. The
hub closes their connection with "You were removed from this session" (close code 1001: the
@@ -395,7 +394,6 @@ resuming with its offline edits, and leaving the session going back to snapshot
a server the owner asks on an open hosting connection with the file's owner token
(`Admin::RemovePeer {file, client}`; the operator may too, with the server key). Addresses are
the only identity a session has: someone behind the same address (one NAT) is barred too.
<!-- end feat: remove-from-session -->
- **Text carets.** While someone types in a text layer of the shared document on your page, their
caret and selected characters show on it in their colour (the text layout's own caret and
selection geometry, so they sit at the right glyphs of your copy), with their name in a tag above
-2
View File
@@ -1090,13 +1090,11 @@ only: a host stops hosting it, a guest leaves it.
pointer. Your selection, page and view go out by themselves when they change.
- `collab.follow {client}` follows that person's viewport; `collab.follow {}` stops (so does any
pan or zoom of your own, or Escape).
<!-- start feat: remove-from-session -->
- `collab.removePerson {client}` removes that person from the session: they are told the session
is over for them (they keep their copy and don't reconnect), and their address can't join the
file again while the session (or the server) runs. Only the person hosting, or the owner of a
file on a server (with its owner token), can; a server older than this request says it can't.
Returns `{removed, name}`.
<!-- end feat: remove-from-session -->
- `collab.cursorChat {text}` shows `text` next to your cursor for everyone in the session (put the
cursor somewhere first with `collab.cursor`): at most 100 characters, control characters become
spaces, `""` closes it. Others see it fade 5 seconds after its last change, so send it again to