Add UI preferences and simplify config save/load (ref #43)

This commit is contained in:
Tulir Asokan 2018-05-24 23:26:57 +03:00
parent a4d07e9a81
commit b76c8d0147
8 changed files with 141 additions and 111 deletions

View File

@ -21,15 +21,27 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"encoding/json"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
"maunium.net/go/gomatrix" "maunium.net/go/gomatrix"
"maunium.net/go/gomuks/debug" "maunium.net/go/gomuks/debug"
"maunium.net/go/gomuks/matrix/pushrules" "maunium.net/go/gomuks/matrix/pushrules"
"maunium.net/go/gomuks/matrix/rooms" "maunium.net/go/gomuks/matrix/rooms"
"strings" "strings"
"encoding/json"
) )
type AuthCache struct {
NextBatch string `yaml:"next_batch"`
FilterID string `yaml:"filter_id"`
InitialSyncDone bool `yaml:"initial_sync_done"`
}
type UserPreferences struct {
HideUserList bool `yaml:"hide_user_list",json:"hide_user_list"`
HideRoomList bool `yaml:"hide_room_list",json:"hide_room_list"`
BareMessageView bool `yaml:"bare_message_view",json:"bare_message_view"`
}
// Config contains the main config of gomuks. // Config contains the main config of gomuks.
type Config struct { type Config struct {
UserID string `yaml:"mxid"` UserID string `yaml:"mxid"`
@ -42,14 +54,10 @@ type Config struct {
MediaDir string `yaml:"media_dir"` MediaDir string `yaml:"media_dir"`
StateDir string `yaml:"state_dir"` StateDir string `yaml:"state_dir"`
AuthCache struct { Preferences UserPreferences `yaml:"-"`
NextBatch string `yaml:"next_batch"` AuthCache AuthCache `yaml:"-"`
FilterID string `yaml:"filter_id"` Rooms map[string]*rooms.Room `yaml:"-"`
InitialSyncDone bool `yaml:"initial_sync_done"` PushRules *pushrules.PushRuleset `yaml:"-"`
} `yaml:"-"`
Rooms map[string]*rooms.Room `yaml:"-"`
PushRules *pushrules.PushRuleset `yaml:"-"`
nosave bool nosave bool
} }
@ -98,29 +106,13 @@ func (config *Config) LoadAll() {
config.Load() config.Load()
config.LoadAuthCache() config.LoadAuthCache()
config.LoadPushRules() config.LoadPushRules()
config.LoadPreferences()
config.LoadRooms() config.LoadRooms()
} }
// Load loads the config from config.yaml in the directory given to the config struct. // Load loads the config from config.yaml in the directory given to the config struct.
func (config *Config) Load() { func (config *Config) Load() {
os.MkdirAll(config.Dir, 0700) config.load("config", config.Dir, "config.yaml", config)
configPath := filepath.Join(config.Dir, "config.yaml")
data, err := ioutil.ReadFile(configPath)
if err != nil {
if os.IsNotExist(err) {
config.CreateCacheDirs()
return
}
debug.Print("Failed to read config from", configPath)
panic(err)
}
err = yaml.Unmarshal(data, &config)
if err != nil {
debug.Print("Failed to parse config at", configPath)
panic(err)
}
config.CreateCacheDirs() config.CreateCacheDirs()
} }
@ -128,109 +120,40 @@ func (config *Config) SaveAll() {
config.Save() config.Save()
config.SaveAuthCache() config.SaveAuthCache()
config.SavePushRules() config.SavePushRules()
config.SavePreferences()
config.SaveRooms() config.SaveRooms()
} }
// Save saves this config to config.yaml in the directory given to the config struct. // Save saves this config to config.yaml in the directory given to the config struct.
func (config *Config) Save() { func (config *Config) Save() {
if config.nosave { config.save("config", config.Dir, "config.yaml", config)
return }
}
os.MkdirAll(config.Dir, 0700) func (config *Config) LoadPreferences() {
data, err := yaml.Marshal(&config) config.load("user preferences", config.CacheDir, "preferences.yaml", &config.Preferences)
if err != nil { }
debug.Print("Failed to marshal config")
panic(err)
}
path := filepath.Join(config.Dir, "config.yaml") func (config *Config) SavePreferences() {
err = ioutil.WriteFile(path, data, 0600) config.save("user preferences", config.CacheDir, "preferences.yaml", &config.Preferences)
if err != nil {
debug.Print("Failed to write config to", path)
panic(err)
}
} }
func (config *Config) LoadAuthCache() { func (config *Config) LoadAuthCache() {
os.MkdirAll(config.Dir, 0700) config.load("auth cache", config.CacheDir, "auth-cache.yaml", &config.AuthCache)
configPath := filepath.Join(config.CacheDir, "auth-cache.yaml")
data, err := ioutil.ReadFile(configPath)
if err != nil {
if os.IsNotExist(err) {
return
}
debug.Print("Failed to read auth cache from", configPath)
panic(err)
}
err = yaml.Unmarshal(data, &config.AuthCache)
if err != nil {
debug.Print("Failed to parse auth cache at", configPath)
panic(err)
}
} }
func (config *Config) SaveAuthCache() { func (config *Config) SaveAuthCache() {
if config.nosave { config.save("auth cache", config.CacheDir, "auth-cache.yaml", &config.AuthCache)
return
}
os.MkdirAll(config.CacheDir, 0700)
data, err := yaml.Marshal(&config.AuthCache)
if err != nil {
debug.Print("Failed to marshal auth cache")
panic(err)
}
path := filepath.Join(config.CacheDir, "auth-cache.yaml")
err = ioutil.WriteFile(path, data, 0600)
if err != nil {
debug.Print("Failed to write auth cache to", path)
panic(err)
}
} }
func (config *Config) LoadPushRules() { func (config *Config) LoadPushRules() {
os.MkdirAll(config.CacheDir, 0700) config.load("push rules", config.CacheDir, "pushrules.json", &config.PushRules)
pushRulesPath := filepath.Join(config.CacheDir, "pushrules.json")
data, err := ioutil.ReadFile(pushRulesPath)
if err != nil {
if os.IsNotExist(err) {
return
}
debug.Print("Failed to read push rules from", pushRulesPath)
return
}
config.PushRules = &pushrules.PushRuleset{}
err = json.Unmarshal(data, &config.PushRules)
if err != nil {
debug.Print("Failed to parse push rules at", pushRulesPath)
return
}
} }
func (config *Config) SavePushRules() { func (config *Config) SavePushRules() {
if config.nosave || config.PushRules == nil { if config.PushRules == nil {
return
}
os.MkdirAll(config.CacheDir, 0700)
data, err := json.Marshal(&config.PushRules)
if err != nil {
debug.Print("Failed to marshal push rules")
return
}
path := filepath.Join(config.CacheDir, "pushrules.json")
err = ioutil.WriteFile(path, data, 0600)
if err != nil {
debug.Print("Failed to write config to", path)
return return
} }
config.save("push rules", config.CacheDir, "pushrules.json", &config.PushRules)
} }
func (config *Config) LoadRooms() { func (config *Config) LoadRooms() {
@ -272,6 +195,56 @@ func (config *Config) SaveRooms() {
} }
} }
func (config *Config) load(name, dir, file string, target interface{}) {
os.MkdirAll(dir, 0700)
path := filepath.Join(dir, file)
data, err := ioutil.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return
}
debug.Print("Failed to read", name, "from", path)
panic(err)
}
if strings.HasSuffix(file, ".yaml") {
err = yaml.Unmarshal(data, target)
} else {
err = json.Unmarshal(data, target)
}
if err != nil {
debug.Print("Failed to parse", name, "at", path)
panic(err)
}
}
func (config *Config) save(name, dir, file string, source interface{}) {
if config.nosave {
return
}
os.MkdirAll(dir, 0700)
var data []byte
var err error
if strings.HasSuffix(file, ".yaml") {
data, err = yaml.Marshal(source)
} else {
data, err = json.Marshal(source)
}
if err != nil {
debug.Print("Failed to marshal", name)
panic(err)
}
path := filepath.Join(dir, file)
err = ioutil.WriteFile(path, data, 0600)
if err != nil {
debug.Print("Failed to write", name, "to", path)
panic(err)
}
}
func (config *Config) GetUserID() string { func (config *Config) GetUserID() string {
return config.UserID return config.UserID
} }

