Commit Diff


commit - 2a4edef3d088dbb3d4653ffa1edf6920955c7f79
commit + d4b74d7a68368284eca18e00ef626542ae8bb20c
blob - 1bf408994b782ce9c379ac83c8ec227c149918e4
blob + 299037dd51bf3491f5aa63d17622b10169a39aa1
--- cmd/Lemmy/Lemmy.go
+++ cmd/Lemmy/Lemmy.go
@@ -2,18 +2,17 @@ package main
 
 import (
 	"bytes"
-	"errors"
 	"flag"
 	"fmt"
-	"io/fs"
 	"log"
 	"os"
 	"path"
+	"strconv"
 	"strings"
+	"time"
 
 	"9fans.net/go/acme"
 	"olowe.co/lemmy"
-	lemmyfs "olowe.co/lemmy/fs"
 )
 
 type awin struct {
@@ -26,50 +25,19 @@ func (win *awin) Look(text string) bool {
 	}
 
 	text = strings.TrimSpace(text)
-	f, err := fsys.Open(text)
-	if err == nil {
-		f.Close()
-		return open(text)
-	} else if errors.Is(err, fs.ErrNotExist) {
-		// maybe we're looking for a post in a community?
-		if win.community() != "" {
-			name := path.Join(win.community(), text)
-			f, err = fsys.Open(name)
-			if err == nil {
-				f.Close()
-				return open(name)
-			}
-		}
+	text = strings.TrimSuffix(text, "/")
+	postID, err := strconv.Atoi(text)
+	if err != nil {
+		return openCommunity(text)
 	}
-	if !errors.Is(err, fs.ErrNotExist) && !errors.Is(err, fs.ErrInvalid) {
-		win.Err(err.Error())
-	}
+
+	community := path.Base(win.name())
+	return openPost(postID, community)
 	return false
 }
 
 func (win *awin) Execute(cmd string) bool {
 	switch cmd {
-	case "Reply":
-		return open("reply")
-	case "Post":
-		body, err := win.ReadAll("body")
-		if err != nil {
-			win.Err(err.Error())
-			return false
-		}
-		comment, err := parseReply(bytes.NewReader(body))
-		if err != nil {
-			win.Err("parse comment reply: " + err.Error())
-			return false
-		}
-		client := fsys.(*lemmyfs.FS).Client
-		if err := client.Reply(comment.PostID, 0, comment.Content); err != nil {
-			emsg := fmt.Sprintf("reply to %d: %v", comment.PostID, err)
-			win.Err(emsg)
-			return false
-		}
-		win.Ctl("clean")
-		return true
 	case "Del":
 	default:
 		log.Println("unsupported execute", cmd)
@@ -77,19 +45,6 @@ func (win *awin) Execute(cmd string) bool {
 	return false
 }
 
-func (w *awin) community() string {
-	elems := strings.Split(w.name(), "/")
-	switch len(elems) {
-	case 2:
-		// /lemmy/community
-		return elems[1]
-	case 3:
-		// /lemmy/community/post
-		return elems[2]
-	}
-	return ""
-}
-
 func (w *awin) name() string {
 	buf, err := w.ReadAll("tag")
 	if err != nil {
@@ -100,114 +55,58 @@ func (w *awin) name() string {
 	return path.Clean(name)
 }
 
-var fsys fs.FS
+var client *lemmy.Client
 
-func loadPostList(dir string) ([]byte, error) {
+func loadPostList(community string) ([]byte, error) {
 	buf := &bytes.Buffer{}
-	dirents, err := fs.ReadDir(fsys, dir)
+	posts, err := client.Posts(community, lemmy.ListAll)
 	if err != nil {
 		return buf.Bytes(), err
 	}
-	for _, d := range dirents {
-		title, err := fs.ReadFile(fsys, path.Join(dir, d.Name(), "title"))
-		if err != nil {
-			return nil, err
-		}
-		creator, err := fs.ReadFile(fsys, path.Join(dir, d.Name(), "creator"))
-		if err != nil {
-			return buf.Bytes(), err
-		}
+	for _, p := range posts {
 		// 1234/	User
 		// 	Hello world!
 		// 5678/	Pengguna
 		// 	Halo Dunia!
-		fmt.Fprintf(buf, "%s/\t%s\n\t%s\n", d.Name(), string(creator), string(title))
+		fmt.Fprintf(buf, "%d/\t%s\n\t%s\n", p.ID, p.Creator, p.Title)
 	}
 	return buf.Bytes(), err
 }
 
-func loadPost(pathname string) ([]byte, error) {
+func loadPost(post lemmy.Post) ([]byte, error) {
 	buf := &bytes.Buffer{}
-	for _, fname := range []string{"title", "creator", "url"} {
-		b, err := fs.ReadFile(fsys, path.Join(pathname, fname))
-		if err != nil {
-			return buf.Bytes(), err
-		}
-		key := fmt.Sprintf("%s:", strings.Title(fname))
-		fmt.Fprintln(buf, key, string(b))
-	}
-	/*
-		stat, err := fs.Stat(fsys, pathname)
-		if err != nil {
-			return nil, err
-		}
-		fmt.Fprintln(buf, "Date:", stat.ModTime())
-	*/
-	fmt.Fprintln(buf)
+	fmt.Fprintf(buf, "From: %s\n", post.Creator)
+	fmt.Fprintf(buf, "Date: %s\n", post.Published.Format(time.RFC822))
+	fmt.Fprintf(buf, "Subject: %s\n", post.Title)
 
-	body, err := fs.ReadFile(fsys, path.Join(pathname, "body"))
-	if err != nil {
-		return buf.Bytes(), err
+	fmt.Fprintln(buf)
+	if post.URL != "" {
+		fmt.Fprintln(buf, post.URL)
+		fmt.Fprintln(buf)
 	}
-	fmt.Fprintln(buf, string(body))
-	fmt.Fprintln(buf, "")
-	dirents, err := fs.ReadDir(fsys, pathname)
-	if err != nil {
-		return buf.Bytes(), err
+	if post.Body != "" {
+		fmt.Fprintln(buf, post.Body)
+		fmt.Fprintln(buf)
 	}
-	for _, ent := range dirents {
-		if !strings.ContainsAny(ent.Name(), "0123456789") {
-			continue
-		}
-		comment, err := fs.ReadFile(fsys, path.Join(pathname, ent.Name()))
-		if err != nil {
-			return buf.Bytes(), err
-		}
-		fmt.Fprintln(buf, string(comment))
-	}
 	return buf.Bytes(), nil
 }
 
-func open(name string) bool {
-	win, err := acme.New()
+func loadComments(id int) ([]byte, error) {
+	comments, err := client.Comments(id, lemmy.ListAll)
 	if err != nil {
-		log.Fatal(err)
+		return nil, err
 	}
-	win.Name(path.Join("/lemmy", name))
-	if name == "." || name == "" {
-		win.Name("/lemmy/")
-	}
-	win.Ctl("dirty")
-	defer win.Ctl("clean")
-
-	var body []byte
-	elems := strings.Split(name, "/")
-	switch len(elems) {
-	case 1:
-		if name == "." {
-			body, err = loadCommunityList()
-			break
-		} else if path.Base(name) == "reply" {
-			win.Write("tag", []byte("Post"))
-			body = loadNewReply("")
-			break
+	buf := &bytes.Buffer{}
+	for _, c := range comments {
+		refs := lemmy.ParseCommentPath(c.Path)
+		// do we have a root comment?
+		// A root comment only referenences itself and "0"
+		if len(refs) == 2 {
+			fprintComment(buf, "", c)
+			printThread(buf, "\t", c.ID, comments)
 		}
-		body, err = loadPostList(name)
-	case 2:
-		win.Write("tag", []byte("Reply"))
-		body, err = loadPost(name)
 	}
-	if errors.Is(err, fs.ErrNotExist) {
-		return false
-	} else if err != nil {
-		log.Print(err)
-		return false
-	}
-
-	awin := &awin{win}
-	awin.Write("body", body)
-	go awin.EventLoop(awin)
-	return true
+	return buf.Bytes(), nil
 }
 
 const Usage string = "usage: Lemmy [host]"
@@ -229,7 +128,7 @@ func main() {
 	} else if len(flag.Args()) == 1 {
 		addr = flag.Arg(0)
 	}
-	client := &lemmy.Client{
+	client = &lemmy.Client{
 		Address: addr,
 		Debug:   *debug,
 	}
@@ -248,11 +147,7 @@ func main() {
 		}
 	}
 
-	fsys = &lemmyfs.FS{
-		Client: client,
-	}
-
-	open(".")
+	openCommunityList()
 	acme.AutoExit(true)
 	select {}
 }
blob - ca5c746091babfdbb6fa7f483f1cef7244233ca2
blob + 9b645de3870778f579e1f3eab608be62c1b56010
--- cmd/Lemmy/comment.go
+++ cmd/Lemmy/comment.go
@@ -33,3 +33,31 @@ func parseReply(r io.Reader) (*lemmy.Comment, error) {
 	}
 	return &comment, nil
 }
+
+func printThread(w io.Writer, prefix string,  parent int, comments []lemmy.Comment) {
+	for _, child := range children(parent, comments) {
+		fprintComment(w, prefix, child)
+		if len(children(child.ID, comments)) > 0 {
+			printThread(w, prefix+"\t", child.ID, comments)
+		}
+	}
+}
+
+func fprintComment(w io.Writer, prefix string, c lemmy.Comment) {
+	fmt.Fprintln(w, prefix, "From:", c.Creator)
+	fmt.Fprintln(w, prefix, "Archived-At:", c.ActivityURL)
+	fmt.Fprintln(w, prefix, c.Content)
+	println()
+}
+
+func children(parent int, pool []lemmy.Comment) []lemmy.Comment {
+	var kids []lemmy.Comment
+	for _, c := range pool {
+		refs := lemmy.ParseCommentPath(c.Path)
+		pnt := refs[len(refs)-2]
+		if pnt == parent {
+			kids = append(kids, c)
+		}
+	}
+	return kids
+}
blob - e321a7039b95ea3a1ff5ee6d40ebdbc7dd1982e9 (mode 644)
blob + /dev/null
--- cmd/Lemmy/fmt.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package main
-
-import (
-	"bytes"
-	"errors"
-	"fmt"
-	"io"
-	"io/fs"
-	"path"
-	"strings"
-)
-
-func printDirEntries(w io.Writer, dirs []fs.DirEntry) {
-	for _, entry := range dirs {
-		name := entry.Name()
-		if entry.IsDir() {
-			name = name + "/"
-		}
-		fmt.Fprintln(w, name)
-	}
-}
-
-func listDir(name string) (string, error) {
-	entries, err := fs.ReadDir(fsys, name)
-	if err != nil {
-		return "", err
-	}
-	var builder strings.Builder
-	for _, entry := range entries {
-		filename := path.Join(name, entry.Name(), "title")
-		title, err := fs.ReadFile(fsys, filename)
-		if err != nil && !errors.Is(err, io.EOF) {
-			return "", err
-		}
-		line := fmt.Sprintf("%s/\t%s\n", entry.Name(), string(title))
-		builder.WriteString(line)
-	}
-	return builder.String(), nil
-}
-
-func loadCommunityList() ([]byte, error) {
-	entries, err := fs.ReadDir(fsys, ".")
-	if err != nil {
-		return nil, err
-	}
-	buf := &bytes.Buffer{}
-	for _, dirent := range entries {
-		fmt.Fprintf(buf, "%s/\n", dirent.Name())
-	}
-	return buf.Bytes(), nil
-}
blob - /dev/null
blob + 87fab1bf1a0a072c7a0c0bf4063e7f3bfb96f130 (mode 644)
--- /dev/null
+++ cmd/Lemmy/open.go
@@ -0,0 +1,102 @@
+package main
+
+import (
+	"errors"
+	"log"
+	"path"
+	"strconv"
+
+	"9fans.net/go/acme"
+	"olowe.co/lemmy"
+)
+
+func openCommunityList() bool {
+	win, err := acme.New()
+	if err != nil {
+		log.Fatal(err)
+	}
+	win.Name("/lemmy/")
+	win.Ctl("dirty")
+	defer win.Ctl("clean")
+
+	communities, err := client.Communities(lemmy.ListAll)
+	if err != nil {
+		log.Print(err)
+		return false
+	}
+	for _, c := range communities {
+		win.Fprintf("body", "%s/\n", c.Name())
+	}
+	awin := &awin{win}
+	go awin.EventLoop(awin)
+	return true
+}
+
+func openCommunity(name string) bool {
+	_, _, err := client.LookupCommunity(name)
+	if errors.Is(err, lemmy.ErrNotFound) {
+		return false
+	} else if err != nil {
+		log.Print(err)
+		return false
+	}
+
+	win, err := acme.New()
+	if err != nil {
+		log.Fatal(err)
+	}
+	win.Ctl("dirty")
+	defer win.Ctl("clean")
+
+	awin := &awin{win}
+	awin.Name(path.Join("/lemmy", name) + "/")
+
+	body, err := loadPostList(name)
+	if err != nil {
+		win.Err(err.Error())
+		return false
+	}
+	awin.Write("body", body)
+	win.Addr("#0")
+	win.Ctl("dot=addr")
+	win.Ctl("show")
+	go awin.EventLoop(awin)
+	return true
+}
+
+func openPost(id int, community string) bool {
+	post, err := client.LookupPost(id)
+	if err != nil {
+		log.Print(err)
+		return false
+	}
+
+	win, err := acme.New()
+	if err != nil {
+		log.Fatal(err)
+	}
+	awin := &awin{win}
+	awin.Name(path.Join("/lemmy", community, strconv.Itoa(id)))
+	win.Ctl("dirty")
+	defer win.Ctl("clean")
+
+	body, err := loadPost(post)
+	if err != nil {
+		awin.Err(err.Error())
+		return false
+	}
+	awin.Write("body", body)
+
+	body, err = loadComments(post.ID)
+	if err != nil {
+		awin.Err(err.Error())
+		return false
+	}
+	awin.Write("body", body)
+
+	win.Addr("#0")
+	win.Ctl("dot=addr")
+	win.Ctl("show")
+	go awin.EventLoop(awin)
+	return true
+}