# HG changeset patch # User Peter Sanchez # Date 1716919103 21600 # Tue May 28 11:58:23 2024 -0600 # Node ID fb0c5ead0f5f69d0062b227fe606d9a22faa08b2 # Parent 71df2833c812beed7af7afcb7a464088fc79844b # Parent 42f5fb0ef40741b91af64dca70ec1a39c5d0cbad Merge tedu upstream diff --git a/activity.go b/activity.go --- a/activity.go +++ b/activity.go @@ -1439,9 +1439,6 @@ jo = junk.New() jo["id"] = h.XID jo["type"] = "Note" - if h.What == "event" { - jo["type"] = "Event" - } if h.What == "update" { j["type"] = "Update" jo["updated"] = dt @@ -1555,6 +1552,12 @@ jl["href"] = h.Link atts = append(atts, jl) } + if tooooFancy(h.Noise) { + jo["type"] = "Article" + } + if h.What == "event" { + jo["type"] = "Event" + } if len(atts) > 0 { jo["attachment"] = atts } @@ -1615,6 +1618,10 @@ return j, jo } +func tooooFancy(noise string) bool { + return strings.Contains(noise, " '' and honks.rid = getthread.x union select rid, convoy from honks, getthread where honks.xid = getthread.x and rid <> '' - ) `+selecthonks+"where honks.honkid > ? and honks.userid = ? and xid in (select x from getthread)"+limit) + ) `+selecthonks+"where honks.honkid > ? and honks.userid = ? and xid in (select x from getthread)"+smalllimit) stmtHonksByOntology = preparetodie(db, selecthonks+"join onts on honks.honkid = onts.honkid where honks.honkid > ? and onts.ontology = ? and (honks.userid = ? or (? = -1 and honks.whofore = 2))"+limit) stmtSaveMeta = preparetodie(db, "insert into honkmeta (honkid, genus, json) values (?, ?, ?)") diff --git a/docs/changelog.txt b/docs/changelog.txt --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -2,6 +2,8 @@ ### next ++ Another tune up for thread sort. + + Rework setup and admin screens. + Add some compat for forgefed activities. diff --git a/fun.go b/fun.go --- a/fun.go +++ b/fun.go @@ -639,12 +639,15 @@ dlog.Printf("need to get a handle: %s", xid) info, _, err := investigate(xid) if err != nil { + dlog.Printf("failed to get handle: %s", err) m := re_unurl.FindStringSubmatch(xid) if len(m) > 2 { handle = m[2] } else { handle = xid } + when := time.Now().UTC().Format(dbtimeformat) + savexonker(xid, handle, "handle", when) } else { handle = info.Name } diff --git a/go.mod b/go.mod --- a/go.mod +++ b/go.mod @@ -10,8 +10,8 @@ golang.org/x/net v0.21.0 humungus.tedunangst.com/r/go-sqlite3 v1.2.1 humungus.tedunangst.com/r/gonix v0.1.4 - humungus.tedunangst.com/r/termvc v0.1.0 - humungus.tedunangst.com/r/webs v0.7.12 + humungus.tedunangst.com/r/termvc v0.1.2 + humungus.tedunangst.com/r/webs v0.7.15 ) require ( diff --git a/go.sum b/go.sum --- a/go.sum +++ b/go.sum @@ -50,7 +50,7 @@ humungus.tedunangst.com/r/go-sqlite3 v1.2.1/go.mod h1:YrRIH0O7uePPLbJriXrER44ym5aQ0QxK8CnaT/GWOkg= humungus.tedunangst.com/r/gonix v0.1.4 h1:FuvWYQlFIzmfHxfvIfq5SYpSiHhFcpJqq3pi+w45s78= humungus.tedunangst.com/r/gonix v0.1.4/go.mod h1:VFBc2bPDXr1ayHOmHUutxYu8fSM+pkwK8o36h4rkORg= -humungus.tedunangst.com/r/termvc v0.1.0 h1:Xe5ImK7W4jZqAOtZhTiec1Lc7CbNpmC2UMuDBUMcVwk= -humungus.tedunangst.com/r/termvc v0.1.0/go.mod h1:TnlG9PbH77OpEf46iDyb/H9drjegQNwhpXalmGGrbhU= -humungus.tedunangst.com/r/webs v0.7.12 h1:SbAOmzwn4LPB5AmAzc1KLIA1zymEkXFyFMV2Cp3MDdo= -humungus.tedunangst.com/r/webs v0.7.12/go.mod h1:ylhqHSPI0Oi7b4nsnx5mSO7AjLXN7wFpEHayLfN/ugk= +humungus.tedunangst.com/r/termvc v0.1.2 h1:TPH5ThFRjR+f1ko9Uh4dhm3ieNKvmoPZAvu3tU5lNIE= +humungus.tedunangst.com/r/termvc v0.1.2/go.mod h1:TnlG9PbH77OpEf46iDyb/H9drjegQNwhpXalmGGrbhU= +humungus.tedunangst.com/r/webs v0.7.15 h1:97l++EcyhAgCUgBDSUvvNHG1wFW1jAdqlqEbWvbbWik= +humungus.tedunangst.com/r/webs v0.7.15/go.mod h1:ylhqHSPI0Oi7b4nsnx5mSO7AjLXN7wFpEHayLfN/ugk= diff --git a/honk.go b/honk.go --- a/honk.go +++ b/honk.go @@ -200,6 +200,10 @@ return honk.Flags&flagIsReacted != 0 } +func (honk *Honk) ShortXID() string { + return shortxid(honk.XID) +} + type Donk struct { FileID int64 XID string diff --git a/import.go b/import.go --- a/import.go +++ b/import.go @@ -48,6 +48,7 @@ } type ActivityObject struct { + Id string AttributedTo string Summary string Content string @@ -658,3 +659,119 @@ zd.Close() fd.Close() } + +func dumpthread(username, convoy string) { + user, _ := butwhatabout(username) + honks := gethonksbyconvoy(user.ID, convoy, 0) + var jonks []junk.Junk + for _, honk := range honks { + noise := honk.Noise + j, jo := jonkjonk(user, honk) + if honk.Format == "markdown" { + source := junk.New() + source["mediaType"] = "text/markdown" + source["content"] = noise + jo["source"] = source + } + jonks = append(jonks, j) + } + j := junk.New() + j["@context"] = itiswhatitis + j["orderedItems"] = jonks + j.Write(os.Stdout) +} +func rawimport(username, filename string) { + user, _ := butwhatabout(username) + type Activity struct { + Id string + Type string + To interface{} + Cc []string + Object ActivityObject + } + var outbox struct { + OrderedItems []Activity + } + ilog.Println("Importing honks...") + fd, err := os.Open(filename) + if err != nil { + elog.Fatal(err) + } + dec := json.NewDecoder(fd) + err = dec.Decode(&outbox) + if err != nil { + elog.Fatalf("error parsing json: %s", err) + } + fd.Close() + + havetoot := func(xid string) bool { + var id int64 + row := stmtFindXonk.QueryRow(user.ID, xid) + err := row.Scan(&id) + if err == nil { + return true + } + return false + } + + items := outbox.OrderedItems + for i, j := 0, len(items)-1; i < j; i, j = i+1, j-1 { + items[i], items[j] = items[j], items[i] + } + for _, item := range items { + toot := item + if toot.Type != "Create" { + continue + } + xid := toot.Object.Id + if havetoot(xid) { + continue + } + + convoy := toot.Object.Context + if convoy == "" { + convoy = toot.Object.Conversation + } + var audience []string + to, ok := toot.To.(string) + if ok { + audience = append(audience, to) + } else { + for _, t := range toot.To.([]interface{}) { + audience = append(audience, t.(string)) + } + } + content := toot.Object.Content + format := "html" + if toot.Object.Source.MediaType == "text/markdown" { + content = toot.Object.Source.Content + format = "markdown" + } + audience = append(audience, toot.Cc...) + honk := Honk{ + UserID: user.ID, + What: "honk", + Honker: toot.Object.AttributedTo, + XID: xid, + RID: toot.Object.InReplyTo, + Date: toot.Object.Published, + URL: xid, + Audience: audience, + Noise: content, + Convoy: convoy, + Whofore: 2, + Format: format, + Precis: toot.Object.Summary, + } + if !loudandproud(honk.Audience) { + honk.Whofore = 3 + } + for _, t := range toot.Object.Tag { + switch t.Type { + case "Hashtag": + honk.Onts = append(honk.Onts, t.Name) + } + } + savehonk(&honk) + } +} diff --git a/main.go b/main.go --- a/main.go +++ b/main.go @@ -186,6 +186,9 @@ if !ok { errx("don't know about %q", cmd) } + if c.nargs > 0 && len(args) != c.nargs { + errx("incorrect arg count: %s", c.help2) + } c.callback(args) } diff --git a/upgradedb.go b/upgradedb.go --- a/upgradedb.go +++ b/upgradedb.go @@ -226,6 +226,7 @@ setV(53) fallthrough case 53: + setcsrfkey() try("analyze") closedatabases() diff --git a/util.go b/util.go --- a/util.go +++ b/util.go @@ -109,7 +109,7 @@ t2 := termvc.NewTextArea() group := termvc.NewVStack(t1, form, t2) group.SetFocus(1) - app.Element = termvc.NewMainPanel(group) + app.Element = group app.Screen = termvc.NewScreen() btn.Submit = func() { t2.Value = "" @@ -147,10 +147,7 @@ return } - var randbytes [16]byte - rand.Read(randbytes[:]) - key := fmt.Sprintf("%x", randbytes) - setconfig("csrfkey", key) + setcsrfkey() setconfig("dbversion", myVersion) setconfig("servermsg", "

