Add Cloudflare email and collaboration workflows
This commit is contained in:
@@ -63,14 +63,17 @@ func (r *Repository) GetComment(ctx context.Context, id string) (*Comment, error
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func (r *Repository) ListCommentsByDocument(ctx context.Context, documentID string) ([]Comment, error) {
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
func (r *Repository) ListCommentsByDocument(ctx context.Context, documentID string, includeResolved bool) ([]Comment, error) {
|
||||
query := `
|
||||
SELECT c.id, c.document_id, c.version_hash, c.parent_id, c.author_id, COALESCE(u.display_name, u.email), c.content, c.anchor_hash, c.anchor_line, c.created_at, c.resolved_at, c.resolved_by
|
||||
FROM comments c
|
||||
JOIN users u ON u.id = c.author_id
|
||||
WHERE c.document_id = ?
|
||||
ORDER BY c.created_at ASC
|
||||
`, documentID)
|
||||
WHERE c.document_id = ?`
|
||||
if !includeResolved {
|
||||
query += ` AND c.resolved_at IS NULL`
|
||||
}
|
||||
query += ` ORDER BY c.created_at ASC`
|
||||
rows, err := r.db.QueryContext(ctx, query, documentID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list comments: %w", err)
|
||||
}
|
||||
@@ -79,14 +82,17 @@ func (r *Repository) ListCommentsByDocument(ctx context.Context, documentID stri
|
||||
return scanComments(rows)
|
||||
}
|
||||
|
||||
func (r *Repository) ListCommentsByAnchor(ctx context.Context, documentID string, anchorHash string) ([]Comment, error) {
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
func (r *Repository) ListCommentsByAnchor(ctx context.Context, documentID string, anchorHash string, includeResolved bool) ([]Comment, error) {
|
||||
query := `
|
||||
SELECT c.id, c.document_id, c.version_hash, c.parent_id, c.author_id, COALESCE(u.display_name, u.email), c.content, c.anchor_hash, c.anchor_line, c.created_at, c.resolved_at, c.resolved_by
|
||||
FROM comments c
|
||||
JOIN users u ON u.id = c.author_id
|
||||
WHERE c.document_id = ? AND c.anchor_hash = ? AND c.resolved_at IS NULL
|
||||
ORDER BY c.created_at ASC
|
||||
`, documentID, anchorHash)
|
||||
WHERE c.document_id = ? AND c.anchor_hash = ?`
|
||||
if !includeResolved {
|
||||
query += ` AND c.resolved_at IS NULL`
|
||||
}
|
||||
query += ` ORDER BY c.created_at ASC`
|
||||
rows, err := r.db.QueryContext(ctx, query, documentID, anchorHash)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list comments by anchor: %w", err)
|
||||
}
|
||||
@@ -95,13 +101,25 @@ func (r *Repository) ListCommentsByAnchor(ctx context.Context, documentID string
|
||||
return scanComments(rows)
|
||||
}
|
||||
|
||||
func (r *Repository) ResolveComment(ctx context.Context, commentID string, userID string) error {
|
||||
_, err := r.db.ExecContext(ctx, `
|
||||
func (r *Repository) ResolveCommentThread(ctx context.Context, commentID string, userID string) error {
|
||||
result, err := r.db.ExecContext(ctx, `
|
||||
WITH RECURSIVE thread(id) AS (
|
||||
SELECT id FROM comments WHERE id = ? AND parent_id IS NULL
|
||||
UNION ALL
|
||||
SELECT c.id FROM comments c JOIN thread t ON c.parent_id = t.id
|
||||
)
|
||||
UPDATE comments SET resolved_at = ?, resolved_by = ?
|
||||
WHERE id = ?
|
||||
`, formatTime(time.Now().UTC()), userID, commentID)
|
||||
WHERE id IN (SELECT id FROM thread) AND resolved_at IS NULL
|
||||
`, commentID, formatTime(time.Now().UTC()), userID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("resolve comment: %w", err)
|
||||
return fmt.Errorf("resolve comment thread: %w", err)
|
||||
}
|
||||
changed, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return fmt.Errorf("resolve comment thread result: %w", err)
|
||||
}
|
||||
if changed == 0 {
|
||||
return fmt.Errorf("comment thread not found or already resolved")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -228,10 +246,12 @@ func (r *Repository) MarkAllNotificationsRead(ctx context.Context, userID string
|
||||
// Watchers
|
||||
|
||||
func (r *Repository) AddWatcher(ctx context.Context, w Watcher) error {
|
||||
if (w.DocumentID == nil) == (w.FolderPath == nil) {
|
||||
return fmt.Errorf("watcher requires exactly one target")
|
||||
}
|
||||
_, err := r.db.ExecContext(ctx, `
|
||||
INSERT INTO watchers (user_id, document_id, folder_path, created_at)
|
||||
INSERT OR IGNORE INTO watchers (user_id, document_id, folder_path, created_at)
|
||||
VALUES (?, ?, ?, ?)
|
||||
ON CONFLICT(user_id, document_id, folder_path) DO NOTHING
|
||||
`, w.UserID, w.DocumentID, w.FolderPath, formatTime(w.CreatedAt))
|
||||
if err != nil {
|
||||
return fmt.Errorf("add watcher: %w", err)
|
||||
@@ -262,12 +282,12 @@ func (r *Repository) ListWatchersByDocument(ctx context.Context, documentID stri
|
||||
return scanWatchers(rows)
|
||||
}
|
||||
|
||||
func (r *Repository) ListWatchersByFolder(ctx context.Context, folderPath string) ([]Watcher, error) {
|
||||
func (r *Repository) ListWatchersByFolder(ctx context.Context, documentPath string) ([]Watcher, error) {
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
SELECT user_id, document_id, folder_path, created_at
|
||||
FROM watchers
|
||||
WHERE folder_path = ? OR ? || '/' = substr(folder_path, 1, length(?) + 1)
|
||||
`, folderPath, folderPath, folderPath)
|
||||
WHERE folder_path = '' OR folder_path || '/' = substr(?, 1, length(folder_path) + 1)
|
||||
`, documentPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list folder watchers: %w", err)
|
||||
}
|
||||
@@ -275,6 +295,24 @@ func (r *Repository) ListWatchersByFolder(ctx context.Context, folderPath string
|
||||
return scanWatchers(rows)
|
||||
}
|
||||
|
||||
func (r *Repository) ListWatchersForDocument(ctx context.Context, documentID string) ([]Watcher, error) {
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
SELECT DISTINCT w.user_id, w.document_id, w.folder_path, w.created_at
|
||||
FROM watchers w
|
||||
JOIN documents d ON d.id = ?
|
||||
WHERE w.document_id = d.id
|
||||
OR (w.folder_path IS NOT NULL AND (
|
||||
w.folder_path = ''
|
||||
OR w.folder_path || '/' = substr(d.path, 1, length(w.folder_path) + 1)
|
||||
))
|
||||
`, documentID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list watchers for document: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
return scanWatchers(rows)
|
||||
}
|
||||
|
||||
func (r *Repository) IsWatching(ctx context.Context, userID string, documentID string) (bool, error) {
|
||||
var count int
|
||||
err := r.db.QueryRowContext(ctx, `
|
||||
|
||||
Reference in New Issue
Block a user