diff --git a/border.go b/border.go new file mode 100644 index 0000000..cd0b8a1 --- /dev/null +++ b/border.go @@ -0,0 +1,44 @@ +// 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 . + +package main + +import ( + "github.com/gdamore/tcell" + "maunium.net/go/tview" +) + +type Border struct { + *tview.Box +} + +func NewBorder() *Border { + return &Border{tview.NewBox()} +} + +func (border *Border) Draw(screen tcell.Screen) { + background := tcell.StyleDefault.Background(border.GetBackgroundColor()).Foreground(border.GetBorderColor()) + x, y, width, height := border.GetRect() + if width == 1 { + for borderY := y; borderY < y+height; borderY++ { + screen.SetContent(x, borderY, tview.GraphicsVertBar, nil, background) + } + } else if height == 1 { + for borderX := x; borderX < x+width; borderX++ { + screen.SetContent(borderX, y, tview.GraphicsHoriBar, nil, background) + } + } +} diff --git a/matrix.go b/matrix.go index c763852..7ef4b06 100644 --- a/matrix.go +++ b/matrix.go @@ -162,50 +162,44 @@ func (c *MatrixContainer) HandleTyping(evt *gomatrix.Event) { } func (c *MatrixContainer) SendMessage(roomID, message string) { - c.client.UserTyping(roomID, false, 0) + c.SendTyping(roomID, false) c.client.SendText(roomID, message) } -func (c *MatrixContainer) SendTyping(roomID string) { +func (c *MatrixContainer) SendTyping(roomID string, typing bool) { time := time.Now().Unix() - if c.typing > time { + if c.typing > time && typing { return } - c.client.UserTyping(roomID, true, 5000) - c.typing = time + 5000 + if typing { + c.client.UserTyping(roomID, true, 5000) + c.typing = time + 5 + } else { + c.client.UserTyping(roomID, false, 0) + c.typing = 0 + } } -func (c *MatrixContainer) GetStateEvent(roomID, eventType, stateKey string) *gomatrix.Event { - content := make(map[string]interface{}) - c.client.StateEvent(roomID, eventType, stateKey, &content) - if len(content) == 0 { +func (c *MatrixContainer) GetState(roomID string) []*gomatrix.Event { + content := make([]*gomatrix.Event, 0) + err := c.client.StateEvent(roomID, "", "", &content) + if err != nil { + c.debug.Print(err) return nil } - return &gomatrix.Event{ - StateKey: &stateKey, - Sender: "", - Type: eventType, - Timestamp: 0, - ID: "", - RoomID: roomID, - Content: content, - } + return content } -func (c *MatrixContainer) GetAndUpdateStateEvent(room *gomatrix.Room, eventType, stateKey string) { +func (c *MatrixContainer) UpdateState(roomID string) { + room := c.client.Store.LoadRoom(roomID) if room == nil { return } - event := c.GetStateEvent(room.ID, eventType, stateKey) - if event != nil { - room.UpdateState(event) + events := c.GetState(room.ID) + if events != nil { + for _, event := range events { + room.UpdateState(event) + } } } - -func (c *MatrixContainer) UpdateRoomInfo(roomID string) { - room := c.client.Store.LoadRoom(roomID) - c.GetAndUpdateStateEvent(room, "m.room.name", "") - c.GetAndUpdateStateEvent(room, "m.room.canonical_alias", "") - c.GetAndUpdateStateEvent(room, "m.room.topic", "") -} diff --git a/room-view.go b/room-view.go new file mode 100644 index 0000000..8e8d9db --- /dev/null +++ b/room-view.go @@ -0,0 +1,104 @@ +// 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 . + +package main + +import ( + "fmt" + "sort" + "strings" + + "github.com/gdamore/tcell" + "maunium.net/go/tview" +) + +type RoomView struct { + *tview.Box + + topic *tview.TextView + content *tview.TextView + status *tview.TextView + userlist *tview.TextView + users sort.StringSlice +} + +func NewRoomView(topic string) *RoomView { + view := &RoomView{ + Box: tview.NewBox(), + topic: tview.NewTextView(), + content: tview.NewTextView(), + status: tview.NewTextView(), + userlist: tview.NewTextView(), + } + view.topic. + SetText(strings.Replace(topic, "\n", " ", -1)). + SetBackgroundColor(tcell.ColorDarkGreen) + view.status.SetBackgroundColor(tcell.ColorDimGray) + return view +} + +func (view *RoomView) Draw(screen tcell.Screen) { + x, y, width, height := view.GetRect() + view.topic.SetRect(x, y, width, 1) + view.content.SetRect(x, y+1, width-30, height-2) + view.status.SetRect(x, y+height-1, width, 1) + view.userlist.SetRect(x+width-29, y+1, 29, height-2) + + view.topic.Draw(screen) + view.content.Draw(screen) + view.status.Draw(screen) + + borderX := x + width - 30 + background := tcell.StyleDefault.Background(view.GetBackgroundColor()).Foreground(view.GetBorderColor()) + for borderY := y + 1; borderY < y+height-1; borderY++ { + screen.SetContent(borderX, borderY, tview.GraphicsVertBar, nil, background) + } + view.userlist.Draw(screen) +} + +func (view *RoomView) SetTyping(users []string) { + if len(users) == 0 { + view.status.SetText("") + } else if len(users) < 2 { + view.status.SetText("Typing: " + strings.Join(users, " and ")) + } else { + view.status.SetText(fmt.Sprintf( + "Typing: %s and %s", + strings.Join(users[:len(users)-1], ", "), users[len(users)-1])) + } +} + +func (view *RoomView) AddMessage(sender, message string) { + fmt.Fprintf(view.content, "<%s> %s\n", sender, message) +} + +func (view *RoomView) SetUsers(users []string) { + view.users = sort.StringSlice(users) + view.users.Sort() + view.userlist.SetText(strings.Join(view.users, "\n")) +} + +func (view *RoomView) RemoveUser(user string) { + i := view.users.Search(user) + if i >= 0 { + view.users = append(view.users[:i], view.users[i+1:]...) + view.userlist.SetText(strings.Join(view.users, "\n")) + } +} + +func (view *RoomView) AddUser(user string) { + view.SetUsers(append(view.users, user)) +} diff --git a/view-main.go b/view-main.go index f23b039..79dd023 100644 --- a/view-main.go +++ b/view-main.go @@ -17,7 +17,6 @@ package main import ( - "fmt" "strings" "github.com/gdamore/tcell" @@ -25,72 +24,6 @@ import ( "maunium.net/go/tview" ) -type RoomView struct { - *tview.Box - - topic *tview.TextView - content *tview.TextView - status *tview.TextView - userlist *tview.TextView - name string -} - -func NewRoomView(name, topic string) *RoomView { - view := &RoomView{ - Box: tview.NewBox(), - topic: tview.NewTextView(), - content: tview.NewTextView(), - status: tview.NewTextView(), - userlist: tview.NewTextView(), - name: name, - } - view.topic.SetText(topic).SetBackgroundColor(tcell.ColorDarkGreen) - view.status.SetBackgroundColor(tcell.ColorDimGray) - view.userlist.SetText("@tulir:maunium.net\n@tulir_test:maunium.net") - return view -} - -func (view *RoomView) Draw(screen tcell.Screen) { - x, y, width, height := view.GetRect() - view.topic.SetRect(x, y, width, 1) - view.content.SetRect(x, y+1, width-30, height-2) - view.status.SetRect(x, y+height-1, width,1) - view.userlist.SetRect(x+width-29, y+1, 29, height - 2) - - view.topic.Draw(screen) - view.content.Draw(screen) - view.status.Draw(screen) - - borderX := x+width-30 - background := tcell.StyleDefault.Background(view.GetBackgroundColor()).Foreground(view.GetBorderColor()) - for borderY := y + 1; borderY < y + height - 1; borderY++ { - screen.SetContent(borderX, borderY, tview.GraphicsVertBar, nil, background) - } - view.userlist.Draw(screen) -} - -type Border struct { - *tview.Box -} - -func NewBorder() *Border { - return &Border{tview.NewBox()} -} - -func (border *Border) Draw(screen tcell.Screen) { - background := tcell.StyleDefault.Background(border.GetBackgroundColor()).Foreground(border.GetBorderColor()) - x, y, width, height := border.GetRect() - if width == 1 { - for borderY := y; borderY < y + height; borderY++ { - screen.SetContent(x, borderY, tview.GraphicsVertBar, nil, background) - } - } else if height == 1 { - for borderX := x; borderX < x + width; borderX++ { - screen.SetContent(borderX, y, tview.GraphicsHoriBar, nil, background) - } - } -} - func (ui *GomuksUI) MakeMainUI() tview.Primitive { ui.mainView = tview.NewGrid() ui.mainView.SetColumns(30, 1, 0).SetRows(0, 1) @@ -106,8 +39,8 @@ func (ui *GomuksUI) MakeMainUI() tview.Primitive { ui.mainView.AddItem(ui.mainViewRoomView, 0, 2, 1, 1, 0, 0, false) ui.mainViewInput = tview.NewInputField() - ui.mainViewInput.SetChangedFunc(func(_ string) { - ui.matrix.SendTyping(ui.currentRoom()) + ui.mainViewInput.SetChangedFunc(func(text string) { + ui.matrix.SendTyping(ui.currentRoom(), len(text) > 0) }) ui.mainViewInput.SetDoneFunc(func(key tcell.Key) { if key == tcell.KeyEnter { @@ -183,32 +116,24 @@ func (ui *GomuksUI) SetRoomList(rooms []string) { for index, room := range rooms { localRoomIndex := index - ui.matrix.UpdateRoomInfo(room) + ui.matrix.UpdateState(room) roomStore := ui.matrix.config.Session.LoadRoom(room) name := room topic := "" + var users []string if roomStore != nil { - nameEvt := roomStore.GetStateEvent("m.room.title", "") - if nameEvt != nil { - name, _ = nameEvt.Content["title"].(string) - } else { - nameEvt = roomStore.GetStateEvent("m.room.canonical_alias", "") - if nameEvt != nil { - name, _ = nameEvt.Content["alias"].(string) - } - } - topicEvt := roomStore.GetStateEvent("m.room.topic", "") - if topicEvt != nil { - topic, _ = topicEvt.Content["topic"].(string) - topic = strings.Replace(topic, "\n", " ", -1) - } + name = roomStore.GetTitle() + topic = roomStore.GetTopic() + users = roomStore.GetMembers() } + ui.mainViewRoomList.AddItem(name, "", 0, func() { ui.SwitchRoom(localRoomIndex) }) if !ui.mainViewRoomView.HasPage(room) { - roomView := NewRoomView(name, topic) + roomView := NewRoomView(topic) + roomView.SetUsers(users) ui.mainViewRooms[room] = roomView ui.mainViewRoomView.AddPage(room, roomView, true, false) } @@ -234,11 +159,7 @@ func (ui *GomuksUI) SwitchRoom(roomIndex int) { func (ui *GomuksUI) SetTyping(room string, users ...string) { roomView, ok := ui.mainViewRooms[room] if ok { - if len(users) > 0 { - roomView.status.SetText("Typing: " + strings.Join(users, ", ")) - } else { - roomView.status.SetText("") - } + roomView.SetTyping(users) ui.Render() } } @@ -246,7 +167,7 @@ func (ui *GomuksUI) SetTyping(room string, users ...string) { func (ui *GomuksUI) Append(room, sender, message string) { roomView, ok := ui.mainViewRooms[room] if ok { - fmt.Fprintf(roomView.content, "<%s> %s\n", sender, message) + roomView.AddMessage(sender, message) ui.Render() } }