Things happen.

") @@ -164,6 +161,13 @@ os.Exit(0) } +func setcsrfkey() { + var randbytes [16]byte + rand.Read(randbytes[:]) + key := fmt.Sprintf("%x", randbytes) + setconfig("csrfkey", key) +} + func initblobdb(blobdbname string) { _, err := os.Stat(blobdbname) if err == nil { @@ -193,7 +197,40 @@ } func adduser() { - panic("todo") + termvc.Start() + defer termvc.Restore() + go termvc.Catch(nil) + + db := opendatabase() + app := termvc.NewApp() + t1 := termvc.NewTextArea() + t1.Value = "\n\n\tHello.\n\t\tLet's invite a friend!" + var inputs []termvc.Element + var offset int + namefield := termvc.NewTextInput("username", &offset) + inputs = append(inputs, namefield) + passfield := termvc.NewPasswordInput("password", &offset) + inputs = append(inputs, passfield) + btn := termvc.NewButton("let's go!") + left := 25 + inputs = append(inputs, termvc.NewHPad(&left, btn, nil)) + form := termvc.NewForm(inputs...) + t2 := termvc.NewTextArea() + group := termvc.NewVStack(t1, form, t2) + group.SetFocus(1) + app.Element = group + app.Screen = termvc.NewScreen() + btn.Submit = func() { + t2.Value = "" + err := createuser(db, namefield.Value, passfield.Value) + if err != nil { + t2.Value += fmt.Sprintf("error: %s\n", err) + return + } + app.Quit() + } + + app.Loop() } func deluser(username string) { diff --git a/views/honk.html b/views/honk.html --- a/views/honk.html +++ b/views/honk.html @@ -1,4 +1,4 @@ -
+
{{ $bonkcsrf := .BonkCSRF }} {{ $IsPreview := .IsPreview }} {{ $maplink := .MapLink }} @@ -47,7 +47,7 @@ {{ end }}
{{ if $bonkcsrf }} -convoy: {{ .Convoy }} +convoy: {{ .Convoy }} {{ end }}

