Skip to content

Commit

Permalink
Add support for switching note folders (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
pbek committed Jul 8, 2022
1 parent fae4cb8 commit d8ac0e3
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 11 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# QOwnNotes command-line snippet manager changelog

## v0.4.0
- Add support for note folder switching (for [#5](https://github.com/qownnotes/qc/issues/5))
- Use `qc switch` to get a list of note folders to select which note folder to switch to
- Use `qc switch -f <note-folder-id>` to make QOwnNotes switch to another note folder instantly
- Needs OwnNotes at least at version 22.7.1

## v0.3.2
- Add Homebrew tap for qc (`brew install qownnotes/qc/qc`)

Expand Down
73 changes: 73 additions & 0 deletions cmd/switch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package cmd

import (
"bufio"
"fmt"
"github.com/qownnotes/qc/config"
"github.com/qownnotes/qc/websocket"
"github.com/spf13/cobra"
"os"
"strconv"
)

// switchCmd represents the switch command
var switchCmd = &cobra.Command{
Use: "switch",
Short: "Switch note folder",
Long: `Switch to a different note folder`,
RunE: switchNoteFolder,
}

func switchNoteFolder(cmd *cobra.Command, args []string) (err error) {
flag := config.Flag

if flag.Query != "" {
id, _ := strconv.Atoi(flag.Query)
fmt.Printf("Attempting to switch to note folder number %d!\n", id)
websocket.SwitchNoteFolder(id)

return nil
}

noteFolderData, currentId := websocket.FetchNoteFolderData()

for _, noteFolder := range noteFolderData {
currentText := ""

if currentId == noteFolder.Id {
currentText = "*"
}

fmt.Printf("%d%s) %s\n", noteFolder.Id, currentText, noteFolder.Name)
}

fmt.Print("\nSelect note folder to switch to: ")

scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()

if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "Error reading standard input:", err)

return nil
}

id, _ := strconv.Atoi(scanner.Text())

for _, noteFolder := range noteFolderData {
if noteFolder.Id == id {
websocket.SwitchNoteFolder(id)
return nil
}
}

fmt.Printf("Could not find note folder number %d!\n\n", id)

return switchNoteFolder(cmd, args)
}

func init() {
RootCmd.AddCommand(switchCmd)
switchCmd.Flags().StringVarP(&config.Flag.Query, "folder", "f", "",
`Note folder id to switch to`)
}
13 changes: 9 additions & 4 deletions entity/entity.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package entity

type SnippetInfo struct {
Description string `json:"description"`
Command string `json:"command"`
Tag []string `json:"tags"`
Output string `json:"output"`
Description string `json:"description"`
Command string `json:"command"`
Tag []string `json:"tags"`
Output string `json:"output"`
}

type NoteFolderInfo struct {
Name string `json:"text"`
Id int `json:"value"`
}
134 changes: 127 additions & 7 deletions websocket/websocket.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,26 @@ type Message struct {
Type string `json:"type"`
}

type NoteFolderSwitchMessage struct {
Token string `json:"token"`
Type string `json:"type"`
Data int `json:"data"`
}

type ResultMessage struct {
Type string `json:"type"`
Type string `json:"type"`
CommandSnippets []entity.SnippetInfo `json:"data"`
// Data string `json:"data"`
}

type NoteFolderResultMessage struct {
Type string `json:"type"`
NoteFolders []entity.NoteFolderInfo `json:"data"`
CurrentId int `json:"currentId"`
}

type NoteFolderSwitchResultMessage struct {
Type string `json:"type"`
Switched bool `json:"data"`
}

// type CommandSnippet struct {
Expand All @@ -53,19 +69,18 @@ const (
)

func FetchSnippetsData() []entity.SnippetInfo {
u := url.URL{Scheme: "ws", Host: "127.0.0.1:" + strconv.Itoa(config.Conf.QOwnNotes.WebSocketPort)}
//log.Printf("Connecting to QOwnNotes on %s", u.String())
// log.Printf("Connecting to QOwnNotes on %s", u.String())

initSnippetCacheFile()
var snippetData []byte = nil

c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
c, err := connectSocket()
if err != nil {
snippetData = readSnippetCacheFile()

if snippetData == nil {
log.Fatal("Couldn't connect to QOwnNotes websocket, did you enable the socket server? Error: ", err)
} else {
} else {
log.Println("Couldn't connect to QOwnNotes websocket, but found cached data in " + snippetCacheFile)
}
}
Expand Down Expand Up @@ -103,7 +118,7 @@ func FetchSnippetsData() []entity.SnippetInfo {
case "commandSnippets":
writeSnippetCacheFile(snippetData)

//log.Printf("CommandSnippets: %v", resultMessage.CommandSnippets)
// log.Printf("CommandSnippets: %v", resultMessage.CommandSnippets)
return resultMessage.CommandSnippets
default:
log.Fatal("Did not understand response from QOwnNotes!")
Expand All @@ -112,6 +127,111 @@ func FetchSnippetsData() []entity.SnippetInfo {
return []entity.SnippetInfo{}
}

func FetchNoteFolderData() (noteFolderInfo []entity.NoteFolderInfo, currentId int) {
var noteFolderData []byte = nil

c, err := connectSocket()
if err != nil {
log.Fatal("Couldn't connect to QOwnNotes websocket, did you enable the socket server? Error: ", err)
}

defer c.Close()

message := Message{
Token: config.Conf.QOwnNotes.Token,
Type: "getNoteFolders",
}

m, err := json.Marshal(message)

err = c.WriteMessage(websocket.TextMessage, m)
if err != nil {
log.Fatal("Couldn't send command to QOwnNotes: ", err)
}

_, noteFolderData, err = c.ReadMessage()
if err != nil {
log.Fatalf("Couldn't read message from QOwnNotes: %v", err)
}

var resultMessage NoteFolderResultMessage
// log.Printf("Connecting to QOwnNotes on vs", noteFolderData)
err = json.Unmarshal(noteFolderData, &resultMessage)
if err != nil {
log.Fatalf("Couldn't interpret message from QOwnNotes: %v\nYou need at least QOwnNotes 22.7.1!", err)
}

switch resultMessage.Type {
case "tokenQuery":
log.Fatal("Please execute \"qc configure\" and configure your token from QOwnNotes!")
case "noteFolders":
// log.Printf("NoteFolders: %v", resultMessage.NoteFolders)
return resultMessage.NoteFolders, resultMessage.CurrentId
default:
log.Fatal("Did not understand response from QOwnNotes!")
}

return []entity.NoteFolderInfo{}, 0
}

func SwitchNoteFolder(id int) {
var noteFolderData []byte = nil
c, err := connectSocket()
if err != nil {
log.Fatal("Couldn't connect to QOwnNotes websocket, did you enable the socket server? Error: ", err)
}

defer c.Close()

message := NoteFolderSwitchMessage{
Token: config.Conf.QOwnNotes.Token,
Type: "switchNoteFolder",
Data: id,
}

m, err := json.Marshal(message)

err = c.WriteMessage(websocket.TextMessage, m)
if err != nil {
log.Fatal("Couldn't send command to QOwnNotes: ", err)
}

_, noteFolderData, err = c.ReadMessage()
if err != nil {
log.Fatalf("Couldn't read message from QOwnNotes: %v", err)
}

var resultMessage NoteFolderSwitchResultMessage
err = json.Unmarshal(noteFolderData, &resultMessage)
if err != nil {
log.Fatalf("Couldn't interpret message from QOwnNotes: %v\nYou need at least QOwnNotes 22.7.1!", err)
}

switch resultMessage.Type {
case "tokenQuery":
log.Fatal("Please execute \"qc configure\" and configure your token from QOwnNotes!")
case "switchedNoteFolder":
if resultMessage.Switched {
fmt.Println("Note folder was switched.")
} else {
fmt.Println("Note folder was not switched.")
}
default:
log.Fatal("Did not understand response from QOwnNotes!")
}
}

func connectSocket() (*websocket.Conn, error) {
u := getSocketUrl()
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
return c, err
}

func getSocketUrl() url.URL {
u := url.URL{Scheme: "ws", Host: "127.0.0.1:" + strconv.Itoa(config.Conf.QOwnNotes.WebSocketPort)}
return u
}

func initSnippetCacheFile() {
if snippetCacheFile == "" {
dir, err := config.GetDefaultConfigDir()
Expand Down

0 comments on commit d8ac0e3

Please sign in to comment.