Skip to content

Commit

Permalink
Rework reader usage and fix loading dropped file
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikKalkoken committed Jul 8, 2024
1 parent 41c5880 commit 76b2fc3
Show file tree
Hide file tree
Showing 10 changed files with 91 additions and 89 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ JSON Viewer is a desktop app for browsing large JSON files. It runs on Linux, Wi
- Single executable file, no installation required
- Desktop app that runs on Windows, Linux and macOS (experimental)
- Automatic dark and light mode
- Open JSON files from disk or clipboard
- JSON files can be opened via file dialog, from clipboard or drag-and-drop

## Screenshots

Expand Down
24 changes: 24 additions & 0 deletions internal/jsondocument/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package jsondocument

import (
"fmt"
"io"

"fyne.io/fyne/v2"
"fyne.io/fyne/v2/storage"
)

// Creates and returns an URIReadCloser from a reader.
func MakeURIReadCloser(r io.Reader, name string) uriReadCloser {
return uriReadCloser{io.NopCloser(r), name}
}

type uriReadCloser struct {
io.ReadCloser
name string
}

func (u uriReadCloser) URI() fyne.URI {
uri, _ := storage.ParseURI(fmt.Sprintf("dummy://dummy/%s", u.name))
return uri
}
15 changes: 15 additions & 0 deletions internal/jsondocument/helper_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package jsondocument_test

import (
"strings"
"testing"

"github.com/ErikKalkoken/jsonviewer/internal/jsondocument"
"github.com/stretchr/testify/assert"
)

func TestHelper(t *testing.T) {
r := strings.NewReader("test")
r2 := jsondocument.MakeURIReadCloser(r, "alpha")
assert.Equal(t, "alpha", r2.URI().Name())
}
14 changes: 8 additions & 6 deletions internal/jsondocument/jsondocument.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strconv"
"sync"

