From b76c8d01478bcdeb8e0f0c00ca56f577a425674b Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 24 May 2018 23:26:57 +0300 Subject: [PATCH] Add UI preferences and simplify config save/load (ref #43) --- config/config.go | 191 +++++++++++++++++++------------------------- interface/matrix.go | 1 + interface/ui.go | 1 + matrix/matrix.go | 20 +++++ matrix/sync.go | 2 +- ui/message-view.go | 2 +- ui/ui.go | 8 ++ ui/view-main.go | 27 +++++++ 8 files changed, 141 insertions(+), 111 deletions(-) diff --git a/config/config.go b/config/config.go index a81b936..873c714 100644 --- a/config/config.go +++ b/config/config.go @@ -21,15 +21,27 @@ import ( "os" "path/filepath" - "encoding/json" "gopkg.in/yaml.v2" "maunium.net/go/gomatrix" "maunium.net/go/gomuks/debug" "maunium.net/go/gomuks/matrix/pushrules" "maunium.net/go/gomuks/matrix/rooms" "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. type Config struct { UserID string `yaml:"mxid"` @@ -42,14 +54,10 @@ type Config struct { MediaDir string `yaml:"media_dir"` StateDir string `yaml:"state_dir"` - AuthCache struct { - NextBatch string `yaml:"next_batch"` - FilterID string `yaml:"filter_id"` - InitialSyncDone bool `yaml:"initial_sync_done"` - } `yaml:"-"` - - Rooms map[string]*rooms.Room `yaml:"-"` - PushRules *pushrules.PushRuleset `yaml:"-"` + Preferences UserPreferences `yaml:"-"` + AuthCache AuthCache `yaml:"-"` + Rooms map[string]*rooms.Room `yaml:"-"` + PushRules *pushrules.PushRuleset `yaml:"-"` nosave bool } @@ -98,29 +106,13 @@ func (config *Config) LoadAll() { config.Load() config.LoadAuthCache() config.LoadPushRules() + config.LoadPreferences() config.LoadRooms() } // Load loads the config from config.yaml in the directory given to the config struct. func (config *Config) Load() { - os.MkdirAll(config.Dir, 0700) - - 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.load("config", config.Dir, "config.yaml", config) config.CreateCacheDirs() } @@ -128,109 +120,40 @@ func (config *Config) SaveAll() { config.Save() config.SaveAuthCache() config.SavePushRules() + config.SavePreferences() config.SaveRooms() } // Save saves this config to config.yaml in the directory given to the config struct. func (config *Config) Save() { - if config.nosave { - return - } + config.save("config", config.Dir, "config.yaml", config) +} - os.MkdirAll(config.Dir, 0700) - data, err := yaml.Marshal(&config) - if err != nil { - debug.Print("Failed to marshal config") - panic(err) - } +func (config *Config) LoadPreferences() { + config.load("user preferences", config.CacheDir, "preferences.yaml", &config.Preferences) +} - path := filepath.Join(config.Dir, "config.yaml") - err = ioutil.WriteFile(path, data, 0600) - if err != nil { - debug.Print("Failed to write config to", path) - panic(err) - } +func (config *Config) SavePreferences() { + config.save("user preferences", config.CacheDir, "preferences.yaml", &config.Preferences) } func (config *Config) LoadAuthCache() { - os.MkdirAll(config.Dir, 0700) - - 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) - } + config.load("auth cache", config.CacheDir, "auth-cache.yaml", &config.AuthCache) } func (config *Config) SaveAuthCache() { - if config.nosave { - 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) - } + config.save("auth cache", config.CacheDir, "auth-cache.yaml", &config.AuthCache) } func (config *Config) LoadPushRules() { - os.MkdirAll(config.CacheDir, 0700) - - 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 - } + config.load("push rules", config.CacheDir, "pushrules.json", &config.PushRules) } func (config *Config) SavePushRules() { - if config.nosave || 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) + if config.PushRules == nil { return } + config.save("push rules", config.CacheDir, "pushrules.json", &config.PushRules) } 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 { return config.UserID } diff --git a/interface/matrix.go b/interface/matrix.go index 111a2b0..08b3d61 100644 --- a/interface/matrix.go +++ b/interface/matrix.go @@ -32,6 +32,7 @@ type MatrixContainer interface { Login(user, password string) error Logout() + SendPreferencesToMatrix() SendMessage(roomID, msgtype, message string) (string, error) SendMarkdownMessage(roomID, msgtype, message string) (string, error) SendTyping(roomID string, typing bool) diff --git a/interface/ui.go b/interface/ui.go index 7e181d8..b106229 100644 --- a/interface/ui.go +++ b/interface/ui.go @@ -29,6 +29,7 @@ type UIProvider func(gmx Gomuks) GomuksUI type GomuksUI interface { Render() + HandleNewPreferences() OnLogin() OnLogout() MainView() MainView diff --git a/matrix/matrix.go b/matrix/matrix.go index b11b9ed..556df4e 100644 --- a/matrix/matrix.go +++ b/matrix/matrix.go @@ -39,6 +39,7 @@ import ( "maunium.net/go/gomuks/matrix/pushrules" "maunium.net/go/gomuks/matrix/rooms" "crypto/tls" + "encoding/json" ) // 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.push_rules", c.HandlePushRules) c.syncer.OnEventType("m.tag", c.HandleTag) + c.syncer.OnEventType("net.maunium.gomuks.preferences", c.HandlePreferences) c.syncer.InitDoneCallback = func() { c.config.AuthCache.InitialSyncDone = true 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. func (c *Container) HandleMessage(source EventSource, evt *gomatrix.Event) { if source&EventSourceLeave != 0 { diff --git a/matrix/sync.go b/matrix/sync.go index 3f6860a..2037fcd 100644 --- a/matrix/sync.go +++ b/matrix/sync.go @@ -184,7 +184,7 @@ func (s *GomuksSyncer) GetFilterJSON(userID string) json.RawMessage { }, }, AccountData: gomatrix.FilterPart{ - Types: []string{"m.push_rules", "m.direct"}, + Types: []string{"m.push_rules", "m.direct", "net.maunium.gomuks.preferences"}, }, Presence: gomatrix.FilterPart{ Types: []string{}, diff --git a/ui/message-view.go b/ui/message-view.go index b6b7ee4..de8e412 100644 --- a/ui/message-view.go +++ b/ui/message-view.go @@ -491,7 +491,7 @@ func (view *MessageView) Draw(screen tcell.Screen) { bareMode := view.parent.parent.bareMessages if bareMode { - messageX = 0 + messageX = x } indexOffset := view.getIndexOffset(screen, height, messageX) diff --git a/ui/ui.go b/ui/ui.go index 5e06d16..c8acede 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -86,6 +86,14 @@ func (ui *GomuksUI) OnLogout() { 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) { ui.views.SwitchToPage(string(name)) } diff --git a/ui/view-main.go b/ui/view-main.go index fd9d606..9f4f247 100644 --- a/ui/view-main.go +++ b/ui/view-main.go @@ -57,6 +57,7 @@ type MainView struct { } func (ui *GomuksUI) NewMainView() tview.Primitive { + prefs := ui.gmx.Config().Preferences mainView := &MainView{ Flex: tview.NewFlex(), roomList: NewRoomList(), @@ -67,6 +68,10 @@ func (ui *GomuksUI) NewMainView() tview.Primitive { gmx: ui.gmx, config: ui.gmx.Config(), parent: ui, + + hideUserList: prefs.HideUserList, + hideRoomList: prefs.HideRoomList, + bareMessages: prefs.BareMessageView, } mainView. @@ -185,6 +190,28 @@ func (view *MainView) HandleCommand(roomView *RoomView, command string, args []s if err == nil { view.RemoveRoom(roomView.Room) } + case "/uitoggle": + if len(args) == 0 { + roomView.AddServiceMessage("Usage: /uitoggle ") + 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 ") + return + } + view.parent.Render() + view.parent.Render() + go view.matrix.SendPreferencesToMatrix() case "/join": if len(args) == 0 { roomView.AddServiceMessage("Usage: /join ")