morpheusv2

A Matrix client written in Go-QT
git clone git://archive.git.mtrnord.blog/Nordgedanken/morpheusv2.git
Log | Files | Refs | README | LICENSE

commit 3ee72a9f7951c4301c6727ccadbfecc2f2e7362c
parent 113c78c16d68cbbb219ba0f8cd9d05ad667217c6
Author: Marcel <MTRNord@users.noreply.github.com>
Date:   Sun,  6 May 2018 01:09:50 +0200

Finish backend work (#6)

* Make sure we used the installed go files instead of the ones from vendor

* Fix copying

* Fix linter and QT Source Copy

* Debug whats happening inside the qtSrc Copy

* Remove QT Src before copy

* Remove ls call that broke the CI

* Make linter happy

* Keep QT outside of Dep

* Ignore qt in dep

* Fix ignored

* Readd lock

* Fix deb building

* Fix early exit

* Run QT Stuff in main Thread

* Try to prevent deadlock without a noop

* Cleanup and log

* Use app in more places

* Handle Exit

* Run signal.Notify in main thread

* Please the linter

* Prepare setting windows in a more clean way

* Actually start The QApp

* Open MainUI on start (basicly empty yet)

* Load qml and move it to the correct directory

* Please the linter

* Open Window before entering main event loop and run both still on the main thread

* Check if windows binary got build wrong

* Fix deb menu icon

* Fix Win build on CI

* Debug output for windows

* Check if LockOSThread breaks QT

* Remove LockOSThread

* Add comments for exported functions

* Fix comments

* Prepare needed types using interfaces and init the DB

* Generate needed Directories

* Log also to file

* Fix DB comments

* Document interfaces in dataTypes

* Document the Init

* Fix deb once again

* Add noop functions for SQLite

* Fix db init

* Fix db init

* Remove Todo which is done

* Implement SaveRoom function in SQLite Implementation

* Optimize SQL types used where possible

* use json for saving things in the DB instead of weird array like things

* Fix file perms

* Dont use maps because of json. Dont use pointers as I am using a interface

* Implement GetRooms

* Implement GetRoom

* Add Messages to the db interface and make the Room on the DB hold the IDs not the messages to not have duplicates in the DB as we have messages Table

* Implement Save User for the SQLite DB

* Implement Get Users for the SQLite DB

* Prepare messages

* Implement SaveMessage

* Implement GetMessages

* Add license to all files

* Implement GetMessage()

* Prepare Room implementation

* Prepare Message Implementation

* Prepare User Implementation

* Make push ready

* Fix Close Event

* We wont need to get all users or all messages ever. Only Rooms are required

* Save currentUser bool and accessToken inside the DB

* Implement Message (without the Show function)

* Implement Room

* Add 2 missing comments in the Database

* Implement User

* Fix sql and User

* Follow golint advice

* Prepare loginUI

* Open LoginUI if no current User is in DB

* Make sure to not exit if no rows found on the currentUser check

* Simplify if

* Fix if logic

* Dont Set the window async

Diffstat:
MGopkg.lock | 8+++++++-
Mcmd/root.go | 8+++++---
Mpkg/app/main.go | 20++++++++++++++++----
Mpkg/app/ui.go | 14++++++++++++++
Mpkg/db/db.go | 33++++++++++++++++++++++++++++-----
Apkg/db/sqlite/db.go | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apkg/db/sqlite/messages.go | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apkg/db/sqlite/rooms.go | 172+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apkg/db/sqlite/struct.go | 22++++++++++++++++++++++
Apkg/db/sqlite/users.go | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apkg/loginUI/loginUI.go | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpkg/mainUI/mainUI.go | 14++++++++++++++
Mpkg/matrix/dataTypes.go | 47+++++++++++++++++++++++++++++++++++++++++++----
Apkg/matrix/messages/message.go | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apkg/matrix/rooms/room.go | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apkg/matrix/users/user.go | 127+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apkg/util/vars.go | 26++++++++++++++++++++++++++
17 files changed, 1061 insertions(+), 17 deletions(-)

diff --git a/Gopkg.lock b/Gopkg.lock @@ -14,6 +14,12 @@ revision = "a7fc80c8060c2544fe5d4dae465b584f8e9b4e27" [[projects]] + name = "github.com/mattn/go-sqlite3" + packages = ["."] + revision = "6c771bb9887719704b210e87e934f08be014bdb1" + version = "v1.6.0" + +[[projects]] branch = "master" name = "github.com/shibukawa/configdir" packages = ["."] @@ -34,6 +40,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "c22db96d1e74b32e9427a6f762b2be5797dcc8426407dc4769795fe6ca7296be" + inputs-digest = "f746b36e895639f6b0cbe2cb4d928637d254f83f60346a95eaf5c0f383683229" solver-name = "gps-cdcl" solver-version = 1 diff --git a/cmd/root.go b/cmd/root.go @@ -19,7 +19,8 @@ import ( "os" "github.com/Nordgedanken/Morpheusv2/pkg/app" - dbImpl "github.com/Nordgedanken/Morpheusv2/pkg/db/implementation" + "github.com/Nordgedanken/Morpheusv2/pkg/db/sqlite" + "github.com/Nordgedanken/Morpheusv2/pkg/util" "github.com/shibukawa/configdir" "github.com/spf13/cobra" "io" @@ -47,7 +48,7 @@ var rootCmd = &cobra.Command{ } // os.OpenFile opens the file where the log is supposed to be written to - logFile, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666) + logFile, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0600) if err != nil { return err } @@ -59,7 +60,8 @@ var rootCmd = &cobra.Command{ log.SetOutput(mw) // dbImpl.Init() generates the needed tables if needed before the app starts - err = dbImpl.Init() + util.DB = &sqlite.SQLite{} + err = util.DB.Init() if err != nil { return err } diff --git a/pkg/app/main.go b/pkg/app/main.go @@ -15,7 +15,10 @@ package app import ( + "database/sql" + "github.com/Nordgedanken/Morpheusv2/pkg/loginUI" "github.com/Nordgedanken/Morpheusv2/pkg/mainUI" + "github.com/Nordgedanken/Morpheusv2/pkg/util" "github.com/matrix-org/gomatrix" "github.com/therecipe/qt/core" "github.com/therecipe/qt/gui" @@ -36,11 +39,20 @@ func Start(argsArg []string) error { initApp() - mainUIS := mainUI.NewMainUI(windowWidth, windowHeight, window) - SetNewWindow(mainUIS, window) + _, err := util.DB.GetCurrentUser() + // We special case ErrNoRows because this is expected to happen if user is missing + if err == sql.ErrNoRows { + loginUIs := loginUI.NewLoginUI(windowWidth, windowHeight, window) + SetNewWindow(loginUIs, window) + } else if err != nil { + return err + } else { + mainUIs := mainUI.NewMainUI(windowWidth, windowHeight, window) + SetNewWindow(mainUIs, window) + } widgets.QApplication_Exec() - + return nil } @@ -65,7 +77,7 @@ func initApp() { window.Resize2(windowWidth, windowHeight) window.Move2(windowX, windowY) - app.ConnectQuit(func() { + window.ConnectCloseEvent(func(event *gui.QCloseEvent) { log.Println("Morpheus closed") }) diff --git a/pkg/app/ui.go b/pkg/app/ui.go @@ -1,3 +1,17 @@ +// Copyright © 2018 MTRNord <info@nordgedanken.de> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package app import ( diff --git a/pkg/db/db.go b/pkg/db/db.go @@ -1,14 +1,37 @@ +// Copyright © 2018 MTRNord <info@nordgedanken.de> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package db -import "github.com/Nordgedanken/Morpheusv2/pkg/matrix" +import ( + "database/sql" + "github.com/Nordgedanken/Morpheusv2/pkg/matrix" +) // DB defines a Interface to allow multiple DB Implementations type DB interface { + Init() error + Open() *sql.DB + SaveRoom(Room matrix.Room) error - GetRooms() (rooms map[string]matrix.Room, err error) - GetRoom(roomID string) (room matrix.Room, err error) + GetRooms() (roomsR []matrix.Room, err error) + GetRoom(roomID string) (roomR matrix.Room, err error) SaveUser(user matrix.User) error - GetUsers() (map[string]matrix.User, error) - GetCurrentUser() (matrix.User, error) + GetUser(userID string) (userR matrix.User, err error) + GetCurrentUser() (userR matrix.User, err error) + + SaveMessage(message matrix.Message) error + GetMessage(eventID string) (messageR matrix.Message, err error) } diff --git a/pkg/db/sqlite/db.go b/pkg/db/sqlite/db.go @@ -0,0 +1,62 @@ +// Copyright © 2018 MTRNord <info@nordgedanken.de> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sqlite + +import ( + "database/sql" + "fmt" + _ "github.com/mattn/go-sqlite3" // Go-sqlite3 side effect import needed to use SQLite3 databases + "github.com/shibukawa/configdir" + "log" + "path/filepath" +) + +// Init prepares the DB by opening it and creating the required tables if needed +func (s *SQLite) Init() (err error) { + log.Println("Start setting up DB") + var openErr error + + // Open the data.db file. It will be created if it doesn't exist. + configDirs := configdir.New("Nordgedanken", "Morpheusv2") + filePath := filepath.ToSlash(configDirs.QueryFolders(configdir.Global)[0].Path) + + log.Println("DBFilePath: ", filePath+"/data.db") + s.db, openErr = sql.Open("sqlite3", filePath+"/data.db") + if openErr != nil { + err = openErr + return + } + + log.Println("Creating DB Tables if needed") + createTables := `CREATE TABLE IF NOT EXISTS users (id varchar not null primary key, display_name text, avatar text, access_token text, own integer); + CREATE TABLE IF NOT EXISTS messages (id varchar not null primary key, author_id varchar, message text, timestamp datetime, pure_event text); + CREATE TABLE IF NOT EXISTS rooms (id varchar not null primary key, room_aliases text, room_name text, room_avatar text, room_topic text, room_messages text); + ` + _, execErr := s.db.Exec(createTables) + if execErr != nil { + err = fmt.Errorf("DB EXEC ERR: %s", execErr) + return + } + log.Println("Finished setting DB Setup") + return +} + +// Open returns the in Init() created db variable +func (s *SQLite) Open() *sql.DB { + if s.db != nil { + return s.db + } + return nil +} diff --git a/pkg/db/sqlite/messages.go b/pkg/db/sqlite/messages.go @@ -0,0 +1,93 @@ +// Copyright © 2018 MTRNord <info@nordgedanken.de> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sqlite + +import ( + "encoding/json" + "github.com/Nordgedanken/Morpheusv2/pkg/matrix" + "github.com/Nordgedanken/Morpheusv2/pkg/matrix/messages" + "github.com/matrix-org/gomatrix" + "time" +) + +// SaveMessage saves message Events to the DB +func (s *SQLite) SaveMessage(message matrix.Message) error { + if s.db == nil { + s.db = s.Open() + } + + tx, err := s.db.Begin() + if err != nil { + return err + } + + stmt, err := tx.Prepare("INSERT INTO messages (id, author_id, message, timestamp, pure_event) VALUES (?, ?, ?, ?, ?)") + if err != nil { + return err + } + defer stmt.Close() + + id := message.GetEventID() + authorID := message.GetAuthorMXID() + messageS := message.GetMessage() + timestampR := message.GetTimestamp() + timestamp := timestampR.Format("2006-01-02 15:04:05") + pureEvent := message.GetEvent() + _, err = stmt.Exec(id, authorID, messageS, timestamp, pureEvent) + if err != nil { + return err + } + + return tx.Commit() +} + +// GetMessage returns the Message where the id matches the eventID +func (s *SQLite) GetMessage(eventID string) (messageR matrix.Message, err error) { + if s.db == nil { + s.db = s.Open() + } + + stmt, err := s.db.Prepare(`SELECT author_id, message, timestamp, pure_event FROM messages WHERE id=$1`) + if err != nil { + return + } + defer stmt.Close() + + row := stmt.QueryRow(eventID) + + var authorID string + var messageS string + var timestamp time.Time + var pureEvent string + err = row.Scan(&authorID, &messageS, &timestamp, &pureEvent) + if err != nil { + return + } + + messageI := &messages.Message{} + messageI.SetEventID(eventID) + messageI.SetAuthorMXID(authorID) + messageI.SetMessage(messageS) + messageI.SetTimestamp(&timestamp) + var gomatrixEvent gomatrix.Event + err = json.Unmarshal([]byte(pureEvent), &gomatrixEvent) + if err != nil { + return + } + messageI.SetEvent(&gomatrixEvent) + + messageR = messageI + return +} diff --git a/pkg/db/sqlite/rooms.go b/pkg/db/sqlite/rooms.go @@ -0,0 +1,172 @@ +// Copyright © 2018 MTRNord <info@nordgedanken.de> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sqlite + +import ( + "encoding/json" + "github.com/Nordgedanken/Morpheusv2/pkg/matrix" + "github.com/Nordgedanken/Morpheusv2/pkg/matrix/rooms" +) + +// SaveRoom saves a Room into the sqlite DB +func (s *SQLite) SaveRoom(Room matrix.Room) error { + if s.db == nil { + s.db = s.Open() + } + + tx, err := s.db.Begin() + if err != nil { + return err + } + + stmt, err := tx.Prepare("INSERT INTO rooms (room_aliases, id, room_name, room_avatar, room_topic, room_messages) VALUES (?, ?, ?, ?, ?, ?)") + if err != nil { + return err + } + defer stmt.Close() + + aliases := Room.GetRoomAliases() + aliasesBytes, err := json.Marshal(aliases) + if err != nil { + return err + } + aliasesS := string(aliasesBytes) + roomID := Room.GetRoomID() + name, err := Room.GetName() + if err != nil { + return err + } + avatar, err := Room.GetAvatar() + if err != nil { + return err + } + topic, err := Room.GetTopic() + if err != nil { + return err + } + + messages := Room.GetMessages() + if err != nil { + return err + } + var messageIDs []string + for _, v := range messages { + messageIDs = append(messageIDs, v.GetEventID()) + } + + _, err = stmt.Exec(aliasesS, roomID, name, string(avatar), topic, messageIDs) + if err != nil { + return err + } + return tx.Commit() +} + +// GetRooms returns all Rooms from the Database +func (s *SQLite) GetRooms() (roomsR []matrix.Room, err error) { + if s.db == nil { + s.db = s.Open() + } + + rows, err := s.db.Query("SELECT id, room_aliases, room_name, room_avatar, room_topic, room_messages FROM rooms") + if err != nil { + return + } + defer rows.Close() + + for rows.Next() { + var roomID string + var roomAliases string + var roomName string + var roomAvatar string + var roomTopic string + var roomMessages string + err = rows.Scan(&roomID, &roomAliases, &roomName, &roomAvatar, &roomTopic, &roomMessages) + if err != nil { + return + } + + roomI := &rooms.Room{} + roomI.SetRoomID(roomID) + var aliases []string + err = json.Unmarshal([]byte(roomAliases), &aliases) + if err != nil { + return + } + roomI.SetRoomAliases(aliases) + roomI.SetName(roomName) + roomI.SetAvatar([]byte(roomAvatar)) + roomI.SetTopic(roomTopic) + var messageIDs []string + err = json.Unmarshal([]byte(roomMessages), &messageIDs) + if err != nil { + return + } + // TODO Convert IDs to messages slice using another call or from the beginning on using a JOIN + //roomI.SetMessages(messages) + + roomsR = append(roomsR, roomI) + } + + // get any error encountered during iteration + err = rows.Err() + return +} + +// GetRoom returns the Room where the id matches the roomID +func (s *SQLite) GetRoom(roomID string) (roomR matrix.Room, err error) { + if s.db == nil { + s.db = s.Open() + } + + stmt, err := s.db.Prepare(`SELECT room_aliases, room_name, room_avatar, room_topic, room_messages FROM rooms WHERE id=$1`) + if err != nil { + return + } + defer stmt.Close() + + row := stmt.QueryRow(roomID) + + var roomAliases string + var roomName string + var roomAvatar string + var roomTopic string + var roomMessages string + err = row.Scan(&roomAliases, &roomName, &roomAvatar, &roomTopic, &roomMessages) + if err != nil { + return + } + + roomI := &rooms.Room{} + roomI.SetRoomID(roomID) + var aliases []string + err = json.Unmarshal([]byte(roomAliases), &aliases) + if err != nil { + return + } + roomI.SetRoomAliases(aliases) + roomI.SetName(roomName) + roomI.SetAvatar([]byte(roomAvatar)) + roomI.SetTopic(roomTopic) + var messageIDs []string + err = json.Unmarshal([]byte(roomMessages), &messageIDs) + if err != nil { + return + } + // TODO Convert IDs to messages slice using another call or from the beginning on using a JOIN + //roomI.SetMessages(messages) + + roomR = roomI + return +} diff --git a/pkg/db/sqlite/struct.go b/pkg/db/sqlite/struct.go @@ -0,0 +1,22 @@ +// Copyright © 2018 MTRNord <info@nordgedanken.de> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sqlite + +import "database/sql" + +// SQLite implements a SQLite database for the db.DB interface +type SQLite struct { + db *sql.DB +} diff --git a/pkg/db/sqlite/users.go b/pkg/db/sqlite/users.go @@ -0,0 +1,125 @@ +// Copyright © 2018 MTRNord <info@nordgedanken.de> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package sqlite + +import ( + "github.com/Nordgedanken/Morpheusv2/pkg/matrix" + "github.com/Nordgedanken/Morpheusv2/pkg/matrix/users" + "github.com/matrix-org/gomatrix" + "strings" +) + +// SaveUser saves a matrix User to the User table +func (s *SQLite) SaveUser(user matrix.User) error { + if s.db == nil { + s.db = s.Open() + } + + tx, err := s.db.Begin() + if err != nil { + return err + } + + stmt, err := tx.Prepare("INSERT INTO users (id, display_name, avatar) VALUES (?, ?, ?)") + if err != nil { + return err + } + defer stmt.Close() + displayName, err := user.GetDisplayName("") + if err != nil { + return err + } + avatar, err := user.GetAvatar("") + if err != nil { + return err + } + mxid := user.GetMXID() + _, err = stmt.Exec(mxid, displayName, string(avatar)) + if err != nil { + return err + } + + return tx.Commit() +} + +// GetCurrentUser returns the current user including a ready gomatrix client +func (s *SQLite) GetCurrentUser() (userR matrix.User, err error) { + if s.db == nil { + s.db = s.Open() + } + + stmt, err := s.db.Prepare(`SELECT id, display_name, avatar, access_token FROM users WHERE own=1`) + if err != nil { + return + } + defer stmt.Close() + + row := stmt.QueryRow() + + var mxid string + var displayName string + var avatar string + var accessToken string + err = row.Scan(&mxid, &displayName, &avatar, &accessToken) + if err != nil { + return + } + + userI := &users.User{} + userI.SetMXID(mxid) + userI.SetDisplayName("", displayName) + userI.SetAvatar("", []byte(avatar)) + + splitUser := strings.Split(mxid, ":") + domain := strings.TrimPrefix(mxid, splitUser[0]+":") + client, err := gomatrix.NewClient(domain, mxid, accessToken) + if err != nil { + return + } + userI.SetCli(client) + + userR = userI + return +} + +// GetUser returns a user matching the mxid of the argument +func (s *SQLite) GetUser(userID string) (userR matrix.User, err error) { + if s.db == nil { + s.db = s.Open() + } + + stmt, err := s.db.Prepare(`SELECT display_name, avatar FROM users WHERE id=$1`) + if err != nil { + return + } + defer stmt.Close() + + row := stmt.QueryRow(userID) + + var displayName string + var avatar string + err = row.Scan(&displayName, &avatar) + if err != nil { + return + } + + userI := &users.User{} + userI.SetMXID(userID) + userI.SetDisplayName("", displayName) + userI.SetAvatar("", []byte(avatar)) + + userR = userI + return +} diff --git a/pkg/loginUI/loginUI.go b/pkg/loginUI/loginUI.go @@ -0,0 +1,77 @@ +// Copyright © 2018 MTRNord <info@nordgedanken.de> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package loginUI + +import ( + "github.com/matrix-org/gomatrix" + "github.com/therecipe/qt/core" + "github.com/therecipe/qt/gui" + "github.com/therecipe/qt/uitools" + "github.com/therecipe/qt/widgets" +) + +// LoginUI defines the data for the login ui +type LoginUI struct { + widget *widgets.QWidget + cli *gomatrix.Client + window *widgets.QMainWindow + windowWidth int + windowHeight int +} + +// NewLoginUI gives you a MainUI struct with profiled data +func NewLoginUI(windowWidth, windowHeight int, window *widgets.QMainWindow) (loginUI *LoginUI) { + loginUI = &LoginUI{ + windowWidth: windowWidth, + windowHeight: windowHeight, + window: window, + } + return +} + +// SetCli sets the gomatrix Client for the LoginUI +func (m *LoginUI) SetCli(cli *gomatrix.Client) { + m.cli = cli +} + +// GetWidget returns the QWidget of the LoginUI +func (m *LoginUI) GetWidget() (widget *widgets.QWidget) { + return m.widget +} + +// NewUI prepares the new UI +func (m *LoginUI) NewUI() error { + m.widget = widgets.NewQWidget(nil, 0) + + var loader = uitools.NewQUiLoader(nil) + var file = core.NewQFile2(":/qml/ui/login.ui") + + file.Open(core.QIODevice__ReadOnly) + mainWidget := loader.Load(file, m.widget) + file.Close() + + var layout = widgets.NewQHBoxLayout() + m.window.SetLayout(layout) + layout.InsertWidget(0, mainWidget, 0, core.Qt__AlignTop|core.Qt__AlignLeft) + layout.SetSpacing(0) + layout.SetContentsMargins(0, 0, 0, 0) + + m.widget.ConnectResizeEvent(func(event *gui.QResizeEvent) { + mainWidget.Resize(event.Size()) + event.Accept() + }) + + return nil +} diff --git a/pkg/mainUI/mainUI.go b/pkg/mainUI/mainUI.go @@ -1,3 +1,17 @@ +// Copyright © 2018 MTRNord <info@nordgedanken.de> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package mainUI import ( diff --git a/pkg/matrix/dataTypes.go b/pkg/matrix/dataTypes.go @@ -1,3 +1,17 @@ +// Copyright © 2018 MTRNord <info@nordgedanken.de> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package matrix import ( @@ -9,8 +23,15 @@ import ( type User interface { SetCli(cli *gomatrix.Client) SetMXID(id string) + SetDisplayName(roomID string, name string) + SetAvatar(roomID string, avatar []byte) + + GetMXID() string GetDisplayName(roomID string) (string, error) - GetAvatar(roomID string) (string, error) + GetAvatar(roomID string) ([]byte, error) + GetCli() (cli *gomatrix.Client) + + //Implement MarshalJSON() (b []byte, e error) as in http://gregtrowbridge.com/golang-json-serialization-with-interfaces/ to support json } // Room defines a Interface to allow multiple Room type Implementations @@ -18,11 +39,20 @@ type Room interface { // Handled using global "own User" //SetCli(cli *gomatrix.Client) SetRoomID(id string) - SetRoomAliases(aliases map[int64]string) + SetRoomAliases(aliases []string) + SetName(name string) + SetAvatar(avatar []byte) + SetTopic(topic string) + SetMessages(messages []Message) + + GetRoomID() string + GetRoomAliases() []string GetName() (string, error) - GetAvatar() (string, error) + GetAvatar() ([]byte, error) GetTopic() (string, error) - GetMessages() (map[string]Message, error) + GetMessages() []Message + + //Implement MarshalJSON() (b []byte, e error) as in http://gregtrowbridge.com/golang-json-serialization-with-interfaces/ to support json } // Message defines a Interface to allow multiple Message type Implementations @@ -34,5 +64,14 @@ type Message interface { SetAuthorMXID(mxid string) SetMessage(message string) SetTimestamp(ts *time.Time) + + GetEventID() (id string) + GetEvent() (event *gomatrix.Event) + GetAuthorMXID() (mxid string) + GetMessage() (message string) + GetTimestamp() (ts *time.Time) + Show() error + + //Implement MarshalJSON() (b []byte, e error) as in http://gregtrowbridge.com/golang-json-serialization-with-interfaces/ to support json } diff --git a/pkg/matrix/messages/message.go b/pkg/matrix/messages/message.go @@ -0,0 +1,85 @@ +// Copyright © 2018 MTRNord <info@nordgedanken.de> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package messages + +import ( + "github.com/matrix-org/gomatrix" + "time" +) + +// Message holds the needed Message data and allows to work with that. It gets normally loaded from the cache +type Message struct { + eventID string + event *gomatrix.Event + authorID string + text string + timestamp *time.Time +} + +// SetEventID adds the eventID to the current Message +func (m *Message) SetEventID(id string) { + m.eventID = id +} + +// SetEvent adds the gomatrix Event to the current Message +func (m *Message) SetEvent(event *gomatrix.Event) { + m.event = event +} + +// SetAuthorMXID adds the author MXID to the current Message +func (m *Message) SetAuthorMXID(mxid string) { + m.authorID = mxid +} + +// SetMessage adds the message Text to the current Message +func (m *Message) SetMessage(message string) { + m.text = message +} + +// SetTimestamp adds the Timestamp to the current Message +func (m *Message) SetTimestamp(ts *time.Time) { + m.timestamp = ts +} + +// GetEventID returns the event ID from the current Message +func (m *Message) GetEventID() (id string) { + return m.eventID +} + +// GetEvent returns the gomatrix event from the current Message +func (m *Message) GetEvent() (event *gomatrix.Event) { + return m.event +} + +// GetAuthorMXID returns the authors mxid from the current Message +func (m *Message) GetAuthorMXID() (mxid string) { + return m.authorID +} + +// GetMessage returns the text from the current Message +func (m *Message) GetMessage() (message string) { + return m.text +} + +// GetTimestamp returns the timestamp from the current Message +func (m *Message) GetTimestamp() (ts *time.Time) { + return m.timestamp +} + +// Show adds the current Message to the UI +// TODO implement it +func (m *Message) Show() error { + return nil +} diff --git a/pkg/matrix/rooms/room.go b/pkg/matrix/rooms/room.go @@ -0,0 +1,145 @@ +// Copyright © 2018 MTRNord <info@nordgedanken.de> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package rooms + +import ( + "github.com/Nordgedanken/Morpheusv2/pkg/matrix" + "github.com/Nordgedanken/Morpheusv2/pkg/util" + "github.com/matrix-org/gomatrix" + "strings" +) + +// Room holds the needed Room data and allows to work with that. It gets normally loaded from the cache +type Room struct { + id string + aliases []string + name string + avatar []byte + topic string + messages []matrix.Message +} + +// SetRoomID adds the roomID to the current Room +func (r *Room) SetRoomID(id string) { + r.id = id +} + +// SetRoomAliases adds the aliases to the current Room +func (r *Room) SetRoomAliases(aliases []string) { + r.aliases = aliases +} + +// SetName adds the name to the current Room +func (r *Room) SetName(name string) { + r.name = name +} + +// SetAvatar adds the avatar to the current Room +func (r *Room) SetAvatar(avatar []byte) { + r.avatar = avatar +} + +// SetTopic adds the topic to the current Room +func (r *Room) SetTopic(topic string) { + r.topic = topic +} + +// SetMessages adds the messages to the current Room +func (r *Room) SetMessages(messages []matrix.Message) { + r.messages = messages +} + +// GetRoomID returns the room ID from the current Room +func (r *Room) GetRoomID() string { + return r.id +} + +// GetRoomAliases returns the room aliases from the current Room +func (r *Room) GetRoomAliases() []string { + return r.aliases +} + +// GetName returns the name from the current Room +func (r *Room) GetName() (string, error) { + if r.name == "" { + type RespRoomName struct { + Name string `json:"name"` + } + resp := &RespRoomName{} + err := util.User.GetCli().StateEvent(r.id, "m.room.name", "", resp) + if err != nil { + return "", err + } + r.name = resp.Name + } + return r.name, nil +} + +// GetAvatar returns the avatar from the current Room +func (r *Room) GetAvatar() ([]byte, error) { + if r.avatar == nil { + resp := &gomatrix.Event{} + err := util.User.GetCli().StateEvent(r.id, "m.room.avatar", "", resp) + if err != nil { + return nil, err + } + var avatar []byte + value, exists := resp.Content["url"] + if !exists { + return nil, nil + } + url, ok := value.(string) + if !ok { + return nil, nil + } + split := strings.Split(url, "/") + servername := strings.TrimPrefix(split[0], "mxc://") + mediaID := split[1] + mediaURL := util.User.GetCli().BuildBaseURL("_matrix/media/r0/download", servername, mediaID) + avatar, err = util.User.GetCli().MakeRequest("GET", mediaURL, nil, nil) + if err != nil { + return nil, err + } + r.avatar = avatar + } + return r.avatar, nil +} + +// GetTopic returns the topic from the current Room +func (r *Room) GetTopic() (string, error) { + if r.topic == "" { + resp := &gomatrix.Event{} + err := util.User.GetCli().StateEvent(r.id, "m.room.topic", "", resp) + if err != nil { + return "", err + } + value, exists := resp.Content["topic"] + if !exists { + return "", nil + } + topic, ok := value.(string) + if !ok { + return "", nil + } + r.topic = topic + } + return r.topic, nil +} + +// GetMessages returns the messages from the current Room +func (r *Room) GetMessages() []matrix.Message { + // TODO Lazy load from DB + return r.messages +} diff --git a/pkg/matrix/users/user.go b/pkg/matrix/users/user.go @@ -0,0 +1,127 @@ +// Copyright © 2018 MTRNord <info@nordgedanken.de> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package users + +import ( + "github.com/Nordgedanken/Morpheusv2/pkg/util" + "github.com/matrix-org/gomatrix" + "strings" +) + +// User holds the needed User data and allows to work with that. It gets normally loaded from the cache +type User struct { + cli *gomatrix.Client + mxid string + defaultDisplayName string + displayName map[string]string + defaultAvatar []byte + avatar map[string][]byte +} + +// SetCli adds the gomatrix.Client to the current User. This only happens if the User is the current User aka the one that logged into the client. +func (u *User) SetCli(cli *gomatrix.Client) { + u.cli = cli +} + +// SetMXID adds the mxid to the current User +func (u *User) SetMXID(id string) { + u.mxid = id +} + +// SetDisplayName adds the displayName to the current User +func (u *User) SetDisplayName(roomID string, name string) { + if roomID == "" { + u.defaultDisplayName = name + } else { + if u.displayName == nil { + u.displayName = make(map[string]string) + } + u.displayName[roomID] = name + } +} + +// SetAvatar adds the avatar to the current User +func (u *User) SetAvatar(roomID string, avatar []byte) { + if roomID == "" { + u.defaultAvatar = avatar + } else { + if u.avatar == nil { + u.avatar = make(map[string][]byte) + } + u.avatar[roomID] = avatar + } +} + +// GetMXID returns the mxid from the current User +func (u *User) GetMXID() string { + return u.mxid +} + +// GetDisplayName returns the displayName from the current User +func (u *User) GetDisplayName(roomID string) (string, error) { + if roomID == "" { + if u.defaultDisplayName == "" { + resp, err := util.User.GetCli().GetDisplayName(u.mxid) + if err != nil { + return "", err + } + u.defaultDisplayName = resp.DisplayName + } + return u.defaultDisplayName, nil + } + // TODO get Membership Event from Room instead returning default directly + if u.displayName[roomID] == "" { + return u.defaultDisplayName, nil + } + return u.displayName[roomID], nil +} + +// GetAvatar returns the avatar from the current User +func (u *User) GetAvatar(roomID string) ([]byte, error) { + if roomID == "" { + if u.defaultAvatar == nil { + urlPath := util.User.GetCli().BuildURL("profile", u.mxid, "avatar_url") + s := struct { + AvatarURL string `json:"avatar_url"` + }{} + + _, err := util.User.GetCli().MakeRequest("GET", urlPath, nil, &s) + if err != nil { + return nil, err + } + + split := strings.Split(s.AvatarURL, "/") + servername := strings.TrimPrefix(split[0], "mxc://") + mediaID := split[1] + mediaURL := util.User.GetCli().BuildBaseURL("_matrix/media/r0/download", servername, mediaID) + avatar, err := util.User.GetCli().MakeRequest("GET", mediaURL, nil, nil) + if err != nil { + return nil, err + } + u.defaultAvatar = avatar + } + return u.defaultAvatar, nil + } + // TODO get Membership Event from Room instead returning default directly + if u.avatar[roomID] == nil { + return u.defaultAvatar, nil + } + return u.avatar[roomID], nil +} + +// GetCli returns the gomatrix.Client from the current User +func (u *User) GetCli() *gomatrix.Client { + return u.cli +} diff --git a/pkg/util/vars.go b/pkg/util/vars.go @@ -0,0 +1,26 @@ +// Copyright © 2018 MTRNord <info@nordgedanken.de> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package util + +import ( + "github.com/Nordgedanken/Morpheusv2/pkg/db" + "github.com/Nordgedanken/Morpheusv2/pkg/matrix" +) + +// DB holds the main pointer to the Database +var DB db.DB + +// User holds the Current User +var User matrix.User