switch mentions from _inbox tags to mentions relation
This commit is contained in:
@@ -170,7 +170,6 @@ ax create "Task" --type issue --status open --prio high
|
|||||||
| `_status` | `open`, `done` | No |
|
| `_status` | `open`, `done` | No |
|
||||||
| `_prio` | `high`, `medium`, `low` | No |
|
| `_prio` | `high`, `medium`, `low` | No |
|
||||||
| `_namespace` | any string | Yes (default: current user) |
|
| `_namespace` | any string | Yes (default: current user) |
|
||||||
| `_inbox` | username | No (auto-set from @mentions) |
|
|
||||||
|
|
||||||
## Mentions and Inbox
|
## Mentions and Inbox
|
||||||
|
|
||||||
|
|||||||
11
cmd/inbox.go
11
cmd/inbox.go
@@ -17,7 +17,16 @@ var inboxCmd = &cobra.Command{
|
|||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if nodes, err := d.ListNodes(db.ListFilter{TagPrefixes: []string{"_inbox::" + db.GetCurrentUser()}}); err == nil {
|
userID, err := d.GetUserByUsername(db.GetCurrentUser())
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if userID == "" {
|
||||||
|
output.PrintNodes(cmd.OutOrStdout(), nil, jsonFlag)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if nodes, err := d.ListNodes(db.ListFilter{MentionsUser: userID}); err == nil {
|
||||||
output.PrintNodes(cmd.OutOrStdout(), nodes, jsonFlag)
|
output.PrintNodes(cmd.OutOrStdout(), nodes, jsonFlag)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
86
db/node.go
86
db/node.go
@@ -67,6 +67,18 @@ func (db *DB) resolveUserRef(tx *sql.Tx, ref string) (string, error) {
|
|||||||
return db.ensureUser(tx, ref)
|
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) {
|
func (db *DB) ensureNamespace(tx *sql.Tx, name string) (string, error) {
|
||||||
var existingID string
|
var existingID string
|
||||||
err := tx.QueryRow(`
|
err := tx.QueryRow(`
|
||||||
@@ -125,6 +137,7 @@ type UpdateParams struct {
|
|||||||
type ListFilter struct {
|
type ListFilter struct {
|
||||||
TagPrefixes []string
|
TagPrefixes []string
|
||||||
Assignee string
|
Assignee string
|
||||||
|
MentionsUser string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) CreateNode(p CreateParams) (*models.Node, error) {
|
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) {
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, t := range append(p.Tags, parse.Mentions(p.Title+" "+p.Content)...) {
|
for _, t := range p.Tags {
|
||||||
if !strings.HasPrefix(t, "_") && strings.HasPrefix(t, "@") {
|
if _, err := tx.Exec("INSERT INTO tags (node_id, tag) VALUES (?, ?)", id, t); err != nil {
|
||||||
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 {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -188,24 +201,28 @@ func (db *DB) UpdateNode(id string, p UpdateParams) error {
|
|||||||
}
|
}
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
|
|
||||||
|
var currentTitle, currentContent string
|
||||||
|
err = tx.QueryRow("SELECT title, COALESCE(content, '') FROM nodes WHERE id = ?", id).Scan(¤tTitle, ¤tContent)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
upd := func(col, val string) error {
|
upd := func(col, val string) error {
|
||||||
_, err := tx.Exec("UPDATE nodes SET "+col+" = ? WHERE id = ?", val, id)
|
_, err := tx.Exec("UPDATE nodes SET "+col+" = ? WHERE id = ?", val, id)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
newTitle, newContent := currentTitle, currentContent
|
||||||
if p.Title != nil {
|
if p.Title != nil {
|
||||||
if err := upd("title", *p.Title); err != nil {
|
if err := upd("title", *p.Title); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
newTitle = *p.Title
|
||||||
}
|
}
|
||||||
if p.Content != nil {
|
if p.Content != nil {
|
||||||
if err := upd("content", *p.Content); err != nil {
|
if err := upd("content", *p.Content); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, m := range parse.Mentions(*p.Content) {
|
newContent = *p.Content
|
||||||
if _, err := db.ensureUser(tx, m); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if p.DueDate != nil {
|
if p.DueDate != nil {
|
||||||
if err := upd("due_date", *p.DueDate); err != 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 {
|
for _, t := range p.RemoveTags {
|
||||||
tx.Exec("DELETE FROM tags WHERE node_id = ? AND tag = ?", id, t)
|
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 = ?")
|
conds = append(conds, "r_assign.to_id = ? AND r_assign.rel_type = ?")
|
||||||
args = append(args, f.Assignee, models.RelAssignee)
|
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 {
|
if len(joins) > 0 {
|
||||||
q += " " + strings.Join(joins, " ") + " "
|
q += " " + strings.Join(joins, " ") + " "
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const (
|
|||||||
RelCreated RelType = "created"
|
RelCreated RelType = "created"
|
||||||
RelAssignee RelType = "assignee"
|
RelAssignee RelType = "assignee"
|
||||||
RelInNamespace RelType = "in_namespace"
|
RelInNamespace RelType = "in_namespace"
|
||||||
|
RelMentions RelType = "mentions"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (n *Node) GetProperty(k string) string {
|
func (n *Node) GetProperty(k string) string {
|
||||||
|
|||||||
Reference in New Issue
Block a user