Files
MindSift/internal/collab/notify.go
T
sandrodachandClaude Opus 5.5 1ea0c78b41
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 50s
feat(notes): operation log, block leases and sync API for collaborative editing
Phase 1 of the collaboration plan (server side):
- internal/collab: block tree with parent IDs and fractional order keys,
  operations with conflict rules (version check, block leases, conflict
  copies instead of lost text, restore on late edits, cycle checks),
  in-memory leases/sessions and long-poll notifier.
- Storage: per-note lock + transaction, append-only effect log (doc_ops),
  idempotent results per opId (doc_op_results), snapshot, log paging,
  central DocRole. Old flat block data is converted to the tree.
- The existing whole-document save (PUT/PATCH) runs as an adapter through
  the same engine, so the current editor keeps working and writes to the log.
- API: GET /docs/{id}/snapshot, POST /docs/{id}/sync, GET /docs/{id}/changes
  (long poll), POST /docs/{id}/leave; DOC_SEQ poke for WebSocket/SSE rooms.
- Backend accepts editor (write) shares for notes and folders; the forced
  read-only migration is gone. The share dialog still offers read only.
- Tests: conflict matrix, order keys, leases, storage adapter round trip,
  idempotency, API protocol incl. long poll, and a randomized simulation
  (4 clients, lost/duplicated requests and responses, offline phases,
  lease expiry) checking convergence, no lost text and exactly-once results.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 11:47:56 +01:00

35 lines
684 B
Go

package collab
import "sync"
// Notifier wakes up long-poll requests waiting for changes of a document.
type Notifier struct {
mu sync.Mutex
wait map[string]chan struct{}
}
func NewNotifier() *Notifier {
return &Notifier{wait: map[string]chan struct{}{}}
}
// Changed returns a channel that is closed on the next Notify for docID.
func (n *Notifier) Changed(docID string) <-chan struct{} {
n.mu.Lock()
defer n.mu.Unlock()
ch, ok := n.wait[docID]
if !ok {
ch = make(chan struct{})
n.wait[docID] = ch
}
return ch
}
func (n *Notifier) Notify(docID string) {
n.mu.Lock()
defer n.mu.Unlock()
if ch, ok := n.wait[docID]; ok {
close(ch)
delete(n.wait, docID)
}
}