switch mentions from _inbox tags to mentions relation

This commit is contained in:
2026-03-29 15:51:46 +02:00
parent f907657c6f
commit 9e8c37564f
4 changed files with 87 additions and 16 deletions

View File

@@ -67,6 +67,18 @@ func (db *DB) resolveUserRef(tx *sql.Tx, ref string) (string, error) {
return db.ensureUser(tx, ref)
}
func (db *DB) GetUserByUsername(username string) (string, error) {
var id string
err := db.QueryRow(`
SELECT n.id FROM nodes n
JOIN tags t ON n.id = t.node_id
WHERE n.title = ? AND t.tag = '_type::user'`, username).Scan(&id)
if err == sql.ErrNoRows {
return "", nil
}
return id, err
}
func (db *DB) ensureNamespace(tx *sql.Tx, name string) (string, error) {
var existingID string
err := tx.QueryRow(`
@@ -123,8 +135,9 @@ type UpdateParams struct {
AddRels, RemoveRels map[models.RelType][]string
}
type ListFilter struct {
TagPrefixes []string
Assignee string
TagPrefixes []string
Assignee string
MentionsUser string
}
func (db *DB) CreateNode(p CreateParams) (*models.Node, error) {
@@ -142,17 +155,17 @@ func (db *DB) CreateNode(p CreateParams) (*models.Node, error) {
}
for _, m := range parse.Mentions(p.Title + " " + p.Content) {
if _, err := db.ensureUser(tx, m); err != nil {
userID, err := db.resolveUserRef(tx, m)
if err != nil {
return nil, err
}
if _, err := tx.Exec("INSERT INTO rels (from_id, to_id, rel_type) VALUES (?, ?, ?)", id, userID, models.RelMentions); err != nil {
return nil, err
}
}
for _, t := range append(p.Tags, parse.Mentions(p.Title+" "+p.Content)...) {
if !strings.HasPrefix(t, "_") && strings.HasPrefix(t, "@") {
if _, err = tx.Exec("INSERT INTO tags (node_id, tag) VALUES (?, ?)", id, "_inbox::"+t[1:]); err != nil {
return nil, err
}
} else if _, err := tx.Exec("INSERT INTO tags (node_id, tag) VALUES (?, ?)", id, t); err != nil {
for _, t := range p.Tags {
if _, err := tx.Exec("INSERT INTO tags (node_id, tag) VALUES (?, ?)", id, t); err != nil {
return nil, err
}
}
@@ -188,24 +201,28 @@ func (db *DB) UpdateNode(id string, p UpdateParams) error {
}
defer tx.Rollback()
var currentTitle, currentContent string
err = tx.QueryRow("SELECT title, COALESCE(content, '') FROM nodes WHERE id = ?", id).Scan(&currentTitle, &currentContent)
if err != nil {
return err
}
upd := func(col, val string) error {
_, err := tx.Exec("UPDATE nodes SET "+col+" = ? WHERE id = ?", val, id)
return err
}
newTitle, newContent := currentTitle, currentContent
if p.Title != nil {
if err := upd("title", *p.Title); err != nil {
return err
}
newTitle = *p.Title
}
if p.Content != nil {
if err := upd("content", *p.Content); err != nil {
return err
}
for _, m := range parse.Mentions(*p.Content) {
if _, err := db.ensureUser(tx, m); err != nil {
return err
}
}
newContent = *p.Content
}
if p.DueDate != nil {
if err := upd("due_date", *p.DueDate); err != nil {
@@ -218,6 +235,46 @@ func (db *DB) UpdateNode(id string, p UpdateParams) error {
}
}
if p.Title != nil || p.Content != nil {
newMentions := parse.Mentions(newTitle + " " + newContent)
rows, err := tx.Query("SELECT to_id FROM rels WHERE from_id = ? AND rel_type = ?", id, models.RelMentions)
if err != nil {
return err
}
existingMentionIDs := make(map[string]bool)
for rows.Next() {
var uid string
if err := rows.Scan(&uid); err != nil {
rows.Close()
return err
}
existingMentionIDs[uid] = true
}
rows.Close()
mentionedUserIDs := make(map[string]bool)
for _, m := range newMentions {
userID, err := db.resolveUserRef(tx, m)
if err != nil {
return err
}
mentionedUserIDs[userID] = true
if !existingMentionIDs[userID] {
if _, err := tx.Exec("INSERT INTO rels (from_id, to_id, rel_type) VALUES (?, ?, ?)", id, userID, models.RelMentions); err != nil {
return err
}
}
}
for uid := range existingMentionIDs {
if !mentionedUserIDs[uid] {
if _, err := tx.Exec("DELETE FROM rels WHERE from_id = ? AND to_id = ? AND rel_type = ?", id, uid, models.RelMentions); err != nil {
return err
}
}
}
}
for _, t := range p.RemoveTags {
tx.Exec("DELETE FROM tags WHERE node_id = ? AND tag = ?", id, t)
}
@@ -315,6 +372,11 @@ func (db *DB) ListNodes(f ListFilter) ([]*models.Node, error) {
conds = append(conds, "r_assign.to_id = ? AND r_assign.rel_type = ?")
args = append(args, f.Assignee, models.RelAssignee)
}
if f.MentionsUser != "" {
joins = append(joins, "JOIN rels r_mentions ON n.id = r_mentions.from_id")
conds = append(conds, "r_mentions.to_id = ? AND r_mentions.rel_type = ?")
args = append(args, f.MentionsUser, models.RelMentions)
}
if len(joins) > 0 {
q += " " + strings.Join(joins, " ") + " "