Code additions/edits
This commit is contained in:
parent
f0333df1b2
commit
90629c5c78
22
config.go
22
config.go
@ -30,13 +30,22 @@ type Config struct {
|
|||||||
HS string `yaml:"homeserver"`
|
HS string `yaml:"homeserver"`
|
||||||
|
|
||||||
dir string `yaml:"-"`
|
dir string `yaml:"-"`
|
||||||
|
gmx Gomuks `yaml:"-"`
|
||||||
|
debug DebugPrinter `yaml:"-"`
|
||||||
Session *Session `yaml:"-"`
|
Session *Session `yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (config *Config) Load(dir string) {
|
func NewConfig(gmx Gomuks, dir string) *Config {
|
||||||
config.dir = dir
|
return &Config{
|
||||||
os.MkdirAll(dir, 0700)
|
gmx: gmx,
|
||||||
configPath := filepath.Join(dir, "config.yaml")
|
debug: gmx.Debug(),
|
||||||
|
dir: dir,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (config *Config) Load() {
|
||||||
|
os.MkdirAll(config.dir, 0700)
|
||||||
|
configPath := filepath.Join(config.dir, "config.yaml")
|
||||||
data, err := ioutil.ReadFile(configPath)
|
data, err := ioutil.ReadFile(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
@ -55,16 +64,17 @@ func (config *Config) Load(dir string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (config *Config) Save() {
|
func (config *Config) Save() {
|
||||||
|
os.MkdirAll(config.dir, 0700)
|
||||||
data, err := yaml.Marshal(&config)
|
data, err := yaml.Marshal(&config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Print("Failed to marshal config")
|
config.debug.Print("Failed to marshal config")
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
path := filepath.Join(config.dir, "config.yaml")
|
path := filepath.Join(config.dir, "config.yaml")
|
||||||
err = ioutil.WriteFile(path, data, 0600)
|
err = ioutil.WriteFile(path, data, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Print("Failed to write config to", path)
|
config.debug.Print("Failed to write config to", path)
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
45
debug.go
45
debug.go
@ -22,35 +22,54 @@ import (
|
|||||||
"github.com/rivo/tview"
|
"github.com/rivo/tview"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const DebugPaneHeight = 40
|
||||||
|
|
||||||
|
type DebugPrinter interface {
|
||||||
|
Printf(text string, args ...interface{})
|
||||||
|
Print(text ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
type DebugPane struct {
|
type DebugPane struct {
|
||||||
text string
|
|
||||||
pane *tview.TextView
|
pane *tview.TextView
|
||||||
num int
|
num int
|
||||||
|
gmx Gomuks
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDebugPane(gmx Gomuks) *DebugPane {
|
||||||
|
pane := tview.NewTextView()
|
||||||
|
pane.
|
||||||
|
SetScrollable(true).
|
||||||
|
SetWrap(true)
|
||||||
|
pane.SetChangedFunc(func() {
|
||||||
|
gmx.App().Draw()
|
||||||
|
})
|
||||||
|
pane.SetBorder(true).SetTitle("Debug output")
|
||||||
|
fmt.Fprintln(pane, "[0] Debug pane initialized")
|
||||||
|
|
||||||
|
return &DebugPane{
|
||||||
|
pane: pane,
|
||||||
|
num: 0,
|
||||||
|
gmx: gmx,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DebugPane) Printf(text string, args ...interface{}) {
|
func (db *DebugPane) Printf(text string, args ...interface{}) {
|
||||||
db.num++
|
db.Write(fmt.Sprintf(text, args...))
|
||||||
db.Write(fmt.Sprintf("[%d] %s\n", db.num, fmt.Sprintf(text, args...)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DebugPane) Print(text ...interface{}) {
|
func (db *DebugPane) Print(text ...interface{}) {
|
||||||
db.num++
|
db.Write(fmt.Sprint(text...))
|
||||||
db.Write(fmt.Sprintf("[%d] %s", db.num, fmt.Sprintln(text...)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DebugPane) Write(text string) {
|
func (db *DebugPane) Write(text string) {
|
||||||
if db.pane != nil {
|
if db.pane != nil {
|
||||||
db.text += text
|
db.num++
|
||||||
db.pane.SetText(db.text)
|
fmt.Fprintf(db.pane, "[%d] %s\n", db.num, text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DebugPane) Wrap(main *tview.Pages) tview.Primitive {
|
func (db *DebugPane) Wrap(main tview.Primitive) tview.Primitive {
|
||||||
db.pane = tview.NewTextView()
|
return tview.NewGrid().SetRows(0, DebugPaneHeight).SetColumns(0).
|
||||||
db.pane.SetBorder(true).SetTitle("Debug output")
|
|
||||||
db.text += "[0] Debug pane initialized\n"
|
|
||||||
db.pane.SetText(db.text)
|
|
||||||
return tview.NewGrid().SetRows(0, 20).SetColumns(0).
|
|
||||||
AddItem(main, 0, 0, 1, 1, 1, 1, true).
|
AddItem(main, 0, 0, 1, 1, 1, 1, true).
|
||||||
AddItem(db.pane, 1, 0, 1, 1, 1, 1, false)
|
AddItem(db.pane, 1, 0, 1, 1, 1, 1, false)
|
||||||
}
|
}
|
||||||
|
128
gomuks.go
128
gomuks.go
@ -20,94 +20,84 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/gdamore/tcell"
|
"github.com/matrix-org/gomatrix"
|
||||||
"github.com/rivo/tview"
|
"github.com/rivo/tview"
|
||||||
)
|
)
|
||||||
|
|
||||||
var matrix = new(MatrixContainer)
|
type Gomuks interface {
|
||||||
var config = new(Config)
|
Debug() DebugPrinter
|
||||||
var debug = new(DebugPane)
|
Matrix() *gomatrix.Client
|
||||||
|
MatrixContainer() *MatrixContainer
|
||||||
func main() {
|
App() *tview.Application
|
||||||
configDir := filepath.Join(os.Getenv("HOME"), ".config/gomuks")
|
UI() *GomuksUI
|
||||||
os.MkdirAll(configDir, 0700)
|
Config() *Config
|
||||||
config.Load(configDir)
|
|
||||||
|
|
||||||
views := tview.NewPages()
|
|
||||||
InitUI(views)
|
|
||||||
|
|
||||||
main := debug.Wrap(views)
|
|
||||||
|
|
||||||
if len(config.MXID) > 0 {
|
|
||||||
config.LoadSession(config.MXID)
|
|
||||||
}
|
}
|
||||||
matrix.Init(config)
|
|
||||||
|
|
||||||
if err := tview.NewApplication().SetRoot(main, true).Run(); err != nil {
|
type gomuks struct {
|
||||||
|
app *tview.Application
|
||||||
|
ui *GomuksUI
|
||||||
|
matrix *MatrixContainer
|
||||||
|
debug *DebugPane
|
||||||
|
config *Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGomuks(debug bool) *gomuks {
|
||||||
|
configDir := filepath.Join(os.Getenv("HOME"), ".config/gomuks")
|
||||||
|
gmx := &gomuks{
|
||||||
|
app: tview.NewApplication(),
|
||||||
|
}
|
||||||
|
gmx.debug = NewDebugPane(gmx)
|
||||||
|
gmx.config = NewConfig(gmx, configDir)
|
||||||
|
gmx.ui = NewGomuksUI(gmx)
|
||||||
|
gmx.matrix = NewMatrixContainer(gmx)
|
||||||
|
gmx.ui.matrix = gmx.matrix
|
||||||
|
|
||||||
|
gmx.config.Load()
|
||||||
|
if len(gmx.config.MXID) > 0 {
|
||||||
|
gmx.config.LoadSession(gmx.config.MXID)
|
||||||
|
}
|
||||||
|
|
||||||
|
gmx.matrix.InitClient()
|
||||||
|
|
||||||
|
main := gmx.ui.InitViews()
|
||||||
|
if debug {
|
||||||
|
main = gmx.debug.Wrap(main)
|
||||||
|
}
|
||||||
|
gmx.app.SetRoot(main, true)
|
||||||
|
|
||||||
|
return gmx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gmx *gomuks) Start() {
|
||||||
|
if err := gmx.app.Run(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitUI(views *tview.Pages) {
|
func (gmx *gomuks) Debug() DebugPrinter {
|
||||||
views.AddPage("login", InitLoginUI(), true, true)
|
return gmx.debug
|
||||||
}
|
}
|
||||||
|
|
||||||
func Center(width, height int, p tview.Primitive) tview.Primitive {
|
func (gmx *gomuks) Matrix() *gomatrix.Client {
|
||||||
return tview.NewFlex().
|
return gmx.matrix.client
|
||||||
AddItem(tview.NewBox(), 0, 1, false).
|
|
||||||
AddItem(tview.NewFlex().
|
|
||||||
SetDirection(tview.FlexRow).
|
|
||||||
AddItem(tview.NewBox(), 0, 1, false).
|
|
||||||
AddItem(p, height, 1, true).
|
|
||||||
AddItem(tview.NewBox(), 0, 1, false), width, 1, true).
|
|
||||||
AddItem(tview.NewBox(), 0, 1, false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type FormTextView struct {
|
func (gmx *gomuks) MatrixContainer() *MatrixContainer {
|
||||||
*tview.TextView
|
return gmx.matrix
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ftv *FormTextView) GetLabel() string {
|
func (gmx *gomuks) App() *tview.Application {
|
||||||
return ""
|
return gmx.app
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ftv *FormTextView) SetFormAttributes(label string, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) tview.FormItem {
|
func (gmx *gomuks) Config() *Config {
|
||||||
return ftv
|
return gmx.config
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ftv *FormTextView) GetFieldWidth() int {
|
func (gmx *gomuks) UI() *GomuksUI {
|
||||||
_, _, w, _ := ftv.TextView.GetRect()
|
return gmx.ui
|
||||||
return w
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ftv *FormTextView) SetFinishedFunc(handler func(key tcell.Key)) tview.FormItem {
|
func main() {
|
||||||
ftv.SetDoneFunc(handler)
|
NewGomuks(true).Start()
|
||||||
return ftv
|
|
||||||
}
|
|
||||||
|
|
||||||
func login(form *tview.Form) func() {
|
|
||||||
return func() {
|
|
||||||
hs := form.GetFormItem(0).(*tview.InputField).GetText()
|
|
||||||
mxid := form.GetFormItem(1).(*tview.InputField).GetText()
|
|
||||||
password := form.GetFormItem(2).(*tview.InputField).GetText()
|
|
||||||
debug.Printf("%s %s %s", hs, mxid, password)
|
|
||||||
config.HS = hs
|
|
||||||
debug.Print(matrix.Init(config))
|
|
||||||
debug.Print(matrix.Login(mxid, password))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func InitLoginUI() tview.Primitive {
|
|
||||||
form := tview.NewForm().SetButtonsAlign(tview.AlignCenter)
|
|
||||||
hs := config.HS
|
|
||||||
if len(hs) == 0 {
|
|
||||||
hs = "https://matrix.org"
|
|
||||||
}
|
|
||||||
form.
|
|
||||||
AddInputField("Homeserver", hs, 30, nil, nil).
|
|
||||||
AddInputField("Username", config.MXID, 30, nil, nil).
|
|
||||||
AddPasswordField("Password", "", 30, '*', nil).
|
|
||||||
AddButton("Log in", login(form))
|
|
||||||
form.SetBorder(true).SetTitle("Log in to Matrix")
|
|
||||||
return Center(45, 13, form)
|
|
||||||
}
|
}
|
||||||
|
70
matrix.go
70
matrix.go
@ -23,27 +23,36 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type MatrixContainer struct {
|
type MatrixContainer struct {
|
||||||
lient *gomatrix.Client
|
client *gomatrix.Client
|
||||||
|
gmx Gomuks
|
||||||
|
ui *GomuksUI
|
||||||
|
debug DebugPrinter
|
||||||
config *Config
|
config *Config
|
||||||
running bool
|
running bool
|
||||||
stop chan bool
|
stop chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) Initialized() bool {
|
func NewMatrixContainer(gmx Gomuks) *MatrixContainer {
|
||||||
return c.lient != nil
|
c := &MatrixContainer{
|
||||||
|
config: gmx.Config(),
|
||||||
|
debug: gmx.Debug(),
|
||||||
|
ui: gmx.UI(),
|
||||||
|
gmx: gmx,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) Init(config *Config) error {
|
return c
|
||||||
c.config = config
|
|
||||||
|
|
||||||
if c.lient != nil {
|
|
||||||
c.lient.StopSync()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *MatrixContainer) InitClient() error {
|
||||||
if len(c.config.HS) == 0 {
|
if len(c.config.HS) == 0 {
|
||||||
return fmt.Errorf("no homeserver in config")
|
return fmt.Errorf("no homeserver in config")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.client != nil {
|
||||||
|
c.Stop()
|
||||||
|
c.client = nil
|
||||||
|
}
|
||||||
|
|
||||||
var mxid, accessToken string
|
var mxid, accessToken string
|
||||||
if c.config.Session != nil {
|
if c.config.Session != nil {
|
||||||
accessToken = c.config.Session.AccessToken
|
accessToken = c.config.Session.AccessToken
|
||||||
@ -51,7 +60,7 @@ func (c *MatrixContainer) Init(config *Config) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
c.lient, err = gomatrix.NewClient(c.config.HS, mxid, accessToken)
|
c.client, err = gomatrix.NewClient(c.config.HS, mxid, accessToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -64,8 +73,12 @@ func (c *MatrixContainer) Init(config *Config) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *MatrixContainer) Initialized() bool {
|
||||||
|
return c.client != nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) Login(user, password string) error {
|
func (c *MatrixContainer) Login(user, password string) error {
|
||||||
resp, err := c.lient.Login(&gomatrix.ReqLogin{
|
resp, err := c.client.Login(&gomatrix.ReqLogin{
|
||||||
Type: "m.login.password",
|
Type: "m.login.password",
|
||||||
User: user,
|
User: user,
|
||||||
Password: password,
|
Password: password,
|
||||||
@ -73,7 +86,7 @@ func (c *MatrixContainer) Login(user, password string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.lient.SetCredentials(resp.UserID, resp.AccessToken)
|
c.client.SetCredentials(resp.UserID, resp.AccessToken)
|
||||||
c.config.MXID = resp.UserID
|
c.config.MXID = resp.UserID
|
||||||
c.config.Save()
|
c.config.Save()
|
||||||
|
|
||||||
@ -88,31 +101,50 @@ func (c *MatrixContainer) Login(user, password string) error {
|
|||||||
|
|
||||||
func (c *MatrixContainer) Stop() {
|
func (c *MatrixContainer) Stop() {
|
||||||
c.stop <- true
|
c.stop <- true
|
||||||
c.lient.StopSync()
|
c.client.StopSync()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MatrixContainer) UpdateRoomList() {
|
||||||
|
rooms, err := c.client.JoinedRooms()
|
||||||
|
if err != nil {
|
||||||
|
c.debug.Print(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ui.SetRoomList(rooms.JoinedRooms)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) Start() {
|
func (c *MatrixContainer) Start() {
|
||||||
debug.Print("Starting sync...")
|
c.debug.Print("Starting sync...")
|
||||||
c.running = true
|
c.running = true
|
||||||
c.lient.Store = c.config.Session
|
c.ui.SetView(ViewMain)
|
||||||
|
c.client.Store = c.config.Session
|
||||||
|
|
||||||
syncer := c.lient.Syncer.(*gomatrix.DefaultSyncer)
|
c.UpdateRoomList()
|
||||||
|
|
||||||
|
syncer := c.client.Syncer.(*gomatrix.DefaultSyncer)
|
||||||
syncer.OnEventType("m.room.message", c.HandleMessage)
|
syncer.OnEventType("m.room.message", c.HandleMessage)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-c.stop:
|
case <-c.stop:
|
||||||
debug.Print("Stopping sync...")
|
c.debug.Print("Stopping sync...")
|
||||||
c.running = false
|
c.running = false
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
if err := c.lient.Sync(); err != nil {
|
if err := c.client.Sync(); err != nil {
|
||||||
debug.Print("Sync() errored", err)
|
c.debug.Print("Sync() errored", err)
|
||||||
|
} else {
|
||||||
|
c.debug.Print("Sync() returned without error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) HandleMessage(evt *gomatrix.Event) {
|
func (c *MatrixContainer) HandleMessage(evt *gomatrix.Event) {
|
||||||
debug.Print("Message received")
|
message, _ := evt.Content["body"].(string)
|
||||||
|
c.ui.Append(evt.RoomID, evt.Sender, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *MatrixContainer) SendMessage(roomID, message string) {
|
||||||
|
c.client.SendText(roomID, message)
|
||||||
}
|
}
|
||||||
|
11
session.go
11
session.go
@ -31,6 +31,8 @@ type Session struct {
|
|||||||
NextBatch string
|
NextBatch string
|
||||||
FilterID string
|
FilterID string
|
||||||
Rooms map[string]*gomatrix.Room
|
Rooms map[string]*gomatrix.Room
|
||||||
|
|
||||||
|
debug DebugPrinter `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (config *Config) LoadSession(mxid string) {
|
func (config *Config) LoadSession(mxid string) {
|
||||||
@ -43,19 +45,20 @@ func (config *Config) NewSession(mxid string) *Session {
|
|||||||
MXID: mxid,
|
MXID: mxid,
|
||||||
path: filepath.Join(config.dir, mxid+".session"),
|
path: filepath.Join(config.dir, mxid+".session"),
|
||||||
Rooms: make(map[string]*gomatrix.Room),
|
Rooms: make(map[string]*gomatrix.Room),
|
||||||
|
debug: config.debug,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) Load() {
|
func (s *Session) Load() {
|
||||||
data, err := ioutil.ReadFile(s.path)
|
data, err := ioutil.ReadFile(s.path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Print("Failed to read session from", s.path)
|
s.debug.Print("Failed to read session from", s.path)
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.Unmarshal(data, s)
|
err = json.Unmarshal(data, s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Print("Failed to parse session at", s.path)
|
s.debug.Print("Failed to parse session at", s.path)
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,13 +66,13 @@ func (s *Session) Load() {
|
|||||||
func (s *Session) Save() {
|
func (s *Session) Save() {
|
||||||
data, err := json.Marshal(s)
|
data, err := json.Marshal(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Print("Failed to marshal session of", s.MXID)
|
s.debug.Print("Failed to marshal session of", s.MXID)
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ioutil.WriteFile(s.path, data, 0600)
|
err = ioutil.WriteFile(s.path, data, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Print("Failed to write session to", s.path)
|
s.debug.Print("Failed to write session to", s.path)
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
71
ui.go
Normal file
71
ui.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
// gomuks - A terminal Matrix client written in Go.
|
||||||
|
// Copyright (C) 2018 Tulir Asokan
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rivo/tview"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Allowed views in GomuksUI
|
||||||
|
const (
|
||||||
|
ViewLogin = "login"
|
||||||
|
ViewMain = "main"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GomuksUI struct {
|
||||||
|
gmx Gomuks
|
||||||
|
app *tview.Application
|
||||||
|
matrix *MatrixContainer
|
||||||
|
debug DebugPrinter
|
||||||
|
config *Config
|
||||||
|
views *tview.Pages
|
||||||
|
|
||||||
|
mainView *tview.Grid
|
||||||
|
mainViewRoomList *tview.List
|
||||||
|
mainViewRoomView *tview.Pages
|
||||||
|
mainViewInput *tview.InputField
|
||||||
|
mainViewRooms map[string]*RoomView
|
||||||
|
currentRoomIndex int
|
||||||
|
roomList []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGomuksUI(gmx Gomuks) (ui *GomuksUI) {
|
||||||
|
ui = &GomuksUI{
|
||||||
|
gmx: gmx,
|
||||||
|
app: gmx.App(),
|
||||||
|
matrix: gmx.MatrixContainer(),
|
||||||
|
debug: gmx.Debug(),
|
||||||
|
config: gmx.Config(),
|
||||||
|
views: tview.NewPages(),
|
||||||
|
}
|
||||||
|
ui.views.SetChangedFunc(ui.Render)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *GomuksUI) Render() {
|
||||||
|
ui.app.Draw()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *GomuksUI) SetView(name string) {
|
||||||
|
ui.views.SwitchToPage(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *GomuksUI) InitViews() tview.Primitive {
|
||||||
|
ui.views.AddPage(ViewLogin, ui.MakeLoginUI(), true, true)
|
||||||
|
ui.views.AddPage(ViewMain, ui.MakeMainUI(), true, false)
|
||||||
|
return ui.views
|
||||||
|
}
|
55
uiutil.go
Normal file
55
uiutil.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// gomuks - A terminal Matrix client written in Go.
|
||||||
|
// Copyright (C) 2018 Tulir Asokan
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
|
"github.com/rivo/tview"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Center(width, height int, p tview.Primitive) tview.Primitive {
|
||||||
|
return tview.NewFlex().
|
||||||
|
AddItem(tview.NewBox(), 0, 1, false).
|
||||||
|
AddItem(tview.NewFlex().
|
||||||
|
SetDirection(tview.FlexRow).
|
||||||
|
AddItem(tview.NewBox(), 0, 1, false).
|
||||||
|
AddItem(p, height, 1, true).
|
||||||
|
AddItem(tview.NewBox(), 0, 1, false), width, 1, true).
|
||||||
|
AddItem(tview.NewBox(), 0, 1, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
type FormTextView struct {
|
||||||
|
*tview.TextView
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ftv *FormTextView) GetLabel() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ftv *FormTextView) SetFormAttributes(label string, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) tview.FormItem {
|
||||||
|
return ftv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ftv *FormTextView) GetFieldWidth() int {
|
||||||
|
_, _, w, _ := ftv.TextView.GetRect()
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ftv *FormTextView) SetFinishedFunc(handler func(key tcell.Key)) tview.FormItem {
|
||||||
|
ftv.SetDoneFunc(handler)
|
||||||
|
return ftv
|
||||||
|
}
|
49
view-login.go
Normal file
49
view-login.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// gomuks - A terminal Matrix client written in Go.
|
||||||
|
// Copyright (C) 2018 Tulir Asokan
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rivo/tview"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (ui *GomuksUI) MakeLoginUI() tview.Primitive {
|
||||||
|
form := tview.NewForm().SetButtonsAlign(tview.AlignCenter)
|
||||||
|
hs := ui.config.HS
|
||||||
|
if len(hs) == 0 {
|
||||||
|
hs = "https://matrix.org"
|
||||||
|
}
|
||||||
|
form.
|
||||||
|
AddInputField("Homeserver", hs, 30, nil, nil).
|
||||||
|
AddInputField("Username", ui.config.MXID, 30, nil, nil).
|
||||||
|
AddPasswordField("Password", "", 30, '*', nil).
|
||||||
|
AddButton("Log in", ui.login(form))
|
||||||
|
form.SetBorder(true).SetTitle("Log in to Matrix")
|
||||||
|
return Center(45, 13, form)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *GomuksUI) login(form *tview.Form) func() {
|
||||||
|
return func() {
|
||||||
|
hs := form.GetFormItem(0).(*tview.InputField).GetText()
|
||||||
|
mxid := form.GetFormItem(1).(*tview.InputField).GetText()
|
||||||
|
password := form.GetFormItem(2).(*tview.InputField).GetText()
|
||||||
|
|
||||||
|
ui.debug.Printf("Logging into %s as %s...", hs, mxid)
|
||||||
|
ui.config.HS = hs
|
||||||
|
ui.debug.Print(ui.matrix.InitClient())
|
||||||
|
ui.debug.Print(ui.matrix.Login(mxid, password))
|
||||||
|
}
|
||||||
|
}
|
164
view-main.go
Normal file
164
view-main.go
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
// gomuks - A terminal Matrix client written in Go.
|
||||||
|
// Copyright (C) 2018 Tulir Asokan
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
|
"github.com/rivo/tview"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RoomView struct {
|
||||||
|
*tview.Grid
|
||||||
|
|
||||||
|
sender, message *tview.TextView
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRoomView() *RoomView {
|
||||||
|
view := &RoomView{
|
||||||
|
tview.NewGrid(),
|
||||||
|
tview.NewTextView(),
|
||||||
|
tview.NewTextView(),
|
||||||
|
}
|
||||||
|
view.SetColumns(30, 0).SetRows(0)
|
||||||
|
|
||||||
|
view.sender.SetTextAlign(tview.AlignRight)
|
||||||
|
view.sender.SetScrollable(true)
|
||||||
|
view.message.SetScrollable(true)
|
||||||
|
|
||||||
|
view.AddItem(view.sender, 0, 0, 1, 1, 1, 1, false)
|
||||||
|
view.AddItem(view.message, 0, 1, 1, 1, 1, 1, false)
|
||||||
|
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *GomuksUI) MakeMainUI() tview.Primitive {
|
||||||
|
ui.mainView = tview.NewGrid().SetColumns(40, 0).SetRows(0, 2)
|
||||||
|
|
||||||
|
ui.mainViewRoomList = tview.NewList().ShowSecondaryText(false)
|
||||||
|
ui.mainViewRoomList.SetBorderPadding(1, 1, 1, 1)
|
||||||
|
ui.mainView.AddItem(ui.mainViewRoomList, 0, 0, 2, 1, 2, 1, false)
|
||||||
|
|
||||||
|
ui.mainViewRoomView = tview.NewPages()
|
||||||
|
ui.mainViewRoomView.SetChangedFunc(ui.Render)
|
||||||
|
ui.mainView.AddItem(ui.mainViewRoomView, 0, 1, 1, 1, 1, 1, false)
|
||||||
|
|
||||||
|
ui.mainViewInput = tview.NewInputField()
|
||||||
|
ui.mainViewInput.SetDoneFunc(func(key tcell.Key) {
|
||||||
|
if key == tcell.KeyEnter {
|
||||||
|
room, text := ui.currentRoom(), ui.mainViewInput.GetText()
|
||||||
|
if len(text) == 0 {
|
||||||
|
return
|
||||||
|
} else if text[0] == '/' {
|
||||||
|
args := strings.SplitN(text, " ", 2)
|
||||||
|
command := strings.ToLower(args[0])
|
||||||
|
args = args[1:]
|
||||||
|
ui.HandleCommand(room, command, args)
|
||||||
|
} else {
|
||||||
|
ui.matrix.SendMessage(room, text)
|
||||||
|
}
|
||||||
|
ui.mainViewInput.SetText("")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
ui.mainView.AddItem(ui.mainViewInput, 1, 1, 1, 1, 1, 1, true)
|
||||||
|
|
||||||
|
ui.debug.Print(ui.mainViewInput.SetInputCapture(ui.MainUIKeyHandler))
|
||||||
|
|
||||||
|
ui.mainViewRooms = make(map[string]*RoomView)
|
||||||
|
|
||||||
|
return ui.mainView
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *GomuksUI) HandleCommand(room, command string, args []string) {
|
||||||
|
switch command {
|
||||||
|
case "quit":
|
||||||
|
ui.matrix.Stop()
|
||||||
|
ui.app.Stop()
|
||||||
|
case "part":
|
||||||
|
case "leave":
|
||||||
|
ui.matrix.client.LeaveRoom(room)
|
||||||
|
case "join":
|
||||||
|
if len(args) == 0 {
|
||||||
|
ui.Append(room, "*", "Usage: /join <room>")
|
||||||
|
}
|
||||||
|
mxid := args[0]
|
||||||
|
server := mxid[strings.Index(mxid, ":")+1:]
|
||||||
|
ui.matrix.client.JoinRoom(mxid, server, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *GomuksUI) MainUIKeyHandler(key *tcell.EventKey) *tcell.EventKey {
|
||||||
|
ui.debug.Print(key)
|
||||||
|
if key.Modifiers() == tcell.ModCtrl {
|
||||||
|
if key.Key() == tcell.KeyDown {
|
||||||
|
ui.SwitchRoom(ui.currentRoomIndex + 1)
|
||||||
|
ui.mainViewRoomList.SetCurrentItem(ui.currentRoomIndex)
|
||||||
|
} else if key.Key() == tcell.KeyUp {
|
||||||
|
ui.SwitchRoom(ui.currentRoomIndex - 1)
|
||||||
|
ui.mainViewRoomList.SetCurrentItem(ui.currentRoomIndex)
|
||||||
|
}
|
||||||
|
} else if key.Key() == tcell.KeyPgUp || key.Key() == tcell.KeyPgDn {
|
||||||
|
ui.mainViewRooms[ui.currentRoom()].sender.InputHandler()(key, nil)
|
||||||
|
ui.mainViewRooms[ui.currentRoom()].message.InputHandler()(key, nil)
|
||||||
|
} else {
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *GomuksUI) SetRoomList(rooms []string) {
|
||||||
|
ui.roomList = rooms
|
||||||
|
ui.mainViewRoomList.Clear()
|
||||||
|
for index, room := range rooms {
|
||||||
|
localRoomIndex := index
|
||||||
|
ui.mainViewRoomList.AddItem(room, "", 0, func() {
|
||||||
|
ui.SwitchRoom(localRoomIndex)
|
||||||
|
})
|
||||||
|
if !ui.mainViewRoomView.HasPage(room) {
|
||||||
|
roomView := NewRoomView()
|
||||||
|
ui.mainViewRooms[room] = roomView
|
||||||
|
ui.mainViewRoomView.AddPage(room, roomView, true, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ui.SwitchRoom(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *GomuksUI) currentRoom() string {
|
||||||
|
if len(ui.roomList) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return ui.roomList[ui.currentRoomIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *GomuksUI) SwitchRoom(roomIndex int) {
|
||||||
|
if roomIndex < 0 {
|
||||||
|
roomIndex = len(ui.roomList) - 1
|
||||||
|
}
|
||||||
|
ui.currentRoomIndex = roomIndex % len(ui.roomList)
|
||||||
|
ui.mainViewRoomView.SwitchToPage(ui.roomList[ui.currentRoomIndex])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ui *GomuksUI) Append(room, sender, message string) {
|
||||||
|
roomView, ok := ui.mainViewRooms[room]
|
||||||
|
if ok {
|
||||||
|
fmt.Fprintf(roomView.sender, sender)
|
||||||
|
fmt.Fprintf(roomView.message, sender)
|
||||||
|
ui.Render()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user