Save history to disk. Fixes #1
This commit is contained in:
parent
232f7fe1be
commit
702a75a8c0
@ -30,19 +30,29 @@ type Config struct {
|
|||||||
UserID string `yaml:"mxid"`
|
UserID string `yaml:"mxid"`
|
||||||
HS string `yaml:"homeserver"`
|
HS string `yaml:"homeserver"`
|
||||||
|
|
||||||
dir string `yaml:"-"`
|
Dir string `yaml:"-"`
|
||||||
Session *Session `yaml:"-"`
|
HistoryDir string `yaml:"history_dir"`
|
||||||
|
Session *Session `yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfig(dir string) *Config {
|
func NewConfig(dir string) *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
dir: dir,
|
Dir: dir,
|
||||||
|
HistoryDir: filepath.Join(dir, "history"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (config *Config) Clear() {
|
||||||
|
if config.Session != nil {
|
||||||
|
config.Session.Clear()
|
||||||
|
}
|
||||||
|
os.RemoveAll(config.HistoryDir)
|
||||||
|
}
|
||||||
|
|
||||||
func (config *Config) Load() {
|
func (config *Config) Load() {
|
||||||
os.MkdirAll(config.dir, 0700)
|
os.MkdirAll(config.Dir, 0700)
|
||||||
configPath := filepath.Join(config.dir, "config.yaml")
|
os.MkdirAll(config.HistoryDir, 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) {
|
||||||
@ -61,14 +71,14 @@ func (config *Config) Load() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (config *Config) Save() {
|
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 {
|
||||||
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 {
|
||||||
debug.Print("Failed to write config to", path)
|
debug.Print("Failed to write config to", path)
|
||||||
|
@ -23,7 +23,7 @@ import (
|
|||||||
|
|
||||||
"maunium.net/go/gomatrix"
|
"maunium.net/go/gomatrix"
|
||||||
"maunium.net/go/gomuks/matrix/pushrules"
|
"maunium.net/go/gomuks/matrix/pushrules"
|
||||||
rooms "maunium.net/go/gomuks/matrix/room"
|
"maunium.net/go/gomuks/matrix/room"
|
||||||
"maunium.net/go/gomuks/ui/debug"
|
"maunium.net/go/gomuks/ui/debug"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ func (config *Config) LoadSession(mxid string) error {
|
|||||||
func (config *Config) NewSession(mxid string) *Session {
|
func (config *Config) NewSession(mxid string) *Session {
|
||||||
return &Session{
|
return &Session{
|
||||||
UserID: mxid,
|
UserID: mxid,
|
||||||
path: filepath.Join(config.dir, mxid+".session"),
|
path: filepath.Join(config.Dir, mxid+".session"),
|
||||||
Rooms: make(map[string]*rooms.Room),
|
Rooms: make(map[string]*rooms.Room),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,13 +61,13 @@ func (s *Session) Clear() {
|
|||||||
func (s *Session) Load() error {
|
func (s *Session) Load() error {
|
||||||
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, err)
|
debug.Printf("Failed to read session from %s: %v", s.path, err)
|
||||||
return err
|
return 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, err)
|
debug.Printf("Failed to parse session at %s: %v", s.path, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -76,13 +76,13 @@ func (s *Session) Load() error {
|
|||||||
func (s *Session) Save() error {
|
func (s *Session) Save() error {
|
||||||
data, err := json.Marshal(s)
|
data, err := json.Marshal(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Print("Failed to marshal session of", s.UserID, err)
|
debug.Printf("Failed to marshal session of %s: %v", s.UserID, err)
|
||||||
return err
|
return 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, err)
|
debug.Printf("Failed to write session of %s to %s: %v", s.UserID, s.path, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
29
gomuks.go
29
gomuks.go
@ -38,12 +38,14 @@ type Gomuks struct {
|
|||||||
debug *debug.Pane
|
debug *debug.Pane
|
||||||
debugMode bool
|
debugMode bool
|
||||||
config *config.Config
|
config *config.Config
|
||||||
|
stop chan bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGomuks(enableDebug bool) *Gomuks {
|
func NewGomuks(enableDebug 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(),
|
||||||
|
stop: make(chan bool, 1),
|
||||||
}
|
}
|
||||||
|
|
||||||
gmx.debug = debug.NewPane()
|
gmx.debug = debug.NewPane()
|
||||||
@ -79,11 +81,33 @@ func (gmx *Gomuks) Stop() {
|
|||||||
gmx.matrix.Stop()
|
gmx.matrix.Stop()
|
||||||
gmx.debug.Print("Cleaning up UI...")
|
gmx.debug.Print("Cleaning up UI...")
|
||||||
gmx.app.Stop()
|
gmx.app.Stop()
|
||||||
|
gmx.stop <- true
|
||||||
|
gmx.Save()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gmx *Gomuks) Save() {
|
||||||
if gmx.config.Session != nil {
|
if gmx.config.Session != nil {
|
||||||
gmx.debug.Print("Saving session...")
|
gmx.debug.Print("Saving session...")
|
||||||
gmx.config.Session.Save()
|
gmx.config.Session.Save()
|
||||||
}
|
}
|
||||||
os.Exit(0)
|
gmx.debug.Print("Saving history...")
|
||||||
|
gmx.ui.MainView().SaveAllHistory()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gmx *Gomuks) StartAutosave() {
|
||||||
|
defer gmx.Recover()
|
||||||
|
ticker := time.NewTicker(time.Minute)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
gmx.Save()
|
||||||
|
case val := <-gmx.stop:
|
||||||
|
if val {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gmx *Gomuks) Recover() {
|
func (gmx *Gomuks) Recover() {
|
||||||
@ -101,6 +125,7 @@ func (gmx *Gomuks) Recover() {
|
|||||||
|
|
||||||
func (gmx *Gomuks) Start() {
|
func (gmx *Gomuks) Start() {
|
||||||
defer gmx.Recover()
|
defer gmx.Recover()
|
||||||
|
go gmx.StartAutosave()
|
||||||
if err := gmx.app.Run(); err != nil {
|
if err := gmx.app.Run(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ type MainView interface {
|
|||||||
AddRoom(roomID string)
|
AddRoom(roomID string)
|
||||||
RemoveRoom(roomID string)
|
RemoveRoom(roomID string)
|
||||||
SetRooms(roomIDs []string)
|
SetRooms(roomIDs []string)
|
||||||
|
SaveAllHistory()
|
||||||
|
|
||||||
SetTyping(roomID string, users []string)
|
SetTyping(roomID string, users []string)
|
||||||
AddServiceMessage(roomID string, message string)
|
AddServiceMessage(roomID string, message string)
|
||||||
|
@ -28,7 +28,7 @@ type Message struct {
|
|||||||
BasicMeta
|
BasicMeta
|
||||||
ID string
|
ID string
|
||||||
Text string
|
Text string
|
||||||
Buffer []string
|
buffer []string
|
||||||
prevBufferWidth int
|
prevBufferWidth int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,12 +64,12 @@ func (message *Message) CalculateBuffer(width int) {
|
|||||||
if width < 2 {
|
if width < 2 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
message.Buffer = []string{}
|
message.buffer = []string{}
|
||||||
forcedLinebreaks := strings.Split(message.Text, "\n")
|
forcedLinebreaks := strings.Split(message.Text, "\n")
|
||||||
newlines := 0
|
newlines := 0
|
||||||
for _, str := range forcedLinebreaks {
|
for _, str := range forcedLinebreaks {
|
||||||
if len(str) == 0 && newlines < 1 {
|
if len(str) == 0 && newlines < 1 {
|
||||||
message.Buffer = append(message.Buffer, "")
|
message.buffer = append(message.buffer, "")
|
||||||
newlines++
|
newlines++
|
||||||
} else {
|
} else {
|
||||||
newlines = 0
|
newlines = 0
|
||||||
@ -87,7 +87,7 @@ func (message *Message) CalculateBuffer(width int) {
|
|||||||
extract = extract[:matches[len(matches)-1][1]]
|
extract = extract[:matches[len(matches)-1][1]]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
message.Buffer = append(message.Buffer, extract)
|
message.buffer = append(message.buffer, extract)
|
||||||
str = str[len(extract):]
|
str = str[len(extract):]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -97,3 +97,11 @@ func (message *Message) CalculateBuffer(width int) {
|
|||||||
func (message *Message) RecalculateBuffer() {
|
func (message *Message) RecalculateBuffer() {
|
||||||
message.CalculateBuffer(message.prevBufferWidth)
|
message.CalculateBuffer(message.prevBufferWidth)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (message *Message) Buffer() []string {
|
||||||
|
return message.buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (message *Message) Height() int {
|
||||||
|
return len(message.buffer)
|
||||||
|
}
|
||||||
|
@ -162,7 +162,7 @@ func (view *MainView) HandleCommand(room, command string, args []string) {
|
|||||||
case "/quit":
|
case "/quit":
|
||||||
view.gmx.Stop()
|
view.gmx.Stop()
|
||||||
case "/clearcache":
|
case "/clearcache":
|
||||||
view.config.Session.Clear()
|
view.config.Clear()
|
||||||
view.gmx.Stop()
|
view.gmx.Stop()
|
||||||
case "/panic":
|
case "/panic":
|
||||||
panic("This is a test panic.")
|
panic("This is a test panic.")
|
||||||
@ -239,6 +239,15 @@ func (view *MainView) Focus(delegate func(p tview.Primitive)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (view *MainView) SaveAllHistory() {
|
||||||
|
for _, room := range view.rooms {
|
||||||
|
err := room.SaveHistory(view.config.HistoryDir)
|
||||||
|
if err != nil {
|
||||||
|
debug.Printf("Failed to save history of %s: %v", room.Room.GetTitle(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (view *MainView) addRoom(index int, room string) {
|
func (view *MainView) addRoom(index int, room string) {
|
||||||
roomStore := view.matrix.GetRoom(room)
|
roomStore := view.matrix.GetRoom(room)
|
||||||
|
|
||||||
@ -254,7 +263,13 @@ func (view *MainView) addRoom(index int, room string) {
|
|||||||
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()
|
||||||
go view.LoadInitialHistory(room)
|
|
||||||
|
count, err := roomView.LoadHistory(view.config.HistoryDir)
|
||||||
|
if err != nil {
|
||||||
|
debug.Printf("Failed to load history of %s: %v", roomView.Room.GetTitle(), err)
|
||||||
|
} else if count <= 0 {
|
||||||
|
go view.LoadInitialHistory(room)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,6 +392,11 @@ func (view *MainView) LoadHistory(room string, initial bool) {
|
|||||||
room.AddMessage(message, widget.PrependMessage)
|
room.AddMessage(message, widget.PrependMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
err = roomView.SaveHistory(view.config.HistoryDir)
|
||||||
|
if err != nil {
|
||||||
|
debug.Printf("%Failed to save history of %s: %v", roomView.Room.GetTitle(), err)
|
||||||
|
}
|
||||||
|
view.config.Session.Save()
|
||||||
view.parent.Render()
|
view.parent.Render()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,9 @@
|
|||||||
package widget
|
package widget
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/gob"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
@ -79,6 +81,45 @@ func (view *MessageView) NewMessage(id, sender, text string, timestamp time.Time
|
|||||||
GetHashColor(sender))
|
GetHashColor(sender))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (view *MessageView) SaveHistory(path string) error {
|
||||||
|
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
enc := gob.NewEncoder(file)
|
||||||
|
err = enc.Encode(view.messages)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (view *MessageView) LoadHistory(path string) (int, error) {
|
||||||
|
file, err := os.OpenFile(path, os.O_RDONLY, 0600)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
dec := gob.NewDecoder(file)
|
||||||
|
err = dec.Decode(&view.messages)
|
||||||
|
if err != nil {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, message := range view.messages {
|
||||||
|
view.updateWidestSender(message.Sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(view.messages), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (view *MessageView) updateWidestSender(sender string) {
|
func (view *MessageView) updateWidestSender(sender string) {
|
||||||
if len(sender) > view.widestSender {
|
if len(sender) > view.widestSender {
|
||||||
view.widestSender = len(sender)
|
view.widestSender = len(sender)
|
||||||
@ -103,8 +144,12 @@ func (view *MessageView) UpdateMessageID(message *types.Message, newID string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (view *MessageView) AddMessage(message *types.Message, direction MessageDirection) {
|
func (view *MessageView) AddMessage(message *types.Message, direction MessageDirection) {
|
||||||
|
if message == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
msg, messageExists := view.messageIDs[message.ID]
|
msg, messageExists := view.messageIDs[message.ID]
|
||||||
if messageExists {
|
if msg != nil && messageExists {
|
||||||
message.CopyTo(msg)
|
message.CopyTo(msg)
|
||||||
direction = IgnoreMessage
|
direction = IgnoreMessage
|
||||||
}
|
}
|
||||||
@ -117,7 +162,7 @@ func (view *MessageView) AddMessage(message *types.Message, direction MessageDir
|
|||||||
|
|
||||||
if direction == AppendMessage {
|
if direction == AppendMessage {
|
||||||
if view.ScrollOffset > 0 {
|
if view.ScrollOffset > 0 {
|
||||||
view.ScrollOffset += len(message.Buffer)
|
view.ScrollOffset += message.Height()
|
||||||
}
|
}
|
||||||
view.messages = append(view.messages, message)
|
view.messages = append(view.messages, message)
|
||||||
view.appendBuffer(message)
|
view.appendBuffer(message)
|
||||||
@ -137,8 +182,8 @@ func (view *MessageView) appendBuffer(message *types.Message) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
view.textBuffer = append(view.textBuffer, message.Buffer...)
|
view.textBuffer = append(view.textBuffer, message.Buffer()...)
|
||||||
for range message.Buffer {
|
for range message.Buffer() {
|
||||||
view.metaBuffer = append(view.metaBuffer, message)
|
view.metaBuffer = append(view.metaBuffer, message)
|
||||||
}
|
}
|
||||||
view.prevMsgCount++
|
view.prevMsgCount++
|
||||||
|
@ -18,6 +18,7 @@ package widget
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -67,6 +68,18 @@ func NewRoomView(room *rooms.Room) *RoomView {
|
|||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (view *RoomView) logPath(dir string) string {
|
||||||
|
return filepath.Join(dir, fmt.Sprintf("%s.gmxlog", view.Room.ID))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (view *RoomView) SaveHistory(dir string) error {
|
||||||
|
return view.MessageView().SaveHistory(view.logPath(dir))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (view *RoomView) LoadHistory(dir string) (int, error) {
|
||||||
|
return view.MessageView().LoadHistory(view.logPath(dir))
|
||||||
|
}
|
||||||
|
|
||||||
func (view *RoomView) SetTabCompleteFunc(fn func(room *RoomView, text string, cursorOffset int) string) *RoomView {
|
func (view *RoomView) SetTabCompleteFunc(fn func(room *RoomView, text string, cursorOffset int) string) *RoomView {
|
||||||
view.input.SetTabCompleteFunc(func(text string, cursorOffset int) string {
|
view.input.SetTabCompleteFunc(func(text string, cursorOffset int) string {
|
||||||
return fn(view, text, cursorOffset)
|
return fn(view, text, cursorOffset)
|
||||||
|
Loading…
Reference in New Issue
Block a user