diff --git a/views/honkpage.html b/views/honkpage.html --- a/views/honkpage.html +++ b/views/honkpage.html @@ -38,3 +38,4 @@ +

diff --git a/views/honkpage.js b/views/honkpage.js --- a/views/honkpage.js +++ b/views/honkpage.js @@ -194,7 +194,7 @@ } switchtopage(data.name, data.arg) } -function switchtopage(name, arg) { +function switchtopage(name, arg, anchor) { var stash = curpagestate.name + ":" + curpagestate.arg var honksonpage = document.getElementById("honksonpage") var holder = honksonpage.children[0] @@ -220,6 +220,10 @@ if (msg) { srvel.prepend(msg) } + if (anchor) { + let el = document.getElementById(anchor) + el.scrollIntoView() + } } else { // or create one and fill it honksonpage.prepend(document.createElement("div")) @@ -227,6 +231,10 @@ get("/hydra?" + encode(args), function(xhr) { if (xhr.status == 200) { fillinhonks(xhr, false) + if (anchor) { + let el = document.getElementById(anchor) + el.scrollIntoView() + } } else { refreshupdate(" status: " + xhr.status) } @@ -246,11 +254,14 @@ if (name == curpagestate.name && arg == curpagestate.arg) { return false } - switchtopage(name, arg) - var url = evt.srcElement.href - if (!url) { + let url = evt.srcElement.href + if (!url) url = evt.srcElement.parentElement.href - } + let anchor + let arr = url.split("#") + if (arr.length == 2) + anchor = arr[1] + switchtopage(name, arg, anchor) history.pushState(newpagestate(name, arg), "some title", url) window.scrollTo(0, 0) return false @@ -377,6 +388,7 @@ ridinput.value = "" honknoise.value = "" } + honknoise.ondrop = donkdrop var updateinput = document.getElementById("updatexidinput") updateinput.value = "" var savedfile = document.getElementById("saveddonkxid") @@ -384,6 +396,28 @@ honknoise.focus() return false } +function donkdrop(evt) { + evt.preventDefault() + let donks = document.querySelector("#donker input") + Array.from(evt.dataTransfer.items).forEach((item) => { + if (item.kind == "file") { + let olddonks = donks.files + let donkarama = new DataTransfer(); + for (donk of olddonks) + donkarama.items.add(donk) + let file = item.getAsFile() + donkarama.items.add(file) + donks.files = donkarama.files; + let t = evt.target + let start = t.selectionStart + let s = t.value.substr(0, start) + let e = t.value.substr(start) + t.value = s + `` + e + t.selectionStart = start + t.selectionEnd = start + } + }) +} function cancelhonking() { hideelement(lehonkform) showelement(lehonkbutton) diff --git a/views/style.css b/views/style.css --- a/views/style.css +++ b/views/style.css @@ -448,3 +448,7 @@ .fontmonospace { font-family: monospace; } +div.footpad { + min-height: 14em; + border-top: 1px solid var(--fg-subtle); +} diff --git a/web.go b/web.go --- a/web.go +++ b/web.go @@ -1108,11 +1108,18 @@ if utf8.RuneCountInString(o.Name) > 24 { continue } + if o.Count < 3 { + continue + } o.Name = o.Name[1:] onts = append(onts, o) - if o.Count > 1 { - pops = append(pops, o) - } + pops = append(pops, o) + } + if len(onts) > 1024 { + sort.Slice(onts, func(i, j int) bool { + return onts[i].Count > onts[j].Count + }) + onts = onts[:1024] } sort.Slice(onts, func(i, j int) bool { return onts[i].Name < onts[j].Name @@ -1315,6 +1322,14 @@ thread := make([]*Honk, 0, len(honks)) var nextlevel func(p *Honk) level := 0 + hasreply := func(p *Honk, who string) bool { + for _, h := range kids[p.XID] { + if h.Honker == who { + return true + } + } + return false + } nextlevel = func(p *Honk) { levelup := level < 4 if pp := honkx[p.RID]; p.RID == "" || (pp != nil && sameperson(p, pp)) { @@ -1330,16 +1345,20 @@ } p.Style += fmt.Sprintf(" level%d", level) childs := kids[p.XID] - if false { - sort.SliceStable(childs, func(i, j int) bool { - return sameperson(childs[i], p) && !sameperson(childs[j], p) - }) - } - if true { - sort.SliceStable(childs, func(i, j int) bool { - return !sameperson(childs[i], p) && sameperson(childs[j], p) - }) - } + sort.SliceStable(childs, func(i, j int) bool { + var ipts, jpts int + if sameperson(childs[i], p) { + ipts += 1 + } else if hasreply(childs[i], p.Honker) { + ipts += 2 + } + if sameperson(childs[j], p) { + jpts += 1 + } else if hasreply(childs[j], p.Honker) { + jpts += 2 + } + return ipts > jpts + }) for _, h := range childs { if !done[h] { done[h] = true