"fyne.io/fyne/v2"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/widget"
)
Expand Down Expand Up @@ -128,7 +129,8 @@ func (t *JSONDocument) Value(uid widget.TreeNodeID) Node {

// Load loads JSON data from a reader and builds a new JSON document from it.
// It reports it's current progress to the caller via updates to progressInfo.
func (t *JSONDocument) Load(reader io.Reader, progressInfo binding.Untyped) error {
func (t *JSONDocument) Load(reader fyne.URIReadCloser, progressInfo binding.Untyped) error {
defer reader.Close()
t.progressInfo = progressInfo
byt, err := t.loadFile(reader)
if err != nil {
Expand Down Expand Up @@ -291,15 +293,15 @@ func (t *JSONDocument) setProgressInfo(info ProgressInfo) error {
return nil
}

func (t *JSONDocument) loadFile(reader io.Reader) ([]byte, error) {
func (t *JSONDocument) loadFile(reader fyne.URIReadCloser) ([]byte, error) {
if err := t.setProgressInfo(ProgressInfo{CurrentStep: 1}); err != nil {
return nil, err
}
dat, err := io.ReadAll(reader)
if err != nil {
return nil, fmt.Errorf("failed to read file: %s", err)
return nil, fmt.Errorf("failed to read file %s: %s", reader.URI(), err)
}
slog.Info("Read file")
slog.Info("Read file", "uri", reader.URI())
return dat, nil
}

Expand All @@ -309,9 +311,9 @@ func (t *JSONDocument) parseFile(dat []byte) (any, error) {
}
var data any
if err := json.Unmarshal(dat, &data); err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON: %s", err)
return nil, fmt.Errorf("failed to unmarshal data: %s", err)
}
slog.Info("Unmarshaled JSON file")
slog.Info("Completed un-marshaling data")
return data, nil
}

Expand Down
4 changes: 2 additions & 2 deletions internal/jsondocument/jsondocument_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func TestLoadFile(t *testing.T) {
if err != nil {
t.Fatal(err)
}
r := bytes.NewReader(dat)
r := MakeURIReadCloser(bytes.NewReader(dat), "test")
// when
byt, err := j.loadFile(r)
if assert.NoError(t, err) {
Expand All @@ -31,7 +31,7 @@ func TestLoadFile(t *testing.T) {
})
t.Run("should return error when stream can not be unmarshaled", func(t *testing.T) {
// given
r := strings.NewReader("invalid JSON")
r := MakeURIReadCloser(strings.NewReader("invalid JSON"), "test")
// when
byt, err := j.loadFile(r)
if assert.NoError(t, err) {
Expand Down
7 changes: 3 additions & 4 deletions internal/jsondocument/jsondocument_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package jsondocument_test
import (
"bytes"
"encoding/json"
"io"
"testing"

"fyne.io/fyne/v2"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/widget"
"github.com/ErikKalkoken/jsonviewer/internal/jsondocument"
Expand Down Expand Up @@ -131,12 +131,11 @@ func TestJsonDocumentLoad(t *testing.T) {
})
}

func makeDataReader(data any) io.Reader {
func makeDataReader(data any) fyne.URIReadCloser {
x, err := json.Marshal(data)
if err != nil {
panic(err)
}
r := bytes.NewReader(x)
return r

return jsondocument.MakeURIReadCloser(r, "test")
}
19 changes: 0 additions & 19 deletions internal/ui/helper.go

This file was deleted.

78 changes: 30 additions & 48 deletions internal/ui/menu.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,31 +31,27 @@ func (u *UI) makeMenu() *fyne.MainMenu {
if reader == nil {
return
}
defer reader.Close()
if err := u.loadDocument(reader); err != nil {
u.showErrorDialog("Failed to load document", err)
return
}
u.loadDocument(reader)
}, u.window)
dialogOpen.Show()
}),
recentItem,
fyne.NewMenuItem("Open From Clipboard", func() {
r := strings.NewReader(u.window.Clipboard().Content())
reader := makeURIReadCloser(r)
if err := u.loadDocument(reader); err != nil {
u.showErrorDialog("Failed to load JSON document from clipboard", err)
return
}
reader := jsondocument.MakeURIReadCloser(r, "CLIPBOARD")
u.loadDocument(reader)
}),
fyne.NewMenuItemSeparator(),
fyne.NewMenuItem("Reload", func() {
if u.currentFile == nil {
return
}
if err := u.loadURI(u.currentFile); err != nil {
reader, err := storage.Reader(u.currentFile)
if err != nil {
u.showErrorDialog("Failed to reload file", err)
return
}
u.loadDocument(reader)
}),
)
viewMenu := fyne.NewMenu("View",
Expand Down Expand Up @@ -122,30 +118,16 @@ func (u *UI) updateRecentFilesMenu() {
dialog.ShowError(err, u.window)
return
}
defer reader.Close()
if err := u.loadDocument(reader); err != nil {
dialog.ShowError(err, u.window)
return
}
u.loadDocument(reader)
})
}
u.fileMenu.Items[1].ChildMenu.Items = items
u.fileMenu.Refresh()
}

func (u *UI) loadURI(uri fyne.URI) error {
reader, err := storage.Reader(uri)
if err != nil {
return err
}
if err := u.loadDocument(reader); err != nil {
return err
}
return nil
}

func (u *UI) loadDocument(reader fyne.URIReadCloser) error {
defer reader.Close()
// loadDocument loads a JSON file
// Shows a loader modal while loading
func (u *UI) loadDocument(reader fyne.URIReadCloser) {
infoText := widget.NewLabel("")
pb1 := widget.NewProgressBarInfinite()
pb2 := widget.NewProgressBar()
Expand Down Expand Up @@ -187,25 +169,25 @@ func (u *UI) loadDocument(reader fyne.URIReadCloser) error {
c := container.NewVBox(infoText, container.NewStack(pb1, pb2))
d2 := dialog.NewCustomWithoutButtons("Loading", c, u.window)
d2.Show()
defer d2.Hide()
if err := u.document.Load(reader, progressInfo); err != nil {
return err
}
p := message.NewPrinter(language.English)
out := p.Sprintf("%d elements", u.document.Size())
u.statusTreeSize.SetText(out)
u.welcomeMessage.Hide()
u.treeWidget.Refresh()
u.detailCopyValue.Show()
x := reader.URI()
if x != nil {
u.setTitle(x.Name())
u.addRecentFile(x)
} else {
u.setTitle("CLIPBOARD")
}
u.currentFile = x
return nil
go func() {
defer d2.Hide()
if err := u.document.Load(reader, progressInfo); err != nil {
u.showErrorDialog("Failed to load document", err)
return
}
p := message.NewPrinter(language.English)
out := p.Sprintf("%d elements", u.document.Size())
u.statusTreeSize.SetText(out)
u.welcomeMessage.Hide()
u.treeWidget.Refresh()
u.detailCopyValue.Show()
uri := reader.URI()
if uri.Scheme() == "file" {
u.addRecentFile(uri)
}
u.setTitle(uri.Name())
u.currentFile = uri
}()
}

func (u *UI) showAboutDialog() {
Expand Down
16 changes: 7 additions & 9 deletions internal/ui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,20 +104,18 @@ func NewUI() (*UI, error) {
u.window.SetMainMenu(u.makeMenu())
u.updateRecentFilesMenu()
u.window.SetMaster()
u.window.SetOnDropped(func(p fyne.Position, uri []fyne.URI) {
if len(uri) < 1 {
u.window.SetOnDropped(func(p fyne.Position, uris []fyne.URI) {
if len(uris) < 1 {
return
}
x := uri[0].String()
x2, err := storage.ParseURI(x)
uri := uris[0]
slog.Info("Loading dropped file", "uri", uri)
reader, err := storage.Reader(uri)
if err != nil {
slog.Error("Failed to identify dropped file", "err", err)
u.showErrorDialog(fmt.Sprintf("Failed to load file: %s", uri), err)
return
}
slog.Info("Loading dropped file", "uri", x2)
if err := u.loadURI(x2); err != nil {
u.showErrorDialog("Failed to load file", err)
}
u.loadDocument(reader)
})
s := fyne.Size{
Width: float32(a.Preferences().FloatWithFallback(settingWindowWidth, 800)),
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func main() {
levelFlag := logLevelFlag{value: slog.LevelWarn}
flag.Var(&levelFlag, "loglevel", "set log level")
flag.Parse()
log.SetFlags(log.LstdFlags | log.Llongfile)
slog.SetLogLoggerLevel(levelFlag.value)
u, err := ui.NewUI()
if err != nil {
Expand Down

0 comments on commit 76b2fc3

Please sign in to comment.