commit 113c78c16d68cbbb219ba0f8cd9d05ad667217c6
parent 117632e38fbde50322fd3339c93c7f121f3a2cf9
Author: Marcel <MTRNord@users.noreply.github.com>
Date: Wed, 2 May 2018 19:47:46 +0200
Implement basic state managment for rooms, messages, users (not state as in matrix proto state) (#4)
* 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
[ci skip]
* 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
Diffstat:
11 files changed, 285 insertions(+), 43 deletions(-)
diff --git a/.circleci/config.yml b/.circleci/config.yml
@@ -21,12 +21,18 @@ jobs:
name: Get Go Dependencies
working_directory: /home/user/work/src/github.com/Nordgedanken/Morpheusv2/
command: dep ensure
+ - run:
+ name: Setup Code Climate test-reporter
+ command: |
+ curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
+ chmod +x ./cc-test-reporter
- persist_to_workspace:
# Must be an absolute path, or relative path from working_directory
root: /home/user/work/
# Must be relative path from root
paths:
- src/
+ - ./cc-test-reporter
build_linux:
docker:
- image: therecipe/qt:linux
diff --git a/Gopkg.lock b/Gopkg.lock
@@ -9,6 +9,12 @@
[[projects]]
branch = "master"
+ name = "github.com/matrix-org/gomatrix"
+ packages = ["."]
+ revision = "a7fc80c8060c2544fe5d4dae465b584f8e9b4e27"
+
+[[projects]]
+ branch = "master"
name = "github.com/shibukawa/configdir"
packages = ["."]
revision = "e180dbdc8da04c4fa04272e875ce64949f38bd3e"
diff --git a/cmd/root.go b/cmd/root.go
@@ -19,19 +19,57 @@ import (
"os"
"github.com/Nordgedanken/Morpheusv2/pkg/app"
+ dbImpl "github.com/Nordgedanken/Morpheusv2/pkg/db/implementation"
+ "github.com/shibukawa/configdir"
"github.com/spf13/cobra"
+ "io"
+ "log"
+ "path/filepath"
)
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
- Use: "Morpheusv2",
- Short: "A brief description of your application",
- Long: `A longer description that spans multiple lines and likely contains
-examples and usage of using your application. For example:
-
-Cobra is a CLI library for Go that empowers applications.
-This application is a tool to generate the needed files
-to quickly create a Cobra application.`,
+ Use: "Morpheusv2",
+ PreRunE: func(cmd *cobra.Command, args []string) error {
+ // Init Logs and folders
+ // configdir.New gets the Config Paths that are available on this system
+ configDirs := configdir.New("Nordgedanken", "Morpheusv2")
+ // path holds the global configdir of the system with the log folder appended
+ path := filepath.ToSlash(configDirs.QueryFolders(configdir.Global)[0].Path + "/log/")
+ logFilePath := filepath.ToSlash(path + "main.log")
+
+ // This checks if the directory is missing and if that's the case to generate the directory
+ if _, StatErr := os.Stat(path); os.IsNotExist(StatErr) {
+ MkdirErr := os.MkdirAll(path, os.ModeDir)
+ if MkdirErr != nil {
+ return MkdirErr
+ }
+ }
+
+ // 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)
+ if err != nil {
+ return err
+ }
+
+ // MultiWriter makes it possible to print log to both log File and console
+ mw := io.MultiWriter(os.Stdout, logFile)
+
+ // SetOutput tells the log where to write to
+ log.SetOutput(mw)
+
+ // dbImpl.Init() generates the needed tables if needed before the app starts
+ err = dbImpl.Init()
+ if err != nil {
+ return err
+ }
+
+ log.Println("DB Set Up")
+ return nil
+ },
+ // TODO add descriptions
+ Short: "",
+ Long: ``,
// Uncomment the following line if your bare application
// has an action associated with it:
RunE: func(cmd *cobra.Command, args []string) error {
diff --git a/deb.json b/deb.json
@@ -50,7 +50,7 @@
"description":"A Matrix client written in Go-QT ",
"generic-name":"Matrix Client",
"exec":"/usr/bin/Morpheusv2.sh",
- "icon":"pkg/qml/resources/logos/MorpheusBig.png",
+ "icon":"qml/resources/logos/MorpheusBig.png",
"type":"Application",
"keywords":"matrix;Morpheusv2",
"startup-notify": false,
diff --git a/main.go b/main.go
@@ -16,24 +16,18 @@ package main
import (
"github.com/Nordgedanken/Morpheusv2/cmd"
- "github.com/Nordgedanken/Morpheusv2/pkg"
- "runtime"
+ "os"
+ "os/signal"
+ "syscall"
)
-// Arrange that main.main runs on main thread.
-func init() {
- runtime.LockOSThread()
-}
+var c = make(chan os.Signal, 2)
func main() {
- go pkg.Do(cmd.Execute)
+ signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
- for {
- pkg.Do(noop)
- }
+ <-c
+ os.Exit(1)
}()
-
- pkg.Main()
+ cmd.Execute()
}
-
-func noop() {}
diff --git a/pkg/app/main.go b/pkg/app/main.go
@@ -15,28 +15,37 @@
package app
import (
- "github.com/shibukawa/configdir"
+ "github.com/Nordgedanken/Morpheusv2/pkg/mainUI"
+ "github.com/matrix-org/gomatrix"
"github.com/therecipe/qt/core"
"github.com/therecipe/qt/gui"
"github.com/therecipe/qt/widgets"
"log"
- "os"
- "path/filepath"
)
+var args []string
+var cli *gomatrix.Client
+var windowHeight = 600
+var windowWidth = 950
+var window *widgets.QMainWindow
+
// Start prepares the Main QT Window and opens it
-func Start(args []string) error {
+func Start(argsArg []string) error {
+ args = argsArg
log.Println("Starting Morpheus v2")
- // Init Logs and folders
- configDirs := configdir.New("Nordgedanken", "Morpheus")
- if _, StatErr := os.Stat(filepath.ToSlash(configDirs.QueryFolders(configdir.Global)[0].Path) + "/log/"); os.IsNotExist(StatErr) {
- MkdirErr := os.MkdirAll(filepath.ToSlash(configDirs.QueryFolders(configdir.Global)[0].Path)+"/log/", 0700)
- if MkdirErr != nil {
- return MkdirErr
- }
- }
+ initApp()
+
+ mainUIS := mainUI.NewMainUI(windowWidth, windowHeight, window)
+ SetNewWindow(mainUIS, window)
+
+ widgets.QApplication_Exec()
+ return nil
+}
+
+func initApp() {
+ log.Println("Create QApp")
app := widgets.NewQApplication(len(args), args)
app.SetAttribute(core.Qt__AA_UseHighDpiPixmaps, true)
@@ -44,22 +53,33 @@ func Start(args []string) error {
app.SetApplicationVersion("0.1.0")
appIcon := gui.NewQIcon5(":/qml/resources/logos/MorpheusBig.png")
app.SetWindowIcon(appIcon)
+ window = widgets.NewQMainWindow(nil, 0)
+ app.SetActiveWindow(window)
- window := widgets.NewQMainWindow(nil, 0)
-
- windowHeight := 600
- windowWidth := 950
-
- desktopApp := widgets.QApplication_Desktop()
+ desktopApp := app.Desktop()
primaryScreen := desktopApp.PrimaryScreen()
screen := desktopApp.Screen(primaryScreen)
windowX := (screen.Width() - windowHeight) / 2
windowY := (screen.Height() - windowWidth) / 2
window.Resize2(windowWidth, windowHeight)
- window.Show()
-
window.Move2(windowX, windowY)
+ app.ConnectQuit(func() {
+ log.Println("Morpheus closed")
+ })
+
+}
+
+// SetNewWindow loads the new UI into the QMainWindow
+func SetNewWindow(ui ui, window *widgets.QMainWindow) error {
+ ui.SetCli(cli)
+ uiErr := ui.NewUI()
+ if uiErr != nil {
+ return uiErr
+ }
+ ui.GetWidget().Resize2(windowWidth, windowHeight)
+ window.SetCentralWidget(ui.GetWidget())
+ window.Show()
return nil
}
diff --git a/pkg/app/ui.go b/pkg/app/ui.go
@@ -0,0 +1,12 @@
+package app
+
+import (
+ "github.com/matrix-org/gomatrix"
+ "github.com/therecipe/qt/widgets"
+)
+
+type ui interface {
+ SetCli(cli *gomatrix.Client)
+ GetWidget() (widget *widgets.QWidget)
+ NewUI() error
+}
diff --git a/pkg/db/db.go b/pkg/db/db.go
@@ -0,0 +1,14 @@
+package db
+
+import "github.com/Nordgedanken/Morpheusv2/pkg/matrix"
+
+// DB defines a Interface to allow multiple DB Implementations
+type DB interface {
+ SaveRoom(Room matrix.Room) error
+ GetRooms() (rooms map[string]matrix.Room, err error)
+ GetRoom(roomID string) (room matrix.Room, err error)
+
+ SaveUser(user matrix.User) error
+ GetUsers() (map[string]matrix.User, error)
+ GetCurrentUser() (matrix.User, error)
+}
diff --git a/pkg/db/implementation/db.go b/pkg/db/implementation/db.go
@@ -0,0 +1,51 @@
+package implementation
+
+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"
+)
+
+var db *sql.DB
+
+// Init prepares the DB by opening it and creating the required tables if needed
+// TODO: Make part of the interface
+func 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")
+ 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 integer not null primary key, display_name text, avatar text);
+ CREATE TABLE IF NOT EXISTS messages (id integer not null primary key, author text, message text, timestamp text, pure_event text);
+ CREATE TABLE IF NOT EXISTS rooms (id integer not null primary key, room_aliases text, room_id text, room_name text, room_avatar text, room_topic text, room_messages text);
+ `
+ _, execErr := 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 Open() *sql.DB {
+ if db != nil {
+ return db
+ }
+ return nil
+}
diff --git a/pkg/mainUI/mainUI.go b/pkg/mainUI/mainUI.go
@@ -0,0 +1,63 @@
+package mainUI
+
+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"
+)
+
+// MainUI defines the data for the main ui (that one with the chats)
+type MainUI struct {
+ widget *widgets.QWidget
+ cli *gomatrix.Client
+ window *widgets.QMainWindow
+ windowWidth int
+ windowHeight int
+}
+
+// NewMainUI gives you a MainUI struct with prefilled data
+func NewMainUI(windowWidth, windowHeight int, window *widgets.QMainWindow) (mainUI *MainUI) {
+ mainUI = &MainUI{
+ windowWidth: windowWidth,
+ windowHeight: windowHeight,
+ window: window,
+ }
+ return
+}
+
+// SetCli sets the gomatrix Client for the MainUI
+func (m *MainUI) SetCli(cli *gomatrix.Client) {
+ m.cli = cli
+}
+
+// GetWidget returns the QWidget of the MainUI
+func (m *MainUI) GetWidget() (widget *widgets.QWidget) {
+ return m.widget
+}
+
+// NewUI prepares the new UI
+func (m *MainUI) NewUI() error {
+ m.widget = widgets.NewQWidget(nil, 0)
+
+ var loader = uitools.NewQUiLoader(nil)
+ var file = core.NewQFile2(":/qml/ui/chat.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/matrix/dataTypes.go b/pkg/matrix/dataTypes.go
@@ -0,0 +1,38 @@
+package matrix
+
+import (
+ "github.com/matrix-org/gomatrix"
+ "time"
+)
+
+// User defines a Interface to allow multiple User type Implementations
+type User interface {
+ SetCli(cli *gomatrix.Client)
+ SetMXID(id string)
+ GetDisplayName(roomID string) (string, error)
+ GetAvatar(roomID string) (string, error)
+}
+
+// Room defines a Interface to allow multiple Room type Implementations
+type Room interface {
+ // Handled using global "own User"
+ //SetCli(cli *gomatrix.Client)
+ SetRoomID(id string)
+ SetRoomAliases(aliases map[int64]string)
+ GetName() (string, error)
+ GetAvatar() (string, error)
+ GetTopic() (string, error)
+ GetMessages() (map[string]Message, error)
+}
+
+// Message defines a Interface to allow multiple Message type Implementations
+type Message interface {
+ // Handled using global "own User"
+ //SetCli(cli *gomatrix.Client)
+ SetEventID(id string)
+ SetEvent(event *gomatrix.Event)
+ SetAuthorMXID(mxid string)
+ SetMessage(message string)
+ SetTimestamp(ts *time.Time)
+ Show() error
+}