~abyxcos/matt

f2cc313581d323334484dab60a18c9718b67e78a — abyxcos 1 year, 6 months ago 107080d
Run go fmt and abuse my pretty code.
4 files changed, 82 insertions(+), 84 deletions(-)

M main.go
M matt_tricks.go
M noisy_lines.go
M widgets.go
M main.go => main.go +27 -28
@@ 6,19 6,18 @@ import (
	"strings"
	_ "time"

	_ "humungus.tedunangst.com/r/go-sqlite3"
	"golang.org/x/term"
	"github.com/muesli/termenv"
	"golang.org/x/term"
	_ "humungus.tedunangst.com/r/go-sqlite3"
)

// Our widgets are global so anyone can print to them easily
var (
	statusBar *Widget
	s			 *ScrollView
	inputBar	 *Widget
	s         *ScrollView
	inputBar  *Widget
)


func (account *Account) readSettings() {
	var err error



@@ 27,7 26,7 @@ func (account *Account) readSettings() {
	if err != nil {
		s.Printf("Cannot open database: %s\n", err)
	}
	// SelectOne/QueryRow can't stay, we need a proper way to handle multi-account support. 
	// SelectOne/QueryRow can't stay, we need a proper way to handle multi-account support.
	row := db.QueryRow("SELECT username, server_URL, access_token FROM accounts")
	// defer row.Close() // QueryRow has no close(), only the plural does
	err = row.Scan(&account.UserName, &account.Server.URL, &account.AccessToken)


@@ 51,47 50,47 @@ func main() {
	defer term.Restore(int(os.Stdin.Fd()), oldState)

	// Get screen size
	// TODO: put this in an update. SIGWINCH? 
	// TODO: put this in an update. SIGWINCH?
	_, h, _ := term.GetSize(int(os.Stdin.Fd()))

	// Status bar
	statusBar = &Widget{
		Height: 1,
		Height:    1,
		YPosition: 1,
		Cursor: Pos{1, 1},
		Cursor:    Pos{1, 1},
	}
	statusBar.Printf("Matt tricks client")

	// Input bar
	inputBar = &Widget{
		Height: 1,
		Height:    1,
		YPosition: h,
		Cursor: Pos{1, 1},
		Cursor:    Pos{1, 1},
	}
	inputBar.Focus()

	// Scroll view
	s = NewScrollView(2, h - 2)
	s = NewScrollView(2, h-2)

	// Grab our two channels to catch events on. 
	// Grab our two channels to catch events on.
	lineEvents, keyEvents := Readline()

	// Read our account out of the database. 
	// Read our account out of the database.
	account.readSettings()

	// Scrolling test
	/*
	go func() {
		i := 0
		for {
			i++
			s.Printf("%d", i)
			time.Sleep(1000 * time.Millisecond)
		}
	}()
		go func() {
			i := 0
			for {
				i++
				s.Printf("%d", i)
				time.Sleep(1000 * time.Millisecond)
			}
		}()
	*/

	// Key events. Handle all the typing/keybinds here. 
	// Key events. Handle all the typing/keybinds here.
	go func() {
		for {
			ev := <-keyEvents


@@ 124,8 123,8 @@ func main() {
			continue
		}

		// And anything starting with a slash should be a command. 
		// Catch non-existent commands rather than send them (possible leak). 
		// And anything starting with a slash should be a command.
		// Catch non-existent commands rather than send them (possible leak).
		command := strings.Split(line, " ")
		switch command[0] {
		case "/quit":


@@ 151,9 150,9 @@ func main() {
				for _, room := range roomList {
					alias, _ := account.GetRoomAlias(room.ID)
					/*
					if err != nil {
						s.Printf("%v", err)
					}
						if err != nil {
							s.Printf("%v", err)
						}
					*/
					s.Printf("[%s] - %s", room.ID, alias)
				}

M matt_tricks.go => matt_tricks.go +37 -39
@@ 2,65 2,65 @@ package main

import (
	"bytes"
	"io"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
)

// Account data structures
type Server struct {
	URL				string
	IdentityServer	string
	URL            string
	IdentityServer string
}

type Room struct {
	ID		string
	Name	string
	ID   string
	Name string
}

type Account struct {
	UserName		string
	MatrixID		string
	AccessToken	string
	DeviceID		string
	Server		Server
	Rooms			[]Room
	UserName    string
	MatrixID    string
	AccessToken string
	DeviceID    string
	Server      Server
	Rooms       []Room

	http.Client
}

func (account *Account) Login(password string) error {
	var resp struct {
		AccessToken		string	`json:"access_token"`
		DeviceID			string	`json:"device_id"`
		UserID			string	`json:"user_id"`
		WellKnown		struct {
			HomeServer	struct {
				BaseURL	string	`json:"base_url"`
			}							`json:"m.homeserver"`
			IdentServer	struct {
				BaseURL	string	`json:"base_url"`
			}							`json:"m.identity_server"`
		}								`json:"well_known"`
		AccessToken string `json:"access_token"`
		DeviceID    string `json:"device_id"`
		UserID      string `json:"user_id"`
		WellKnown   struct {
			HomeServer struct {
				BaseURL string `json:"base_url"`
			} `json:"m.homeserver"`
			IdentServer struct {
				BaseURL string `json:"base_url"`
			} `json:"m.identity_server"`
		} `json:"well_known"`
	}

	requestBody, err := json.Marshal(&struct {
		Identifier	struct {
			Type	string		`json:"type"`
			User	string		`json:"user"`
		}							`json:"identifier"`
		Type			string	`json:"type"`
		Password		string	`json:"password"`
		Identifier struct {
			Type string `json:"type"`
			User string `json:"user"`
		} `json:"identifier"`
		Type     string `json:"type"`
		Password string `json:"password"`
	}{
		Identifier: struct {
			Type	string		`json:"type"`
			User	string		`json:"user"`
			Type string `json:"type"`
			User string `json:"user"`
		}{
			Type: "m.id.user",
			User: account.UserName,
		},
		Type: "m.login.password",
		Type:     "m.login.password",
		Password: password,
	})
	if err != nil {


@@ 91,10 91,10 @@ func (account *Account) GetRooms() ([]Room, error) {
		return nil, err
	}

	// TODO: Merge our room list somehow to we don't fall out of sync. 
	// TODO: Merge our room list somehow to we don't fall out of sync.
	var rooms []Room
	for _,roomId := range resp.JoinedRooms {
		room := Room{ ID: roomId }
	for _, roomId := range resp.JoinedRooms {
		room := Room{ID: roomId}
		rooms = append(rooms, room)
	}



@@ 106,7 106,7 @@ func (account *Account) GetRoomAlias(roomId string) (string, error) {
		Alias string `json:"alias"`
	}

	err := account.get("/_matrix/client/v3/rooms/" + roomId + "/state/m.room.canonical_alias", &resp)
	err := account.get("/_matrix/client/v3/rooms/"+roomId+"/state/m.room.canonical_alias", &resp)
	if err != nil {
		return "", err
	}


@@ 114,8 114,6 @@ func (account *Account) GetRoomAlias(roomId string) (string, error) {
	return resp.Alias, nil
}



// HTTP client helper functions
// Inspiration taken from the Decred wallet http client implementation
// https://github.com/decred/dcrwallet/blob/master/internal/vsp/client.go


@@ 137,8 135,8 @@ func (a *Account) do(method, path string, resp, req interface{}) error {
		}
		reqBody = bytes.NewReader(body)
	}
	httpReq, err := http.NewRequest(method, a.Server.URL + path +
		"?access_token=" + a.AccessToken, reqBody)
	httpReq, err := http.NewRequest(method, a.Server.URL+path+
		"?access_token="+a.AccessToken, reqBody)
	if err != nil {
		return fmt.Errorf("New request: %w", err)
	}

M noisy_lines.go => noisy_lines.go +7 -6
@@ 12,6 12,7 @@ type KeyEvent struct {
	Key Key
	Ch  rune
}

var KeyChan chan KeyEvent

var Line strings.Builder


@@ 90,8 91,8 @@ const (
	KeyDEL Key = 0x7F
)

// Named keys. 
// KeyRune is not a real key, but used to signify a printable character as in tcell. 
// Named keys.
// KeyRune is not a real key, but used to signify a printable character as in tcell.
const (
	KeyRune Key = iota + 0x100
	KeyUp


@@ 218,7 219,7 @@ func GetKey() KeyEvent {
			break
		}

		// One of the control keys 
		// One of the control keys
		ev.Key = Key(b[0])
	}



@@ 236,17 237,17 @@ func Readline() (<-chan string, <-chan KeyEvent) {
			case KeyNone:
				break
			case KeyRune:
				// Send a key event (KeyRune) for the client to display. 
				// Send a key event (KeyRune) for the client to display.
				KeyChan <- ev
				Line.WriteRune(ev.Ch)
			case KeyEnter:
				// Shove the current line down the pipe
				LineChan <- Line.String()
				// Let the pipe know there was an enter event. 
				// Let the pipe know there was an enter event.
				// This can be safely ignored, or used to separate drawing logic
				KeyChan <- ev
				// Reset our line contents
				// TODO: Lock me? 
				// TODO: Lock me?
				Line.Reset()
			default:
				// Send all non-editing events down the pipe

M widgets.go => widgets.go +11 -11
@@ 7,19 7,19 @@ import (
	"github.com/muesli/termenv"
)

// Our shared printing mutex. 
// Our shared printing mutex.
// Don't steal the cursor while another routing is moving it.
var PrintMutex sync.Mutex

type Pos struct {
	Col	int
	Row	int
	Col int
	Row int
}

type Widget struct {
	Height		int
	YPosition	int
	Cursor		Pos
	Height    int
	YPosition int
	Cursor    Pos
}

/*


@@ 32,11 32,11 @@ type ScrollView struct {
type ScrollView Widget

func NewScrollView(yposition int, height int) *ScrollView {
	// Init the cursor to row 0 
	// Init the cursor to row 0
	s := ScrollView{
		Height: height,
		Height:    height,
		YPosition: yposition,
		Cursor: Pos{1, 0},
		Cursor:    Pos{1, 0},
	}

	s.UpdateSize(yposition, height)


@@ 56,7 56,7 @@ func (s *ScrollView) UpdateSize(yposition int, height int) {
	s.Height = height
	s.YPosition = yposition

	termenv.ChangeScrollingRegion(yposition, height + 1)
	termenv.ChangeScrollingRegion(yposition, height+1)
	// TODO: Adjust cursor to put it back inside the ScrollView
}



@@ 81,7 81,7 @@ func (s *ScrollView) Printf(format string, a ...interface{}) {
	}

	// Move to the current line on the ScrollView.
	termenv.MoveCursor(s.YPosition + s.Cursor.Row - 1, s.Cursor.Col)
	termenv.MoveCursor(s.YPosition+s.Cursor.Row-1, s.Cursor.Col)
	fmt.Printf(format, a...)
}