Organize files
This commit is contained in:
parent
0509b19562
commit
72945c9a28
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package main
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -23,22 +23,19 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
"maunium.net/go/gomuks/ui/debug"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
MXID string `yaml:"mxid"`
|
MXID string `yaml:"mxid"`
|
||||||
HS string `yaml:"homeserver"`
|
HS string `yaml:"homeserver"`
|
||||||
|
|
||||||
dir string `yaml:"-"`
|
dir string `yaml:"-"`
|
||||||
gmx Gomuks `yaml:"-"`
|
Session *Session `yaml:"-"`
|
||||||
debug DebugPrinter `yaml:"-"`
|
|
||||||
Session *Session `yaml:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfig(gmx Gomuks, dir string) *Config {
|
func NewConfig(dir string) *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
gmx: gmx,
|
|
||||||
debug: gmx.Debug(),
|
|
||||||
dir: dir,
|
dir: dir,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,14 +64,14 @@ func (config *Config) Save() {
|
|||||||
os.MkdirAll(config.dir, 0700)
|
os.MkdirAll(config.dir, 0700)
|
||||||
data, err := yaml.Marshal(&config)
|
data, err := yaml.Marshal(&config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
config.debug.Print("Failed to marshal 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 {
|
||||||
config.debug.Print("Failed to write config to", path)
|
debug.Print("Failed to write config to", path)
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package main
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -22,6 +22,8 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"maunium.net/go/gomatrix"
|
"maunium.net/go/gomatrix"
|
||||||
|
rooms "maunium.net/go/gomuks/matrix/room"
|
||||||
|
"maunium.net/go/gomuks/ui/debug"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
@ -30,9 +32,7 @@ type Session struct {
|
|||||||
AccessToken string
|
AccessToken string
|
||||||
NextBatch string
|
NextBatch string
|
||||||
FilterID string
|
FilterID string
|
||||||
Rooms map[string]*Room
|
Rooms map[string]*rooms.Room
|
||||||
|
|
||||||
debug DebugPrinter `json:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (config *Config) LoadSession(mxid string) {
|
func (config *Config) LoadSession(mxid string) {
|
||||||
@ -44,13 +44,12 @@ func (config *Config) NewSession(mxid string) *Session {
|
|||||||
return &Session{
|
return &Session{
|
||||||
MXID: mxid,
|
MXID: mxid,
|
||||||
path: filepath.Join(config.dir, mxid+".session"),
|
path: filepath.Join(config.dir, mxid+".session"),
|
||||||
Rooms: make(map[string]*Room),
|
Rooms: make(map[string]*rooms.Room),
|
||||||
debug: config.debug,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) Clear() {
|
func (s *Session) Clear() {
|
||||||
s.Rooms = make(map[string]*Room)
|
s.Rooms = make(map[string]*rooms.Room)
|
||||||
s.NextBatch = ""
|
s.NextBatch = ""
|
||||||
s.FilterID = ""
|
s.FilterID = ""
|
||||||
s.Save()
|
s.Save()
|
||||||
@ -59,13 +58,13 @@ func (s *Session) Clear() {
|
|||||||
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 {
|
||||||
s.debug.Print("Failed to read session from", s.path)
|
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 {
|
||||||
s.debug.Print("Failed to parse session at", s.path)
|
debug.Print("Failed to parse session at", s.path)
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,13 +72,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 {
|
||||||
s.debug.Print("Failed to marshal session of", s.MXID)
|
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 {
|
||||||
s.debug.Print("Failed to write session to", s.path)
|
debug.Print("Failed to write session to", s.path)
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -92,16 +91,16 @@ func (s *Session) LoadNextBatch(_ string) string {
|
|||||||
return s.NextBatch
|
return s.NextBatch
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) GetRoom(mxid string) *Room {
|
func (s *Session) GetRoom(mxid string) *rooms.Room {
|
||||||
room, _ := s.Rooms[mxid]
|
room, _ := s.Rooms[mxid]
|
||||||
if room == nil {
|
if room == nil {
|
||||||
room = NewRoom(mxid)
|
room = rooms.NewRoom(mxid)
|
||||||
s.Rooms[room.ID] = room
|
s.Rooms[room.ID] = room
|
||||||
}
|
}
|
||||||
return room
|
return room
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) PutRoom(room *Room) {
|
func (s *Session) PutRoom(room *rooms.Room) {
|
||||||
s.Rooms[room.ID] = room
|
s.Rooms[room.ID] = room
|
||||||
s.Save()
|
s.Save()
|
||||||
}
|
}
|
14
go.mod
14
go.mod
@ -1,14 +0,0 @@
|
|||||||
module "maunium.net/go/gomuks"
|
|
||||||
|
|
||||||
require (
|
|
||||||
"github.com/gdamore/encoding" v0.0.0-20151215212835-b23993cbb635
|
|
||||||
"github.com/gdamore/tcell" v1.0.0
|
|
||||||
"github.com/jroimartin/gocui" v0.0.0-20170827195011-4f518eddb04b
|
|
||||||
"github.com/lucasb-eyer/go-colorful" v0.0.0-20170903184257-231272389856
|
|
||||||
"github.com/matrix-org/gomatrix" v0.0.0-20171003113848-a7fc80c8060c
|
|
||||||
"github.com/mattn/go-runewidth" v0.0.2
|
|
||||||
"github.com/nsf/termbox-go" v0.0.0-20180303152453-e2050e41c884
|
|
||||||
"github.com/rivo/tview" v0.0.0-20180313071706-0b69b9b58142
|
|
||||||
"golang.org/x/text" v0.0.0-20171214130843-f21a4dfb5e38
|
|
||||||
"gopkg.in/yaml.v2" v1.1.1-gopkgin-v2.1.1
|
|
||||||
)
|
|
60
gomuks.go
60
gomuks.go
@ -21,43 +21,37 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"maunium.net/go/gomatrix"
|
"maunium.net/go/gomatrix"
|
||||||
|
"maunium.net/go/gomuks/config"
|
||||||
|
"maunium.net/go/gomuks/interface"
|
||||||
|
"maunium.net/go/gomuks/matrix"
|
||||||
|
"maunium.net/go/gomuks/ui"
|
||||||
|
"maunium.net/go/gomuks/ui/debug"
|
||||||
"maunium.net/go/tview"
|
"maunium.net/go/tview"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Gomuks interface {
|
|
||||||
Debug() DebugPrinter
|
|
||||||
Matrix() *gomatrix.Client
|
|
||||||
MatrixContainer() *MatrixContainer
|
|
||||||
App() *tview.Application
|
|
||||||
UI() *GomuksUI
|
|
||||||
Config() *Config
|
|
||||||
|
|
||||||
Start()
|
|
||||||
Stop()
|
|
||||||
Recover()
|
|
||||||
}
|
|
||||||
|
|
||||||
type gomuks struct {
|
type gomuks struct {
|
||||||
app *tview.Application
|
app *tview.Application
|
||||||
ui *GomuksUI
|
ui *ui.GomuksUI
|
||||||
matrix *MatrixContainer
|
matrix *matrix.Container
|
||||||
debug *DebugPane
|
debug *debug.Pane
|
||||||
config *Config
|
config *config.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
var gdebug DebugPrinter
|
func NewGomuks(enableDebug bool) *gomuks {
|
||||||
|
|
||||||
func NewGomuks(debug bool) *gomuks {
|
|
||||||
configDir := filepath.Join(os.Getenv("HOME"), ".config/gomuks")
|
configDir := filepath.Join(os.Getenv("HOME"), ".config/gomuks")
|
||||||
gmx := &gomuks{
|
gmx := &gomuks{
|
||||||
app: tview.NewApplication(),
|
app: tview.NewApplication(),
|
||||||
}
|
}
|
||||||
gmx.debug = NewDebugPane(gmx)
|
|
||||||
gdebug = gmx.debug
|
gmx.debug = debug.NewPane()
|
||||||
gmx.config = NewConfig(gmx, configDir)
|
gmx.debug.SetChangedFunc(func() {
|
||||||
gmx.ui = NewGomuksUI(gmx)
|
gmx.ui.Render()
|
||||||
gmx.matrix = NewMatrixContainer(gmx)
|
})
|
||||||
gmx.ui.matrix = gmx.matrix
|
debug.Default = gmx.debug
|
||||||
|
|
||||||
|
gmx.config = config.NewConfig(configDir)
|
||||||
|
gmx.ui = ui.NewGomuksUI(gmx)
|
||||||
|
gmx.matrix = matrix.NewMatrixContainer(gmx)
|
||||||
|
|
||||||
gmx.config.Load()
|
gmx.config.Load()
|
||||||
if len(gmx.config.MXID) > 0 {
|
if len(gmx.config.MXID) > 0 {
|
||||||
@ -67,7 +61,7 @@ func NewGomuks(debug bool) *gomuks {
|
|||||||
gmx.matrix.InitClient()
|
gmx.matrix.InitClient()
|
||||||
|
|
||||||
main := gmx.ui.InitViews()
|
main := gmx.ui.InitViews()
|
||||||
if debug {
|
if enableDebug {
|
||||||
main = gmx.debug.Wrap(main)
|
main = gmx.debug.Wrap(main)
|
||||||
}
|
}
|
||||||
gmx.app.SetRoot(main, true)
|
gmx.app.SetRoot(main, true)
|
||||||
@ -101,15 +95,11 @@ func (gmx *gomuks) Start() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gmx *gomuks) Debug() DebugPrinter {
|
|
||||||
return gmx.debug
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gmx *gomuks) Matrix() *gomatrix.Client {
|
func (gmx *gomuks) Matrix() *gomatrix.Client {
|
||||||
return gmx.matrix.client
|
return gmx.matrix.Client()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gmx *gomuks) MatrixContainer() *MatrixContainer {
|
func (gmx *gomuks) MatrixContainer() ifc.MatrixContainer {
|
||||||
return gmx.matrix
|
return gmx.matrix
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,11 +107,11 @@ func (gmx *gomuks) App() *tview.Application {
|
|||||||
return gmx.app
|
return gmx.app
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gmx *gomuks) Config() *Config {
|
func (gmx *gomuks) Config() *config.Config {
|
||||||
return gmx.config
|
return gmx.config
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gmx *gomuks) UI() *GomuksUI {
|
func (gmx *gomuks) UI() ifc.GomuksUI {
|
||||||
return gmx.ui
|
return gmx.ui
|
||||||
}
|
}
|
||||||
|
|
||||||
|
35
interface/gomuks.go
Normal file
35
interface/gomuks.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// 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 ifc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"maunium.net/go/gomatrix"
|
||||||
|
"maunium.net/go/gomuks/config"
|
||||||
|
"maunium.net/go/tview"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Gomuks interface {
|
||||||
|
Matrix() *gomatrix.Client
|
||||||
|
MatrixContainer() MatrixContainer
|
||||||
|
App() *tview.Application
|
||||||
|
UI() GomuksUI
|
||||||
|
Config() *config.Config
|
||||||
|
|
||||||
|
Start()
|
||||||
|
Stop()
|
||||||
|
Recover()
|
||||||
|
}
|
40
interface/matrix.go
Normal file
40
interface/matrix.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// 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 ifc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"maunium.net/go/gomatrix"
|
||||||
|
"maunium.net/go/gomuks/matrix/room"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MatrixContainer interface {
|
||||||
|
Client() *gomatrix.Client
|
||||||
|
InitClient() error
|
||||||
|
Initialized() bool
|
||||||
|
Login(user, password string) error
|
||||||
|
Start()
|
||||||
|
Stop()
|
||||||
|
// HandleMessage(evt *gomatrix.Event)
|
||||||
|
// HandleMembership(evt *gomatrix.Event)
|
||||||
|
// HandleTyping(evt *gomatrix.Event)
|
||||||
|
SendMessage(roomID, message string)
|
||||||
|
SendTyping(roomID string, typing bool)
|
||||||
|
JoinRoom(roomID string) error
|
||||||
|
LeaveRoom(roomID string) error
|
||||||
|
GetHistory(roomID, prevBatch string, limit int) ([]gomatrix.Event, string, error)
|
||||||
|
GetRoom(roomID string) *room.Room
|
||||||
|
}
|
54
interface/ui.go
Normal file
54
interface/ui.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// 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 ifc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"maunium.net/go/gomatrix"
|
||||||
|
"maunium.net/go/gomuks/ui/types"
|
||||||
|
"maunium.net/go/gomuks/ui/widget"
|
||||||
|
"maunium.net/go/tview"
|
||||||
|
)
|
||||||
|
|
||||||
|
type View string
|
||||||
|
|
||||||
|
// Allowed views in GomuksUI
|
||||||
|
const (
|
||||||
|
ViewLogin View = "login"
|
||||||
|
ViewMain View = "main"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GomuksUI interface {
|
||||||
|
Render()
|
||||||
|
SetView(name View)
|
||||||
|
InitViews() tview.Primitive
|
||||||
|
MainView() MainView
|
||||||
|
}
|
||||||
|
|
||||||
|
type MainView interface {
|
||||||
|
InputTabComplete(text string, cursorOffset int) string
|
||||||
|
GetRoom(roomID string) *widget.RoomView
|
||||||
|
HasRoom(roomID string) bool
|
||||||
|
AddRoom(roomID string)
|
||||||
|
RemoveRoom(roomID string)
|
||||||
|
SetRooms(roomIDs []string)
|
||||||
|
|
||||||
|
SetTyping(roomID string, users []string)
|
||||||
|
AddServiceMessage(roomID string, message string)
|
||||||
|
GetHistory(room string)
|
||||||
|
ProcessMessageEvent(evt *gomatrix.Event) (*widget.RoomView, *types.Message)
|
||||||
|
ProcessMembershipEvent(evt *gomatrix.Event, new bool) (*widget.RoomView, *types.Message)
|
||||||
|
}
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package main
|
package matrix
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -22,24 +22,27 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"maunium.net/go/gomatrix"
|
"maunium.net/go/gomatrix"
|
||||||
|
"maunium.net/go/gomuks/config"
|
||||||
|
"maunium.net/go/gomuks/interface"
|
||||||
|
rooms "maunium.net/go/gomuks/matrix/room"
|
||||||
|
"maunium.net/go/gomuks/ui/debug"
|
||||||
|
"maunium.net/go/gomuks/ui/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MatrixContainer struct {
|
type Container struct {
|
||||||
client *gomatrix.Client
|
client *gomatrix.Client
|
||||||
gmx Gomuks
|
gmx ifc.Gomuks
|
||||||
ui *GomuksUI
|
ui ifc.GomuksUI
|
||||||
debug DebugPrinter
|
config *config.Config
|
||||||
config *Config
|
|
||||||
running bool
|
running bool
|
||||||
stop chan bool
|
stop chan bool
|
||||||
|
|
||||||
typing int64
|
typing int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMatrixContainer(gmx Gomuks) *MatrixContainer {
|
func NewMatrixContainer(gmx ifc.Gomuks) *Container {
|
||||||
c := &MatrixContainer{
|
c := &Container{
|
||||||
config: gmx.Config(),
|
config: gmx.Config(),
|
||||||
debug: gmx.Debug(),
|
|
||||||
ui: gmx.UI(),
|
ui: gmx.UI(),
|
||||||
gmx: gmx,
|
gmx: gmx,
|
||||||
}
|
}
|
||||||
@ -47,7 +50,7 @@ func NewMatrixContainer(gmx Gomuks) *MatrixContainer {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) InitClient() error {
|
func (c *Container) 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")
|
||||||
}
|
}
|
||||||
@ -77,11 +80,11 @@ func (c *MatrixContainer) InitClient() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) Initialized() bool {
|
func (c *Container) Initialized() bool {
|
||||||
return c.client != nil
|
return c.client != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) Login(user, password string) error {
|
func (c *Container) Login(user, password string) error {
|
||||||
resp, err := c.client.Login(&gomatrix.ReqLogin{
|
resp, err := c.client.Login(&gomatrix.ReqLogin{
|
||||||
Type: "m.login.password",
|
Type: "m.login.password",
|
||||||
User: user,
|
User: user,
|
||||||
@ -103,24 +106,28 @@ func (c *MatrixContainer) Login(user, password string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) Stop() {
|
func (c *Container) Stop() {
|
||||||
if c.running {
|
if c.running {
|
||||||
c.stop <- true
|
c.stop <- true
|
||||||
c.client.StopSync()
|
c.client.StopSync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) UpdateRoomList() {
|
func (c *Container) Client() *gomatrix.Client {
|
||||||
rooms, err := c.client.JoinedRooms()
|
return c.client
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) UpdateRoomList() {
|
||||||
|
resp, err := c.client.JoinedRooms()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.debug.Print("Error fetching room list:", err)
|
debug.Print("Error fetching room list:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ui.MainView().SetRoomList(rooms.JoinedRooms)
|
c.ui.MainView().SetRooms(resp.JoinedRooms)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) OnLogin() {
|
func (c *Container) OnLogin() {
|
||||||
c.client.Store = c.config.Session
|
c.client.Store = c.config.Session
|
||||||
|
|
||||||
syncer := NewGomuksSyncer(c.config.Session)
|
syncer := NewGomuksSyncer(c.config.Session)
|
||||||
@ -132,38 +139,38 @@ func (c *MatrixContainer) OnLogin() {
|
|||||||
c.UpdateRoomList()
|
c.UpdateRoomList()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) Start() {
|
func (c *Container) Start() {
|
||||||
defer c.gmx.Recover()
|
defer c.gmx.Recover()
|
||||||
|
|
||||||
|
c.ui.SetView(ifc.ViewMain)
|
||||||
c.OnLogin()
|
c.OnLogin()
|
||||||
|
|
||||||
c.debug.Print("Starting sync...")
|
debug.Print("Starting sync...")
|
||||||
c.running = true
|
c.running = true
|
||||||
c.ui.SetView(ViewMain)
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-c.stop:
|
case <-c.stop:
|
||||||
c.debug.Print("Stopping sync...")
|
debug.Print("Stopping sync...")
|
||||||
c.running = false
|
c.running = false
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
if err := c.client.Sync(); err != nil {
|
if err := c.client.Sync(); err != nil {
|
||||||
c.debug.Print("Sync() errored", err)
|
debug.Print("Sync() errored", err)
|
||||||
} else {
|
} else {
|
||||||
c.debug.Print("Sync() returned without error")
|
debug.Print("Sync() returned without error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) HandleMessage(evt *gomatrix.Event) {
|
func (c *Container) HandleMessage(evt *gomatrix.Event) {
|
||||||
room, message := c.ui.MainView().ProcessMessageEvent(evt)
|
room, message := c.ui.MainView().ProcessMessageEvent(evt)
|
||||||
if room != nil {
|
if room != nil {
|
||||||
room.AddMessage(message, AppendMessage)
|
room.AddMessage(message, widget.AppendMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) HandleMembership(evt *gomatrix.Event) {
|
func (c *Container) HandleMembership(evt *gomatrix.Event) {
|
||||||
const Hour = 1 * 60 * 60 * 1000
|
const Hour = 1 * 60 * 60 * 1000
|
||||||
if evt.Unsigned.Age > Hour {
|
if evt.Unsigned.Age > Hour {
|
||||||
return
|
return
|
||||||
@ -172,15 +179,15 @@ func (c *MatrixContainer) HandleMembership(evt *gomatrix.Event) {
|
|||||||
room, message := c.ui.MainView().ProcessMembershipEvent(evt, true)
|
room, message := c.ui.MainView().ProcessMembershipEvent(evt, true)
|
||||||
if room != nil {
|
if room != nil {
|
||||||
// TODO this shouldn't be necessary
|
// TODO this shouldn't be necessary
|
||||||
room.room.UpdateState(evt)
|
room.Room.UpdateState(evt)
|
||||||
// TODO This should probably also be in a different place
|
// TODO This should probably also be in a different place
|
||||||
room.UpdateUserList()
|
room.UpdateUserList()
|
||||||
|
|
||||||
room.AddMessage(message, AppendMessage)
|
room.AddMessage(message, widget.AppendMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) HandleTyping(evt *gomatrix.Event) {
|
func (c *Container) HandleTyping(evt *gomatrix.Event) {
|
||||||
users := evt.Content["user_ids"].([]interface{})
|
users := evt.Content["user_ids"].([]interface{})
|
||||||
|
|
||||||
strUsers := make([]string, len(users))
|
strUsers := make([]string, len(users))
|
||||||
@ -190,29 +197,29 @@ func (c *MatrixContainer) HandleTyping(evt *gomatrix.Event) {
|
|||||||
c.ui.MainView().SetTyping(evt.RoomID, strUsers)
|
c.ui.MainView().SetTyping(evt.RoomID, strUsers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) SendMessage(roomID, message string) {
|
func (c *Container) SendMessage(roomID, message string) {
|
||||||
c.gmx.Recover()
|
c.gmx.Recover()
|
||||||
c.SendTyping(roomID, false)
|
c.SendTyping(roomID, false)
|
||||||
c.client.SendText(roomID, message)
|
c.client.SendText(roomID, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) SendTyping(roomID string, typing bool) {
|
func (c *Container) SendTyping(roomID string, typing bool) {
|
||||||
c.gmx.Recover()
|
c.gmx.Recover()
|
||||||
time := time.Now().Unix()
|
ts := time.Now().Unix()
|
||||||
if c.typing > time && typing {
|
if c.typing > ts && typing {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if typing {
|
if typing {
|
||||||
c.client.UserTyping(roomID, true, 5000)
|
c.client.UserTyping(roomID, true, 5000)
|
||||||
c.typing = time + 5
|
c.typing = ts + 5
|
||||||
} else {
|
} else {
|
||||||
c.client.UserTyping(roomID, false, 0)
|
c.client.UserTyping(roomID, false, 0)
|
||||||
c.typing = 0
|
c.typing = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) JoinRoom(roomID string) error {
|
func (c *Container) JoinRoom(roomID string) error {
|
||||||
if len(roomID) == 0 {
|
if len(roomID) == 0 {
|
||||||
return fmt.Errorf("invalid room ID")
|
return fmt.Errorf("invalid room ID")
|
||||||
}
|
}
|
||||||
@ -222,26 +229,40 @@ func (c *MatrixContainer) JoinRoom(roomID string) error {
|
|||||||
server = roomID[strings.Index(roomID, ":")+1:]
|
server = roomID[strings.Index(roomID, ":")+1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := c.client.JoinRoom(roomID, server, nil)
|
_, err := c.client.JoinRoom(roomID, server, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ui.MainView().AddRoom(resp.RoomID)
|
// TODO probably safe to remove
|
||||||
|
// c.ui.MainView().AddRoom(resp.RoomID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) getState(roomID string) []*gomatrix.Event {
|
func (c *Container) LeaveRoom(roomID string) error {
|
||||||
|
if len(roomID) == 0 {
|
||||||
|
return fmt.Errorf("invalid room ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := c.client.LeaveRoom(roomID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Container) getState(roomID string) []*gomatrix.Event {
|
||||||
content := make([]*gomatrix.Event, 0)
|
content := make([]*gomatrix.Event, 0)
|
||||||
err := c.client.StateEvent(roomID, "", "", &content)
|
err := c.client.StateEvent(roomID, "", "", &content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.debug.Print("Error getting state of", roomID, err)
|
debug.Print("Error getting state of", roomID, err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return content
|
return content
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) GetHistory(roomID, prevBatch string, limit int) ([]gomatrix.Event, string, error) {
|
func (c *Container) GetHistory(roomID, prevBatch string, limit int) ([]gomatrix.Event, string, error) {
|
||||||
resp, err := c.client.Messages(roomID, prevBatch, "", 'b', limit)
|
resp, err := c.client.Messages(roomID, prevBatch, "", 'b', limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
@ -249,7 +270,7 @@ func (c *MatrixContainer) GetHistory(roomID, prevBatch string, limit int) ([]gom
|
|||||||
return resp.Chunk, resp.End, nil
|
return resp.Chunk, resp.End, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MatrixContainer) GetRoom(roomID string) *Room {
|
func (c *Container) GetRoom(roomID string) *rooms.Room {
|
||||||
room := c.config.Session.GetRoom(roomID)
|
room := c.config.Session.GetRoom(roomID)
|
||||||
if room != nil && len(room.State) == 0 {
|
if room != nil && len(room.State) == 0 {
|
||||||
events := c.getState(room.ID)
|
events := c.getState(room.ID)
|
51
matrix/room/member.go
Normal file
51
matrix/room/member.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// 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 room
|
||||||
|
|
||||||
|
import (
|
||||||
|
"maunium.net/go/gomatrix"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Member struct {
|
||||||
|
UserID string `json:"-"`
|
||||||
|
Membership string `json:"membership"`
|
||||||
|
DisplayName string `json:"displayname"`
|
||||||
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func eventToRoomMember(userID string, event *gomatrix.Event) *Member {
|
||||||
|
if event == nil {
|
||||||
|
return &Member{
|
||||||
|
UserID: userID,
|
||||||
|
Membership: "leave",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
membership, _ := event.Content["membership"].(string)
|
||||||
|
avatarURL, _ := event.Content["avatar_url"].(string)
|
||||||
|
|
||||||
|
displayName, _ := event.Content["displayname"].(string)
|
||||||
|
if len(displayName) == 0 {
|
||||||
|
displayName = userID
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Member{
|
||||||
|
UserID: userID,
|
||||||
|
Membership: membership,
|
||||||
|
DisplayName: displayName,
|
||||||
|
AvatarURL: avatarURL,
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package main
|
package room
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"maunium.net/go/gomatrix"
|
"maunium.net/go/gomatrix"
|
||||||
@ -25,7 +25,7 @@ type Room struct {
|
|||||||
*gomatrix.Room
|
*gomatrix.Room
|
||||||
|
|
||||||
PrevBatch string
|
PrevBatch string
|
||||||
memberCache map[string]*RoomMember
|
memberCache map[string]*Member
|
||||||
nameCache string
|
nameCache string
|
||||||
topicCache string
|
topicCache string
|
||||||
}
|
}
|
||||||
@ -107,38 +107,8 @@ func (room *Room) GetTitle() string {
|
|||||||
return room.nameCache
|
return room.nameCache
|
||||||
}
|
}
|
||||||
|
|
||||||
type RoomMember struct {
|
func (room *Room) createMemberCache() map[string]*Member {
|
||||||
UserID string `json:"-"`
|
cache := make(map[string]*Member)
|
||||||
Membership string `json:"membership"`
|
|
||||||
DisplayName string `json:"displayname"`
|
|
||||||
AvatarURL string `json:"avatar_url"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func eventToRoomMember(userID string, event *gomatrix.Event) *RoomMember {
|
|
||||||
if event == nil {
|
|
||||||
return &RoomMember{
|
|
||||||
UserID: userID,
|
|
||||||
Membership: "leave",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
membership, _ := event.Content["membership"].(string)
|
|
||||||
avatarURL, _ := event.Content["avatar_url"].(string)
|
|
||||||
|
|
||||||
displayName, _ := event.Content["displayname"].(string)
|
|
||||||
if len(displayName) == 0 {
|
|
||||||
displayName = userID
|
|
||||||
}
|
|
||||||
|
|
||||||
return &RoomMember{
|
|
||||||
UserID: userID,
|
|
||||||
Membership: membership,
|
|
||||||
DisplayName: displayName,
|
|
||||||
AvatarURL: avatarURL,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (room *Room) createMemberCache() map[string]*RoomMember {
|
|
||||||
cache := make(map[string]*RoomMember)
|
|
||||||
events := room.GetStateEvents("m.room.member")
|
events := room.GetStateEvents("m.room.member")
|
||||||
if events != nil {
|
if events != nil {
|
||||||
for userID, event := range events {
|
for userID, event := range events {
|
||||||
@ -152,14 +122,14 @@ func (room *Room) createMemberCache() map[string]*RoomMember {
|
|||||||
return cache
|
return cache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (room *Room) GetMembers() map[string]*RoomMember {
|
func (room *Room) GetMembers() map[string]*Member {
|
||||||
if len(room.memberCache) == 0 {
|
if len(room.memberCache) == 0 {
|
||||||
room.createMemberCache()
|
room.createMemberCache()
|
||||||
}
|
}
|
||||||
return room.memberCache
|
return room.memberCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func (room *Room) GetMember(userID string) *RoomMember {
|
func (room *Room) GetMember(userID string) *Member {
|
||||||
if len(room.memberCache) == 0 {
|
if len(room.memberCache) == 0 {
|
||||||
room.createMemberCache()
|
room.createMemberCache()
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package main
|
package matrix
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -7,20 +7,21 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"maunium.net/go/gomatrix"
|
"maunium.net/go/gomatrix"
|
||||||
|
"maunium.net/go/gomuks/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GomuksSyncer is the default syncing implementation. You can either write your own syncer, or selectively
|
// GomuksSyncer is the default syncing implementation. You can either write your own syncer, or selectively
|
||||||
// replace parts of this default syncer (e.g. the ProcessResponse method). The default syncer uses the observer
|
// replace parts of this default syncer (e.g. the ProcessResponse method). The default syncer uses the observer
|
||||||
// pattern to notify callers about incoming events. See GomuksSyncer.OnEventType for more information.
|
// pattern to notify callers about incoming events. See GomuksSyncer.OnEventType for more information.
|
||||||
type GomuksSyncer struct {
|
type GomuksSyncer struct {
|
||||||
Session *Session
|
Session *config.Session
|
||||||
listeners map[string][]gomatrix.OnEventListener // event type to listeners array
|
listeners map[string][]gomatrix.OnEventListener // event type to listeners array
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGomuksSyncer returns an instantiated GomuksSyncer
|
// NewGomuksSyncer returns an instantiated GomuksSyncer
|
||||||
func NewGomuksSyncer(session *Session) *GomuksSyncer {
|
func NewGomuksSyncer(session *config.Session) *GomuksSyncer {
|
||||||
return &GomuksSyncer{
|
return &GomuksSyncer{
|
||||||
Session: session,
|
Session: session,
|
||||||
listeners: make(map[string][]gomatrix.OnEventListener),
|
listeners: make(map[string][]gomatrix.OnEventListener),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -38,22 +39,22 @@ func (s *GomuksSyncer) ProcessResponse(res *gomatrix.RespSync, since string) (er
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
for _, event := range res.Presence.Events {
|
for _, event := range res.Presence.Events {
|
||||||
s.notifyListeners(&event)
|
s.notifyListeners(event)
|
||||||
}
|
}
|
||||||
for roomID, roomData := range res.Rooms.Join {
|
for roomID, roomData := range res.Rooms.Join {
|
||||||
room := s.Session.GetRoom(roomID)
|
room := s.Session.GetRoom(roomID)
|
||||||
for _, event := range roomData.State.Events {
|
for _, event := range roomData.State.Events {
|
||||||
event.RoomID = roomID
|
event.RoomID = roomID
|
||||||
room.UpdateState(&event)
|
room.UpdateState(event)
|
||||||
s.notifyListeners(&event)
|
s.notifyListeners(event)
|
||||||
}
|
}
|
||||||
for _, event := range roomData.Timeline.Events {
|
for _, event := range roomData.Timeline.Events {
|
||||||
event.RoomID = roomID
|
event.RoomID = roomID
|
||||||
s.notifyListeners(&event)
|
s.notifyListeners(event)
|
||||||
}
|
}
|
||||||
for _, event := range roomData.Ephemeral.Events {
|
for _, event := range roomData.Ephemeral.Events {
|
||||||
event.RoomID = roomID
|
event.RoomID = roomID
|
||||||
s.notifyListeners(&event)
|
s.notifyListeners(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(room.PrevBatch) == 0 {
|
if len(room.PrevBatch) == 0 {
|
||||||
@ -64,8 +65,8 @@ func (s *GomuksSyncer) ProcessResponse(res *gomatrix.RespSync, since string) (er
|
|||||||
room := s.Session.GetRoom(roomID)
|
room := s.Session.GetRoom(roomID)
|
||||||
for _, event := range roomData.State.Events {
|
for _, event := range roomData.State.Events {
|
||||||
event.RoomID = roomID
|
event.RoomID = roomID
|
||||||
room.UpdateState(&event)
|
room.UpdateState(event)
|
||||||
s.notifyListeners(&event)
|
s.notifyListeners(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for roomID, roomData := range res.Rooms.Leave {
|
for roomID, roomData := range res.Rooms.Leave {
|
||||||
@ -73,8 +74,8 @@ func (s *GomuksSyncer) ProcessResponse(res *gomatrix.RespSync, since string) (er
|
|||||||
for _, event := range roomData.Timeline.Events {
|
for _, event := range roomData.Timeline.Events {
|
||||||
if event.StateKey != nil {
|
if event.StateKey != nil {
|
||||||
event.RoomID = roomID
|
event.RoomID = roomID
|
||||||
room.UpdateState(&event)
|
room.UpdateState(event)
|
||||||
s.notifyListeners(&event)
|
s.notifyListeners(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package main
|
package debug
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -22,54 +22,62 @@ import (
|
|||||||
"maunium.net/go/tview"
|
"maunium.net/go/tview"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DebugPaneHeight = 35
|
type Printer interface {
|
||||||
|
|
||||||
type DebugPrinter interface {
|
|
||||||
Printf(text string, args ...interface{})
|
Printf(text string, args ...interface{})
|
||||||
Print(text ...interface{})
|
Print(text ...interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
type DebugPane struct {
|
type Pane struct {
|
||||||
pane *tview.TextView
|
*tview.TextView
|
||||||
|
Height int
|
||||||
num int
|
num int
|
||||||
gmx Gomuks
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDebugPane(gmx Gomuks) *DebugPane {
|
var Default Printer
|
||||||
|
|
||||||
|
func NewPane() *Pane {
|
||||||
pane := tview.NewTextView()
|
pane := tview.NewTextView()
|
||||||
pane.
|
pane.
|
||||||
SetScrollable(true).
|
SetScrollable(true).
|
||||||
SetWrap(true)
|
SetWrap(true).
|
||||||
pane.SetChangedFunc(func() {
|
SetBorder(true).
|
||||||
gmx.App().Draw()
|
SetTitle("Debug output")
|
||||||
})
|
|
||||||
pane.SetBorder(true).SetTitle("Debug output")
|
|
||||||
fmt.Fprintln(pane, "[0] Debug pane initialized")
|
fmt.Fprintln(pane, "[0] Debug pane initialized")
|
||||||
|
|
||||||
return &DebugPane{
|
return &Pane{
|
||||||
pane: pane,
|
TextView: pane,
|
||||||
|
Height: 35,
|
||||||
num: 0,
|
num: 0,
|
||||||
gmx: gmx,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DebugPane) Printf(text string, args ...interface{}) {
|
func (db *Pane) Printf(text string, args ...interface{}) {
|
||||||
db.Write(fmt.Sprintf(text, args...) + "\n")
|
db.WriteString(fmt.Sprintf(text, args...) + "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DebugPane) Print(text ...interface{}) {
|
func (db *Pane) Print(text ...interface{}) {
|
||||||
db.Write(fmt.Sprintln(text...))
|
db.WriteString(fmt.Sprintln(text...))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DebugPane) Write(text string) {
|
func (db *Pane) WriteString(text string) {
|
||||||
if db.pane != nil {
|
db.num++
|
||||||
db.num++
|
fmt.Fprintf(db, "[%d] %s", db.num, text)
|
||||||
fmt.Fprintf(db.pane, "[%d] %s", db.num, text)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DebugPane) Wrap(main tview.Primitive) tview.Primitive {
|
func (db *Pane) Wrap(main tview.Primitive) tview.Primitive {
|
||||||
return tview.NewGrid().SetRows(0, DebugPaneHeight).SetColumns(0).
|
return tview.NewGrid().SetRows(0, db.Height).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, 1, 0, 1, 1, 1, 1, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Printf(text string, args ...interface{}) {
|
||||||
|
if Default != nil {
|
||||||
|
Default.Printf(text, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Print(text ...interface{}) {
|
||||||
|
if Default != nil {
|
||||||
|
Default.Print(text...)
|
||||||
|
}
|
||||||
}
|
}
|
85
ui/types/message.go
Normal file
85
ui/types/message.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
// 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 types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
|
"github.com/mattn/go-runewidth"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
ID string
|
||||||
|
Sender string
|
||||||
|
Text string
|
||||||
|
Timestamp string
|
||||||
|
Date string
|
||||||
|
|
||||||
|
Buffer []string
|
||||||
|
SenderColor tcell.Color
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMessage(id, sender, text, timestamp, date string, senderColor tcell.Color) *Message {
|
||||||
|
return &Message{
|
||||||
|
ID: id,
|
||||||
|
Sender: sender,
|
||||||
|
Text: text,
|
||||||
|
Timestamp: timestamp,
|
||||||
|
Date: date,
|
||||||
|
SenderColor: senderColor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
boundaryPattern = regexp.MustCompile("([[:punct:]]\\s*|\\s+)")
|
||||||
|
spacePattern = regexp.MustCompile(`\s+`)
|
||||||
|
)
|
||||||
|
|
||||||
|
func (message *Message) CalculateBuffer(width int) {
|
||||||
|
if width < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
message.Buffer = []string{}
|
||||||
|
forcedLinebreaks := strings.Split(message.Text, "\n")
|
||||||
|
newlines := 0
|
||||||
|
for _, str := range forcedLinebreaks {
|
||||||
|
if len(str) == 0 && newlines < 1 {
|
||||||
|
message.Buffer = append(message.Buffer, "")
|
||||||
|
newlines++
|
||||||
|
} else {
|
||||||
|
newlines = 0
|
||||||
|
}
|
||||||
|
// From tview/textview.go#reindexBuffer()
|
||||||
|
for len(str) > 0 {
|
||||||
|
extract := runewidth.Truncate(str, width, "")
|
||||||
|
if len(extract) < len(str) {
|
||||||
|
if spaces := spacePattern.FindStringIndex(str[len(extract):]); spaces != nil && spaces[0] == 0 {
|
||||||
|
extract = str[:len(extract)+spaces[1]]
|
||||||
|
}
|
||||||
|
|
||||||
|
matches := boundaryPattern.FindAllStringIndex(extract, -1)
|
||||||
|
if len(matches) > 0 {
|
||||||
|
extract = extract[:matches[len(matches)-1][1]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
message.Buffer = append(message.Buffer, extract)
|
||||||
|
str = str[len(extract):]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,25 +14,17 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package main
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
|
"maunium.net/go/gomuks/interface"
|
||||||
"maunium.net/go/tview"
|
"maunium.net/go/tview"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Allowed views in GomuksUI
|
|
||||||
const (
|
|
||||||
ViewLogin = "login"
|
|
||||||
ViewMain = "main"
|
|
||||||
)
|
|
||||||
|
|
||||||
type GomuksUI struct {
|
type GomuksUI struct {
|
||||||
gmx Gomuks
|
gmx ifc.Gomuks
|
||||||
app *tview.Application
|
app *tview.Application
|
||||||
matrix *MatrixContainer
|
|
||||||
debug DebugPrinter
|
|
||||||
config *Config
|
|
||||||
views *tview.Pages
|
views *tview.Pages
|
||||||
|
|
||||||
mainView *MainView
|
mainView *MainView
|
||||||
@ -44,13 +36,10 @@ func init() {
|
|||||||
tview.Styles.ContrastBackgroundColor = tcell.ColorDarkGreen
|
tview.Styles.ContrastBackgroundColor = tcell.ColorDarkGreen
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGomuksUI(gmx Gomuks) (ui *GomuksUI) {
|
func NewGomuksUI(gmx ifc.Gomuks) (ui *GomuksUI) {
|
||||||
ui = &GomuksUI{
|
ui = &GomuksUI{
|
||||||
gmx: gmx,
|
gmx: gmx,
|
||||||
app: gmx.App(),
|
app: gmx.App(),
|
||||||
matrix: gmx.MatrixContainer(),
|
|
||||||
debug: gmx.Debug(),
|
|
||||||
config: gmx.Config(),
|
|
||||||
views: tview.NewPages(),
|
views: tview.NewPages(),
|
||||||
}
|
}
|
||||||
ui.views.SetChangedFunc(ui.Render)
|
ui.views.SetChangedFunc(ui.Render)
|
||||||
@ -61,16 +50,16 @@ func (ui *GomuksUI) Render() {
|
|||||||
ui.app.Draw()
|
ui.app.Draw()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *GomuksUI) SetView(name string) {
|
func (ui *GomuksUI) SetView(name ifc.View) {
|
||||||
ui.views.SwitchToPage(name)
|
ui.views.SwitchToPage(string(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *GomuksUI) InitViews() tview.Primitive {
|
func (ui *GomuksUI) InitViews() tview.Primitive {
|
||||||
ui.views.AddPage(ViewLogin, ui.NewLoginView(), true, true)
|
ui.views.AddPage(string(ifc.ViewLogin), ui.NewLoginView(), true, true)
|
||||||
ui.views.AddPage(ViewMain, ui.NewMainView(), true, false)
|
ui.views.AddPage(string(ifc.ViewMain), ui.NewMainView(), true, false)
|
||||||
return ui.views
|
return ui.views
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *GomuksUI) MainView() *MainView {
|
func (ui *GomuksUI) MainView() ifc.MainView {
|
||||||
return ui.mainView
|
return ui.mainView
|
||||||
}
|
}
|
@ -14,14 +14,16 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package main
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"maunium.net/go/gomuks/ui/debug"
|
||||||
|
"maunium.net/go/gomuks/ui/widget"
|
||||||
"maunium.net/go/tview"
|
"maunium.net/go/tview"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (ui *GomuksUI) NewLoginView() tview.Primitive {
|
func (ui *GomuksUI) NewLoginView() tview.Primitive {
|
||||||
hs := ui.config.HS
|
hs := ui.gmx.Config().HS
|
||||||
if len(hs) == 0 {
|
if len(hs) == 0 {
|
||||||
hs = "https://matrix.org"
|
hs = "https://matrix.org"
|
||||||
}
|
}
|
||||||
@ -29,13 +31,13 @@ func (ui *GomuksUI) NewLoginView() tview.Primitive {
|
|||||||
ui.loginView = tview.NewForm()
|
ui.loginView = tview.NewForm()
|
||||||
ui.loginView.
|
ui.loginView.
|
||||||
AddInputField("Homeserver", hs, 30, nil, nil).
|
AddInputField("Homeserver", hs, 30, nil, nil).
|
||||||
AddInputField("Username", ui.config.MXID, 30, nil, nil).
|
AddInputField("Username", ui.gmx.Config().MXID, 30, nil, nil).
|
||||||
AddPasswordField("Password", "", 30, '*', nil).
|
AddPasswordField("Password", "", 30, '*', nil).
|
||||||
AddButton("Log in", ui.login).
|
AddButton("Log in", ui.login).
|
||||||
AddButton("Quit", ui.gmx.Stop).
|
AddButton("Quit", ui.gmx.Stop).
|
||||||
SetButtonsAlign(tview.AlignCenter).
|
SetButtonsAlign(tview.AlignCenter).
|
||||||
SetBorder(true).SetTitle("Log in to Matrix")
|
SetBorder(true).SetTitle("Log in to Matrix")
|
||||||
return Center(45, 11, ui.loginView)
|
return widget.Center(45, 11, ui.loginView)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *GomuksUI) login() {
|
func (ui *GomuksUI) login() {
|
||||||
@ -43,8 +45,9 @@ func (ui *GomuksUI) login() {
|
|||||||
mxid := ui.loginView.GetFormItem(1).(*tview.InputField).GetText()
|
mxid := ui.loginView.GetFormItem(1).(*tview.InputField).GetText()
|
||||||
password := ui.loginView.GetFormItem(2).(*tview.InputField).GetText()
|
password := ui.loginView.GetFormItem(2).(*tview.InputField).GetText()
|
||||||
|
|
||||||
ui.debug.Printf("Logging into %s as %s...", hs, mxid)
|
debug.Printf("Logging into %s as %s...", hs, mxid)
|
||||||
ui.config.HS = hs
|
ui.gmx.Config().HS = hs
|
||||||
ui.debug.Print("Connect result:", ui.matrix.InitClient())
|
mx := ui.gmx.MatrixContainer()
|
||||||
ui.debug.Print("Login result:", ui.matrix.Login(mxid, password))
|
debug.Print("Connect result:", mx.InitClient())
|
||||||
|
debug.Print("Login result:", mx.Login(mxid, password))
|
||||||
}
|
}
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package main
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -26,6 +26,11 @@ import (
|
|||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
"github.com/mattn/go-runewidth"
|
"github.com/mattn/go-runewidth"
|
||||||
"maunium.net/go/gomatrix"
|
"maunium.net/go/gomatrix"
|
||||||
|
"maunium.net/go/gomuks/config"
|
||||||
|
"maunium.net/go/gomuks/interface"
|
||||||
|
"maunium.net/go/gomuks/ui/debug"
|
||||||
|
"maunium.net/go/gomuks/ui/types"
|
||||||
|
"maunium.net/go/gomuks/ui/widget"
|
||||||
"maunium.net/go/tview"
|
"maunium.net/go/tview"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -34,15 +39,14 @@ type MainView struct {
|
|||||||
|
|
||||||
roomList *tview.List
|
roomList *tview.List
|
||||||
roomView *tview.Pages
|
roomView *tview.Pages
|
||||||
rooms map[string]*RoomView
|
rooms map[string]*widget.RoomView
|
||||||
input *AdvancedInputField
|
input *widget.AdvancedInputField
|
||||||
currentRoomIndex int
|
currentRoomIndex int
|
||||||
roomIDs []string
|
roomIDs []string
|
||||||
|
|
||||||
matrix *MatrixContainer
|
matrix ifc.MatrixContainer
|
||||||
debug DebugPrinter
|
gmx ifc.Gomuks
|
||||||
gmx Gomuks
|
config *config.Config
|
||||||
config *Config
|
|
||||||
parent *GomuksUI
|
parent *GomuksUI
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,13 +59,12 @@ func (ui *GomuksUI) NewMainView() tview.Primitive {
|
|||||||
Grid: tview.NewGrid(),
|
Grid: tview.NewGrid(),
|
||||||
roomList: tview.NewList(),
|
roomList: tview.NewList(),
|
||||||
roomView: tview.NewPages(),
|
roomView: tview.NewPages(),
|
||||||
rooms: make(map[string]*RoomView),
|
rooms: make(map[string]*widget.RoomView),
|
||||||
input: NewAdvancedInputField(),
|
input: widget.NewAdvancedInputField(),
|
||||||
|
|
||||||
matrix: ui.matrix,
|
matrix: ui.gmx.MatrixContainer(),
|
||||||
debug: ui.debug,
|
|
||||||
gmx: ui.gmx,
|
gmx: ui.gmx,
|
||||||
config: ui.config,
|
config: ui.gmx.Config(),
|
||||||
parent: ui,
|
parent: ui,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +86,7 @@ func (ui *GomuksUI) NewMainView() tview.Primitive {
|
|||||||
SetInputCapture(mainView.InputCapture)
|
SetInputCapture(mainView.InputCapture)
|
||||||
|
|
||||||
mainView.addItem(mainView.roomList, 0, 0, 2, 1)
|
mainView.addItem(mainView.roomList, 0, 0, 2, 1)
|
||||||
mainView.addItem(NewBorder(), 0, 1, 2, 1)
|
mainView.addItem(widget.NewBorder(), 0, 1, 2, 1)
|
||||||
mainView.addItem(mainView.roomView, 0, 2, 1, 1)
|
mainView.addItem(mainView.roomView, 0, 2, 1, 1)
|
||||||
mainView.AddItem(mainView.input, 1, 2, 1, 1, 0, 0, true)
|
mainView.AddItem(mainView.input, 1, 2, 1, 1, 0, 0, true)
|
||||||
|
|
||||||
@ -121,8 +124,7 @@ func (view *MainView) InputTabComplete(text string, cursorOffset int) string {
|
|||||||
if len(userCompletions) == 1 {
|
if len(userCompletions) == 1 {
|
||||||
text = str[0:len(str)-len(word)] + userCompletions[0] + text[len(str):]
|
text = str[0:len(str)-len(word)] + userCompletions[0] + text[len(str):]
|
||||||
} else if len(userCompletions) > 1 && len(userCompletions) < 6 {
|
} else if len(userCompletions) > 1 && len(userCompletions) < 6 {
|
||||||
roomView.status.Clear()
|
roomView.SetStatus(fmt.Sprintf("Completions: %s", strings.Join(userCompletions, ", ")))
|
||||||
fmt.Fprintf(roomView.status, "Completions: %s", strings.Join(userCompletions, ", "))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return text
|
return text
|
||||||
@ -147,7 +149,7 @@ func (view *MainView) InputDone(key tcell.Key) {
|
|||||||
|
|
||||||
func (view *MainView) HandleCommand(room, command string, args []string) {
|
func (view *MainView) HandleCommand(room, command string, args []string) {
|
||||||
view.gmx.Recover()
|
view.gmx.Recover()
|
||||||
view.debug.Print("Handling command", command, args)
|
debug.Print("Handling command", command, args)
|
||||||
switch command {
|
switch command {
|
||||||
case "/quit":
|
case "/quit":
|
||||||
view.gmx.Stop()
|
view.gmx.Stop()
|
||||||
@ -157,13 +159,13 @@ func (view *MainView) HandleCommand(room, command string, args []string) {
|
|||||||
case "/part":
|
case "/part":
|
||||||
fallthrough
|
fallthrough
|
||||||
case "/leave":
|
case "/leave":
|
||||||
view.matrix.client.LeaveRoom(room)
|
debug.Print(view.matrix.LeaveRoom(room))
|
||||||
case "/join":
|
case "/join":
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
view.AddServiceMessage(room, "Usage: /join <room>")
|
view.AddServiceMessage(room, "Usage: /join <room>")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
view.debug.Print(view.matrix.JoinRoom(args[0]))
|
debug.Print(view.matrix.JoinRoom(args[0]))
|
||||||
default:
|
default:
|
||||||
view.AddServiceMessage(room, "Unknown command.")
|
view.AddServiceMessage(room, "Unknown command.")
|
||||||
}
|
}
|
||||||
@ -218,7 +220,7 @@ func (view *MainView) addRoom(index int, room string) {
|
|||||||
view.SwitchRoom(index)
|
view.SwitchRoom(index)
|
||||||
})
|
})
|
||||||
if !view.roomView.HasPage(room) {
|
if !view.roomView.HasPage(room) {
|
||||||
roomView := NewRoomView(view, roomStore)
|
roomView := widget.NewRoomView(view, roomStore)
|
||||||
view.rooms[room] = roomView
|
view.rooms[room] = roomView
|
||||||
view.roomView.AddPage(room, roomView, true, false)
|
view.roomView.AddPage(room, roomView, true, false)
|
||||||
roomView.UpdateUserList()
|
roomView.UpdateUserList()
|
||||||
@ -226,7 +228,7 @@ func (view *MainView) addRoom(index int, room string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *MainView) GetRoom(id string) *RoomView {
|
func (view *MainView) GetRoom(id string) *widget.RoomView {
|
||||||
return view.rooms[id]
|
return view.rooms[id]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,11 +267,11 @@ func (view *MainView) RemoveRoom(room string) {
|
|||||||
view.Render()
|
view.Render()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *MainView) SetRoomList(rooms []string) {
|
func (view *MainView) SetRooms(rooms []string) {
|
||||||
view.roomIDs = rooms
|
view.roomIDs = rooms
|
||||||
view.roomList.Clear()
|
view.roomList.Clear()
|
||||||
view.roomView.Clear()
|
view.roomView.Clear()
|
||||||
view.rooms = make(map[string]*RoomView)
|
view.rooms = make(map[string]*widget.RoomView)
|
||||||
for index, room := range rooms {
|
for index, room := range rooms {
|
||||||
view.addRoom(index, room)
|
view.addRoom(index, room)
|
||||||
}
|
}
|
||||||
@ -289,7 +291,7 @@ func (view *MainView) AddServiceMessage(room, message string) {
|
|||||||
if ok {
|
if ok {
|
||||||
messageView := roomView.MessageView()
|
messageView := roomView.MessageView()
|
||||||
message := messageView.NewMessage("", "*", message, time.Now())
|
message := messageView.NewMessage("", "*", message, time.Now())
|
||||||
messageView.AddMessage(message, AppendMessage)
|
messageView.AddMessage(message, widget.AppendMessage)
|
||||||
view.parent.Render()
|
view.parent.Render()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -300,29 +302,29 @@ func (view *MainView) Render() {
|
|||||||
|
|
||||||
func (view *MainView) GetHistory(room string) {
|
func (view *MainView) GetHistory(room string) {
|
||||||
roomView := view.rooms[room]
|
roomView := view.rooms[room]
|
||||||
history, _, err := view.matrix.GetHistory(roomView.room.ID, view.config.Session.NextBatch, 50)
|
history, _, err := view.matrix.GetHistory(roomView.Room.ID, view.config.Session.NextBatch, 50)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
view.debug.Print("Failed to fetch history for", roomView.room.ID, err)
|
debug.Print("Failed to fetch history for", roomView.Room.ID, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, evt := range history {
|
for _, evt := range history {
|
||||||
var room *RoomView
|
var room *widget.RoomView
|
||||||
var message *Message
|
var message *types.Message
|
||||||
if evt.Type == "m.room.message" {
|
if evt.Type == "m.room.message" {
|
||||||
room, message = view.ProcessMessageEvent(&evt)
|
room, message = view.ProcessMessageEvent(&evt)
|
||||||
} else if evt.Type == "m.room.member" {
|
} else if evt.Type == "m.room.member" {
|
||||||
room, message = view.ProcessMembershipEvent(&evt, false)
|
room, message = view.ProcessMembershipEvent(&evt, false)
|
||||||
}
|
}
|
||||||
if room != nil && message != nil {
|
if room != nil && message != nil {
|
||||||
room.AddMessage(message, PrependMessage)
|
room.AddMessage(message, widget.PrependMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *MainView) ProcessMessageEvent(evt *gomatrix.Event) (room *RoomView, message *Message) {
|
func (view *MainView) ProcessMessageEvent(evt *gomatrix.Event) (room *widget.RoomView, message *types.Message) {
|
||||||
room = view.GetRoom(evt.RoomID)
|
room = view.GetRoom(evt.RoomID)
|
||||||
if room != nil {
|
if room != nil {
|
||||||
text := evt.Content["body"].(string)
|
text, _ := evt.Content["body"].(string)
|
||||||
message = room.NewMessage(evt.ID, evt.Sender, text, unixToTime(evt.Timestamp))
|
message = room.NewMessage(evt.ID, evt.Sender, text, unixToTime(evt.Timestamp))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -344,7 +346,7 @@ func (view *MainView) processOwnMembershipChange(evt *gomatrix.Event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *MainView) ProcessMembershipEvent(evt *gomatrix.Event, new bool) (room *RoomView, message *Message) {
|
func (view *MainView) ProcessMembershipEvent(evt *gomatrix.Event, new bool) (room *widget.RoomView, message *types.Message) {
|
||||||
if new && evt.StateKey != nil && *evt.StateKey == view.config.Session.MXID {
|
if new && evt.StateKey != nil && *evt.StateKey == view.config.Session.MXID {
|
||||||
view.processOwnMembershipChange(evt)
|
view.processOwnMembershipChange(evt)
|
||||||
}
|
}
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package main
|
package widget
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
@ -14,7 +14,7 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package main
|
package widget
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
32
ui/widget/center.go
Normal file
32
ui/widget/center.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// 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 widget
|
||||||
|
|
||||||
|
import (
|
||||||
|
"maunium.net/go/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)
|
||||||
|
}
|
60
ui/widget/color.go
Normal file
60
ui/widget/color.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// 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 widget
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"hash/fnv"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
|
)
|
||||||
|
|
||||||
|
var colorNames []string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
colorNames = make([]string, len(tcell.ColorNames))
|
||||||
|
i := 0
|
||||||
|
for name, _ := range tcell.ColorNames {
|
||||||
|
colorNames[i] = name
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
sort.Sort(sort.StringSlice(colorNames))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHashColorName(s string) string {
|
||||||
|
switch s {
|
||||||
|
case "-->":
|
||||||
|
return "green"
|
||||||
|
case "<--":
|
||||||
|
return "red"
|
||||||
|
case "---":
|
||||||
|
return "yellow"
|
||||||
|
default:
|
||||||
|
h := fnv.New32a()
|
||||||
|
h.Write([]byte(s))
|
||||||
|
return colorNames[int(h.Sum32())%len(colorNames)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHashColor(s string) tcell.Color {
|
||||||
|
return tcell.ColorNames[GetHashColorName(s)]
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddHashColor(s string) string {
|
||||||
|
return fmt.Sprintf("[%s]%s[white]", GetHashColorName(s), s)
|
||||||
|
}
|
@ -14,24 +14,13 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package main
|
package widget
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
"maunium.net/go/tview"
|
"maunium.net/go/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 {
|
type FormTextView struct {
|
||||||
*tview.TextView
|
*tview.TextView
|
||||||
}
|
}
|
@ -14,79 +14,19 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package main
|
package widget
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
"github.com/mattn/go-runewidth"
|
"github.com/mattn/go-runewidth"
|
||||||
|
"maunium.net/go/gomuks/ui/types"
|
||||||
"maunium.net/go/tview"
|
"maunium.net/go/tview"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Message struct {
|
|
||||||
ID string
|
|
||||||
Sender string
|
|
||||||
Text string
|
|
||||||
Timestamp string
|
|
||||||
Date string
|
|
||||||
|
|
||||||
buffer []string
|
|
||||||
senderColor tcell.Color
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMessage(id, sender, text, timestamp, date string, senderColor tcell.Color) *Message {
|
|
||||||
return &Message{
|
|
||||||
ID: id,
|
|
||||||
Sender: sender,
|
|
||||||
Text: text,
|
|
||||||
Timestamp: timestamp,
|
|
||||||
Date: date,
|
|
||||||
senderColor: senderColor,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
boundaryPattern = regexp.MustCompile("([[:punct:]]\\s*|\\s+)")
|
|
||||||
spacePattern = regexp.MustCompile(`\s+`)
|
|
||||||
)
|
|
||||||
|
|
||||||
func (message *Message) calculateBuffer(width int) {
|
|
||||||
if width < 1 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
message.buffer = []string{}
|
|
||||||
forcedLinebreaks := strings.Split(message.Text, "\n")
|
|
||||||
newlines := 0
|
|
||||||
for _, str := range forcedLinebreaks {
|
|
||||||
if len(str) == 0 && newlines < 1 {
|
|
||||||
message.buffer = append(message.buffer, "")
|
|
||||||
newlines++
|
|
||||||
} else {
|
|
||||||
newlines = 0
|
|
||||||
}
|
|
||||||
// From tview/textview.go#reindexBuffer()
|
|
||||||
for len(str) > 0 {
|
|
||||||
extract := runewidth.Truncate(str, width, "")
|
|
||||||
if len(extract) < len(str) {
|
|
||||||
if spaces := spacePattern.FindStringIndex(str[len(extract):]); spaces != nil && spaces[0] == 0 {
|
|
||||||
extract = str[:len(extract)+spaces[1]]
|
|
||||||
}
|
|
||||||
|
|
||||||
matches := boundaryPattern.FindAllStringIndex(extract, -1)
|
|
||||||
if len(matches) > 0 {
|
|
||||||
extract = extract[:matches[len(matches)-1][1]]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
message.buffer = append(message.buffer, extract)
|
|
||||||
str = str[len(extract):]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type MessageView struct {
|
type MessageView struct {
|
||||||
*tview.Box
|
*tview.Box
|
||||||
|
|
||||||
@ -106,7 +46,7 @@ type MessageView struct {
|
|||||||
totalHeight int
|
totalHeight int
|
||||||
|
|
||||||
messageIDs map[string]bool
|
messageIDs map[string]bool
|
||||||
messages []*Message
|
messages []*types.Message
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMessageView() *MessageView {
|
func NewMessageView() *MessageView {
|
||||||
@ -119,7 +59,7 @@ func NewMessageView() *MessageView {
|
|||||||
Separator: '|',
|
Separator: '|',
|
||||||
ScrollOffset: 0,
|
ScrollOffset: 0,
|
||||||
|
|
||||||
messages: make([]*Message, 0),
|
messages: make([]*types.Message, 0),
|
||||||
messageIDs: make(map[string]bool),
|
messageIDs: make(map[string]bool),
|
||||||
|
|
||||||
widestSender: 5,
|
widestSender: 5,
|
||||||
@ -132,11 +72,11 @@ func NewMessageView() *MessageView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *MessageView) NewMessage(id, sender, text string, timestamp time.Time) *Message {
|
func (view *MessageView) NewMessage(id, sender, text string, timestamp time.Time) *types.Message {
|
||||||
return NewMessage(id, sender, text,
|
return types.NewMessage(id, sender, text,
|
||||||
timestamp.Format(view.TimestampFormat),
|
timestamp.Format(view.TimestampFormat),
|
||||||
timestamp.Format(view.DateFormat),
|
timestamp.Format(view.DateFormat),
|
||||||
getColor(sender))
|
GetHashColor(sender))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *MessageView) recalculateBuffers() {
|
func (view *MessageView) recalculateBuffers() {
|
||||||
@ -144,7 +84,7 @@ func (view *MessageView) recalculateBuffers() {
|
|||||||
width -= view.TimestampWidth + TimestampSenderGap + view.widestSender + SenderMessageGap
|
width -= view.TimestampWidth + TimestampSenderGap + view.widestSender + SenderMessageGap
|
||||||
if width != view.prevWidth {
|
if width != view.prevWidth {
|
||||||
for _, message := range view.messages {
|
for _, message := range view.messages {
|
||||||
message.calculateBuffer(width)
|
message.CalculateBuffer(width)
|
||||||
}
|
}
|
||||||
view.prevWidth = width
|
view.prevWidth = width
|
||||||
}
|
}
|
||||||
@ -164,7 +104,7 @@ const (
|
|||||||
PrependMessage
|
PrependMessage
|
||||||
)
|
)
|
||||||
|
|
||||||
func (view *MessageView) AddMessage(message *Message, direction int) {
|
func (view *MessageView) AddMessage(message *types.Message, direction int) {
|
||||||
_, messageExists := view.messageIDs[message.ID]
|
_, messageExists := view.messageIDs[message.ID]
|
||||||
if messageExists {
|
if messageExists {
|
||||||
return
|
return
|
||||||
@ -174,15 +114,15 @@ func (view *MessageView) AddMessage(message *Message, direction int) {
|
|||||||
|
|
||||||
_, _, width, _ := view.GetInnerRect()
|
_, _, width, _ := view.GetInnerRect()
|
||||||
width -= view.TimestampWidth + TimestampSenderGap + view.widestSender + SenderMessageGap
|
width -= view.TimestampWidth + TimestampSenderGap + view.widestSender + SenderMessageGap
|
||||||
message.calculateBuffer(width)
|
message.CalculateBuffer(width)
|
||||||
|
|
||||||
if direction == AppendMessage {
|
if direction == AppendMessage {
|
||||||
if view.ScrollOffset > 0 {
|
if view.ScrollOffset > 0 {
|
||||||
view.ScrollOffset += len(message.buffer)
|
view.ScrollOffset += len(message.Buffer)
|
||||||
}
|
}
|
||||||
view.messages = append(view.messages, message)
|
view.messages = append(view.messages, message)
|
||||||
} else if direction == PrependMessage {
|
} else if direction == PrependMessage {
|
||||||
view.messages = append([]*Message{message}, view.messages...)
|
view.messages = append([]*types.Message{message}, view.messages...)
|
||||||
}
|
}
|
||||||
|
|
||||||
view.messageIDs[message.ID] = true
|
view.messageIDs[message.ID] = true
|
||||||
@ -199,7 +139,7 @@ func (view *MessageView) recalculateHeight() {
|
|||||||
for i := len(view.messages) - 1; i >= 0; i-- {
|
for i := len(view.messages) - 1; i >= 0; i-- {
|
||||||
prevTotalHeight := view.totalHeight
|
prevTotalHeight := view.totalHeight
|
||||||
message := view.messages[i]
|
message := view.messages[i]
|
||||||
view.totalHeight += len(message.buffer)
|
view.totalHeight += len(message.Buffer)
|
||||||
if message.Date != prevDate {
|
if message.Date != prevDate {
|
||||||
if len(prevDate) != 0 {
|
if len(prevDate) != 0 {
|
||||||
view.totalHeight++
|
view.totalHeight++
|
||||||
@ -268,6 +208,9 @@ func (view *MessageView) writeLineRight(screen tcell.Screen, line string, x, y,
|
|||||||
screen.SetContent(x+offsetX+localOffset, y, ch, nil, tcell.StyleDefault.Foreground(color))
|
screen.SetContent(x+offsetX+localOffset, y, ch, nil, tcell.StyleDefault.Foreground(color))
|
||||||
}
|
}
|
||||||
offsetX += chWidth
|
offsetX += chWidth
|
||||||
|
if offsetX > maxWidth {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,7 +245,7 @@ func (view *MessageView) Draw(screen tcell.Screen) {
|
|||||||
prevSenderLine := -1
|
prevSenderLine := -1
|
||||||
for i := view.firstDisplayMessage; i >= view.lastDisplayMessage; i-- {
|
for i := view.firstDisplayMessage; i >= view.lastDisplayMessage; i-- {
|
||||||
message := view.messages[i]
|
message := view.messages[i]
|
||||||
messageHeight := len(message.buffer)
|
messageHeight := len(message.Buffer)
|
||||||
|
|
||||||
// Show message when the date changes.
|
// Show message when the date changes.
|
||||||
if message.Date != prevDate {
|
if message.Date != prevDate {
|
||||||
@ -325,7 +268,7 @@ func (view *MessageView) Draw(screen tcell.Screen) {
|
|||||||
view.writeLine(screen, message.Timestamp, x, senderAtLine, tcell.ColorDefault)
|
view.writeLine(screen, message.Timestamp, x, senderAtLine, tcell.ColorDefault)
|
||||||
view.writeLineRight(screen, message.Sender,
|
view.writeLineRight(screen, message.Sender,
|
||||||
x+usernameOffsetX, senderAtLine,
|
x+usernameOffsetX, senderAtLine,
|
||||||
view.widestSender, message.senderColor)
|
view.widestSender, message.SenderColor)
|
||||||
|
|
||||||
if message.Sender == prevSender {
|
if message.Sender == prevSender {
|
||||||
// Sender is same as previous. We're looping from bottom to top, and we want the
|
// Sender is same as previous. We're looping from bottom to top, and we want the
|
||||||
@ -333,12 +276,12 @@ func (view *MessageView) Draw(screen tcell.Screen) {
|
|||||||
// below.
|
// below.
|
||||||
view.writeLineRight(screen, strings.Repeat(" ", view.widestSender),
|
view.writeLineRight(screen, strings.Repeat(" ", view.widestSender),
|
||||||
x+usernameOffsetX, prevSenderLine,
|
x+usernameOffsetX, prevSenderLine,
|
||||||
view.widestSender, message.senderColor)
|
view.widestSender, message.SenderColor)
|
||||||
}
|
}
|
||||||
prevSender = message.Sender
|
prevSender = message.Sender
|
||||||
prevSenderLine = senderAtLine
|
prevSenderLine = senderAtLine
|
||||||
|
|
||||||
for num, line := range message.buffer {
|
for num, line := range message.Buffer {
|
||||||
offsetY := height - messageHeight - writeOffset + num
|
offsetY := height - messageHeight - writeOffset + num
|
||||||
// Only render message if it's within the message view.
|
// Only render message if it's within the message view.
|
||||||
if offsetY >= 0 {
|
if offsetY >= 0 {
|
@ -14,19 +14,23 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package main
|
package widget
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash/fnv"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
|
rooms "maunium.net/go/gomuks/matrix/room"
|
||||||
|
"maunium.net/go/gomuks/ui/types"
|
||||||
"maunium.net/go/tview"
|
"maunium.net/go/tview"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Renderable interface {
|
||||||
|
Render()
|
||||||
|
}
|
||||||
|
|
||||||
type RoomView struct {
|
type RoomView struct {
|
||||||
*tview.Box
|
*tview.Box
|
||||||
|
|
||||||
@ -34,31 +38,19 @@ type RoomView struct {
|
|||||||
content *MessageView
|
content *MessageView
|
||||||
status *tview.TextView
|
status *tview.TextView
|
||||||
userList *tview.TextView
|
userList *tview.TextView
|
||||||
room *Room
|
Room *rooms.Room
|
||||||
|
|
||||||
parent *MainView
|
parent Renderable
|
||||||
}
|
}
|
||||||
|
|
||||||
var colorNames []string
|
func NewRoomView(parent Renderable, room *rooms.Room) *RoomView {
|
||||||
|
|
||||||
func init() {
|
|
||||||
colorNames = make([]string, len(tcell.ColorNames))
|
|
||||||
i := 0
|
|
||||||
for name, _ := range tcell.ColorNames {
|
|
||||||
colorNames[i] = name
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
sort.Sort(sort.StringSlice(colorNames))
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRoomView(parent *MainView, room *Room) *RoomView {
|
|
||||||
view := &RoomView{
|
view := &RoomView{
|
||||||
Box: tview.NewBox(),
|
Box: tview.NewBox(),
|
||||||
topic: tview.NewTextView(),
|
topic: tview.NewTextView(),
|
||||||
content: NewMessageView(),
|
content: NewMessageView(),
|
||||||
status: tview.NewTextView(),
|
status: tview.NewTextView(),
|
||||||
userList: tview.NewTextView(),
|
userList: tview.NewTextView(),
|
||||||
room: room,
|
Room: room,
|
||||||
parent: parent,
|
parent: parent,
|
||||||
}
|
}
|
||||||
view.topic.
|
view.topic.
|
||||||
@ -90,9 +82,13 @@ func (view *RoomView) Draw(screen tcell.Screen) {
|
|||||||
view.userList.Draw(screen)
|
view.userList.Draw(screen)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (view *RoomView) SetStatus(status string) {
|
||||||
|
view.status.SetText(status)
|
||||||
|
}
|
||||||
|
|
||||||
func (view *RoomView) SetTyping(users []string) {
|
func (view *RoomView) SetTyping(users []string) {
|
||||||
for index, user := range users {
|
for index, user := range users {
|
||||||
member := view.room.GetMember(user)
|
member := view.Room.GetMember(user)
|
||||||
if member != nil {
|
if member != nil {
|
||||||
users[index] = member.DisplayName
|
users[index] = member.DisplayName
|
||||||
}
|
}
|
||||||
@ -109,7 +105,7 @@ func (view *RoomView) SetTyping(users []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (view *RoomView) AutocompleteUser(existingText string) (completions []string) {
|
func (view *RoomView) AutocompleteUser(existingText string) (completions []string) {
|
||||||
for _, user := range view.room.GetMembers() {
|
for _, user := range view.Room.GetMembers() {
|
||||||
if strings.HasPrefix(user.DisplayName, existingText) {
|
if strings.HasPrefix(user.DisplayName, existingText) {
|
||||||
completions = append(completions, user.DisplayName)
|
completions = append(completions, user.DisplayName)
|
||||||
} else if strings.HasPrefix(user.UserID, existingText) {
|
} else if strings.HasPrefix(user.UserID, existingText) {
|
||||||
@ -123,38 +119,15 @@ func (view *RoomView) MessageView() *MessageView {
|
|||||||
return view.content
|
return view.content
|
||||||
}
|
}
|
||||||
|
|
||||||
func getColorName(s string) string {
|
|
||||||
switch s {
|
|
||||||
case "-->":
|
|
||||||
return "green"
|
|
||||||
case "<--":
|
|
||||||
return "red"
|
|
||||||
case "---":
|
|
||||||
return "yellow"
|
|
||||||
default:
|
|
||||||
h := fnv.New32a()
|
|
||||||
h.Write([]byte(s))
|
|
||||||
return colorNames[int(h.Sum32())%len(colorNames)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getColor(s string) tcell.Color {
|
|
||||||
return tcell.ColorNames[getColorName(s)]
|
|
||||||
}
|
|
||||||
|
|
||||||
func color(s string) string {
|
|
||||||
return fmt.Sprintf("[%s]%s[white]", getColorName(s), s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (view *RoomView) UpdateUserList() {
|
func (view *RoomView) UpdateUserList() {
|
||||||
var joined strings.Builder
|
var joined strings.Builder
|
||||||
var invited strings.Builder
|
var invited strings.Builder
|
||||||
for _, user := range view.room.GetMembers() {
|
for _, user := range view.Room.GetMembers() {
|
||||||
if user.Membership == "join" {
|
if user.Membership == "join" {
|
||||||
joined.WriteString(color(user.DisplayName))
|
joined.WriteString(AddHashColor(user.DisplayName))
|
||||||
joined.WriteRune('\n')
|
joined.WriteRune('\n')
|
||||||
} else if user.Membership == "invite" {
|
} else if user.Membership == "invite" {
|
||||||
invited.WriteString(color(user.DisplayName))
|
invited.WriteString(AddHashColor(user.DisplayName))
|
||||||
invited.WriteRune('\n')
|
invited.WriteRune('\n')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,15 +138,15 @@ func (view *RoomView) UpdateUserList() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *RoomView) NewMessage(id, sender, text string, timestamp time.Time) *Message {
|
func (view *RoomView) NewMessage(id, sender, text string, timestamp time.Time) *types.Message {
|
||||||
member := view.room.GetMember(sender)
|
member := view.Room.GetMember(sender)
|
||||||
if member != nil {
|
if member != nil {
|
||||||
sender = member.DisplayName
|
sender = member.DisplayName
|
||||||
}
|
}
|
||||||
return view.content.NewMessage(id, sender, text, timestamp)
|
return view.content.NewMessage(id, sender, text, timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *RoomView) AddMessage(message *Message, direction int) {
|
func (view *RoomView) AddMessage(message *types.Message, direction int) {
|
||||||
view.content.AddMessage(message, direction)
|
view.content.AddMessage(message, direction)
|
||||||
view.parent.Render()
|
view.parent.Render()
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user