View File

@ -32,6 +32,7 @@ type MatrixContainer interface {
Login(user, password string) error Login(user, password string) error
Logout() Logout()
SendPreferencesToMatrix()
SendMessage(roomID, msgtype, message string) (string, error) SendMessage(roomID, msgtype, message string) (string, error)
SendMarkdownMessage(roomID, msgtype, message string) (string, error) SendMarkdownMessage(roomID, msgtype, message string) (string, error)
SendTyping(roomID string, typing bool) SendTyping(roomID string, typing bool)

View File

@ -29,6 +29,7 @@ type UIProvider func(gmx Gomuks) GomuksUI
type GomuksUI interface { type GomuksUI interface {
Render() Render()
HandleNewPreferences()
OnLogin() OnLogin()
OnLogout() OnLogout()
MainView() MainView MainView() MainView

View File

@ -39,6 +39,7 @@ import (
"maunium.net/go/gomuks/matrix/pushrules" "maunium.net/go/gomuks/matrix/pushrules"
"maunium.net/go/gomuks/matrix/rooms" "maunium.net/go/gomuks/matrix/rooms"
"crypto/tls" "crypto/tls"
"encoding/json"
) )
// Container is a wrapper for a gomatrix Client and some other stuff. // Container is a wrapper for a gomatrix Client and some other stuff.
@ -188,6 +189,7 @@ func (c *Container) OnLogin() {
c.syncer.OnEventType("m.direct", c.HandleDirectChatInfo) c.syncer.OnEventType("m.direct", c.HandleDirectChatInfo)
c.syncer.OnEventType("m.push_rules", c.HandlePushRules) c.syncer.OnEventType("m.push_rules", c.HandlePushRules)
c.syncer.OnEventType("m.tag", c.HandleTag) c.syncer.OnEventType("m.tag", c.HandleTag)
c.syncer.OnEventType("net.maunium.gomuks.preferences", c.HandlePreferences)
c.syncer.InitDoneCallback = func() { c.syncer.InitDoneCallback = func() {
c.config.AuthCache.InitialSyncDone = true c.config.AuthCache.InitialSyncDone = true
c.config.SaveAuthCache() c.config.SaveAuthCache()
@ -235,6 +237,24 @@ func (c *Container) Start() {
} }
} }
func (c *Container) HandlePreferences(source EventSource, evt *gomatrix.Event) {
orig := c.config.Preferences
rt, _ := json.Marshal(&evt.Content)
json.Unmarshal(rt, &c.config.Preferences)
debug.Print("Updated preferences:", orig, "->", c.config.Preferences)
c.ui.HandleNewPreferences()
}
func (c *Container) SendPreferencesToMatrix() {
defer debug.Recover()
debug.Print("Sending updated preferences:", c.config.Preferences)
u := c.client.BuildURL("user", c.config.UserID, "account_data", "net.maunium.gomuks.preferences")
_, err := c.client.MakeRequest("PUT", u, &c.config.Preferences, nil)
if err != nil {
debug.Print("Failed to update preferences:", err)
}
}
// HandleMessage is the event handler for the m.room.message timeline event. // HandleMessage is the event handler for the m.room.message timeline event.
func (c *Container) HandleMessage(source EventSource, evt *gomatrix.Event) { func (c *Container) HandleMessage(source EventSource, evt *gomatrix.Event) {
if source&EventSourceLeave != 0 { if source&EventSourceLeave != 0 {

View File

@ -184,7 +184,7 @@ func (s *GomuksSyncer) GetFilterJSON(userID string) json.RawMessage {
}, },
}, },
AccountData: gomatrix.FilterPart{ AccountData: gomatrix.FilterPart{
Types: []string{"m.push_rules", "m.direct"}, Types: []string{"m.push_rules", "m.direct", "net.maunium.gomuks.preferences"},
}, },
Presence: gomatrix.FilterPart{ Presence: gomatrix.FilterPart{
Types: []string{}, Types: []string{},

View File

@ -491,7 +491,7 @@ func (view *MessageView) Draw(screen tcell.Screen) {
bareMode := view.parent.parent.bareMessages bareMode := view.parent.parent.bareMessages
if bareMode { if bareMode {
messageX = 0 messageX = x
} }
indexOffset := view.getIndexOffset(screen, height, messageX) indexOffset := view.getIndexOffset(screen, height, messageX)

View File

@ -86,6 +86,14 @@ func (ui *GomuksUI) OnLogout() {
ui.app.SetFocus(ui.loginView) ui.app.SetFocus(ui.loginView)
} }
func (ui *GomuksUI) HandleNewPreferences() {
prefs := ui.gmx.Config().Preferences
ui.mainView.bareMessages = prefs.BareMessageView
ui.mainView.hideUserList = prefs.HideUserList
ui.mainView.hideRoomList = prefs.HideRoomList
ui.Render()
}
func (ui *GomuksUI) SetView(name View) { func (ui *GomuksUI) SetView(name View) {
ui.views.SwitchToPage(string(name)) ui.views.SwitchToPage(string(name))
} }

View File

@ -57,6 +57,7 @@ type MainView struct {
} }
func (ui *GomuksUI) NewMainView() tview.Primitive { func (ui *GomuksUI) NewMainView() tview.Primitive {
prefs := ui.gmx.Config().Preferences
mainView := &MainView{ mainView := &MainView{
Flex: tview.NewFlex(), Flex: tview.NewFlex(),
roomList: NewRoomList(), roomList: NewRoomList(),
@ -67,6 +68,10 @@ func (ui *GomuksUI) NewMainView() tview.Primitive {
gmx: ui.gmx, gmx: ui.gmx,
config: ui.gmx.Config(), config: ui.gmx.Config(),
parent: ui, parent: ui,
hideUserList: prefs.HideUserList,
hideRoomList: prefs.HideRoomList,
bareMessages: prefs.BareMessageView,
} }
mainView. mainView.
@ -185,6 +190,28 @@ func (view *MainView) HandleCommand(roomView *RoomView, command string, args []s
if err == nil { if err == nil {
view.RemoveRoom(roomView.Room) view.RemoveRoom(roomView.Room)
} }
case "/uitoggle":
if len(args) == 0 {
roomView.AddServiceMessage("Usage: /uitoggle <rooms/users/baremessages>")
break
}
switch args[0] {
case "rooms":
view.hideRoomList = !view.hideRoomList
view.config.Preferences.HideRoomList = view.hideRoomList
case "users":
view.hideUserList = !view.hideUserList
view.config.Preferences.HideUserList = view.hideUserList
case "baremessages":
view.bareMessages = !view.bareMessages
view.config.Preferences.BareMessageView = view.bareMessages
default:
roomView.AddServiceMessage("Usage: /uitoggle <rooms/users/baremessages>")
return
}
view.parent.Render()
view.parent.Render()
go view.matrix.SendPreferencesToMatrix()
case "/join": case "/join":
if len(args) == 0 { if len(args) == 0 {
roomView.AddServiceMessage("Usage: /join <room>") roomView.AddServiceMessage("Usage: /join <room>")