From 911be1ba000e988834a98c8c981a3b569755eb06 Mon Sep 17 00:00:00 2001 From: sqshq Date: Mon, 20 May 2019 00:12:40 -0400 Subject: [PATCH] add intro page and storage initialization --- client/backend.go | 8 ++ component/alert.go | 11 +-- component/intro.go | 139 +++++++++++++++++++++++++++++++ component/layout/layout.go | 68 ++++++++++----- component/menu.go | 33 +++----- component/textbox/textbox.go | 3 + component/util/format.go | 12 +++ component/{ => util}/geometry.go | 8 +- config/config.go | 4 +- data/item.go | 2 +- data/sampler.go | 4 +- main.go | 19 ++++- storage/license.go | 53 ++++++++++++ storage/stats.go | 6 ++ storage/storage.go | 56 +++++++++++++ 15 files changed, 366 insertions(+), 60 deletions(-) create mode 100644 client/backend.go create mode 100644 component/intro.go rename component/{ => util}/geometry.go (76%) create mode 100644 storage/license.go create mode 100644 storage/stats.go create mode 100644 storage/storage.go diff --git a/client/backend.go b/client/backend.go new file mode 100644 index 0000000..8aceb49 --- /dev/null +++ b/client/backend.go @@ -0,0 +1,8 @@ +package client + +import "github.com/sqshq/sampler/storage" + +// TODO +func loadLicense(key string) *storage.License { + return nil +} diff --git a/component/alert.go b/component/alert.go index 4fe9aae..a4e5508 100644 --- a/component/alert.go +++ b/component/alert.go @@ -44,7 +44,7 @@ func RenderAlert(alert *data.Alert, area image.Rectangle, buffer *ui.Buffer) { } block := *ui.NewBlock() - block.SetRect(getRectCoordinates(area, width, len(lines))) + block.SetRect(util.GetRectCoordinates(area, width, len(lines))) block.BorderStyle = ui.Style{Fg: color, Bg: ui.ColorClear} block.Draw(buffer) @@ -52,13 +52,6 @@ func RenderAlert(alert *data.Alert, area image.Rectangle, buffer *ui.Buffer) { for i := 0; i < len(lines); i++ { buffer.SetString(lines[i], - ui.NewStyle(color), getMiddlePoint(block.Inner, lines[i], i-1)) + ui.NewStyle(color), util.GetMiddlePoint(block.Inner, lines[i], i-1)) } } - -// TODO move to utils -func getRectCoordinates(area image.Rectangle, width int, height int) (int, int, int, int) { - x1 := area.Min.X + area.Dx()/2 - width/2 - y1 := area.Min.Y + area.Dy()/2 - height - return x1, y1, x1 + width, y1 + height + 2 -} diff --git a/component/intro.go b/component/intro.go new file mode 100644 index 0000000..4e4d404 --- /dev/null +++ b/component/intro.go @@ -0,0 +1,139 @@ +package component + +import ( + ui "github.com/gizak/termui/v3" + "github.com/sqshq/sampler/component/util" + "github.com/sqshq/sampler/console" +) + +type Intro struct { + *ui.Block + page IntroPage + option introOption + palette console.Palette +} + +type IntroPage rune + +const ( + IntroPageWelcome IntroPage = 0 + IntroPageCommercial IntroPage = 1 + IntroPagePersonal IntroPage = 2 +) + +type introOption rune + +const ( + introOptionCommercial introOption = 0 + introOptionPersonal introOption = 1 +) + +const ( + buttonCommercial string = " COMMERCIAL USE " + buttonPersonal string = " PERSONAL USE " + buttonOk string = " OK " +) + +func (intro *Intro) Up() { + intro.option = introOptionCommercial +} + +func (intro *Intro) Down() { + intro.option = introOptionPersonal +} + +func (intro *Intro) NextPage() { + if intro.option == introOptionCommercial { + intro.page = IntroPageCommercial + } else { + intro.page = IntroPagePersonal + } +} + +func (intro *Intro) GetSelectedPage() IntroPage { + return intro.page +} + +func NewIntro(palette console.Palette) *Intro { + return &Intro{ + Block: NewBlock("", false, palette), + palette: palette, + } +} + +func (intro *Intro) Draw(buffer *ui.Buffer) { + + logo := []string{ + " __ ", + " _________ ____ ___ ____ / /__ _____", + " / ___/ __ `/ __ `__ \\/ __ \\/ / _ \\/ ___/", + " (__ ) /_/ / / / / / / /_/ / / __/ / ", + "/____/\\__,_/_/ /_/ /_/ .___/_/\\___/_/ ", + " /_/ ", + } + + introText := append(logo, []string{ + "", "", "", + "Welcome.", + "Sampler is free of charge for personal use, but license must be purchased to use it for business purposes.", + "Clicking below indicates you agree to the terms of the license agreement and privacy policy: www.sampler.dev/license", + "", "", "", + "How do you plan to use Sampler?", + }...) + + commericalText := append(logo, []string{ + "", "", "", "", + "Please visit www.sampler.dev to purchase a license and then start Sampler with --license flag", + }...) + + personalText := append(logo, []string{ + "", "", "", "", + "Sampler is always free for non-commercial use, but you can support the project and buy a personal license:", + "www.sampler.dev", + }...) + + text := introText + + switch intro.page { + case IntroPageWelcome: + text = introText + case IntroPageCommercial: + text = commericalText + case IntroPagePersonal: + text = personalText + } + + for i, a := range text { + util.PrintString( + a, + ui.NewStyle(intro.palette.BaseColor), + util.GetMiddlePoint(intro.Block.Rectangle, a, i-15), + buffer) + } + + highlightedStyle := ui.NewStyle(intro.palette.ReverseColor, intro.palette.BaseColor) + regularStyle := ui.NewStyle(intro.palette.BaseColor, intro.palette.ReverseColor) + + if intro.page == IntroPageWelcome { + + commercialButtonStyle := highlightedStyle + if intro.option == introOptionPersonal { + commercialButtonStyle = regularStyle + } + + personalButtonStyle := highlightedStyle + if intro.option == introOptionCommercial { + personalButtonStyle = regularStyle + } + + buffer.SetString(string(buttonCommercial), commercialButtonStyle, + util.GetMiddlePoint(intro.Block.Rectangle, string(buttonCommercial), 6)) + buffer.SetString(string(buttonPersonal), personalButtonStyle, + util.GetMiddlePoint(intro.Block.Rectangle, string(buttonPersonal), 8)) + } else { + buffer.SetString(string(buttonOk), highlightedStyle, + util.GetMiddlePoint(intro.Block.Rectangle, string(buttonOk), 4)) + } + + intro.Block.Draw(buffer) +} diff --git a/component/layout/layout.go b/component/layout/layout.go index 1c1fdbc..6e5f6b0 100644 --- a/component/layout/layout.go +++ b/component/layout/layout.go @@ -4,6 +4,7 @@ import ( ui "github.com/gizak/termui/v3" "github.com/sqshq/sampler/component" "github.com/sqshq/sampler/component/runchart" + "github.com/sqshq/sampler/component/util" "github.com/sqshq/sampler/config" "github.com/sqshq/sampler/console" "github.com/sqshq/sampler/data" @@ -16,6 +17,7 @@ type Layout struct { Components []*component.Component statusbar *component.StatusBar menu *component.Menu + intro *component.Intro ChangeModeEvents chan Mode mode Mode selection int @@ -25,12 +27,13 @@ type Mode rune const ( ModeDefault Mode = 0 - ModePause Mode = 1 - ModeComponentSelect Mode = 2 - ModeMenuOptionSelect Mode = 3 - ModeComponentMove Mode = 4 - ModeComponentResize Mode = 5 - ModeChartPinpoint Mode = 6 + ModeIntro Mode = 1 + ModePause Mode = 2 + ModeComponentSelect Mode = 3 + ModeMenuOptionSelect Mode = 4 + ModeComponentMove Mode = 5 + ModeComponentResize Mode = 6 + ModeChartPinpoint Mode = 7 ) const ( @@ -38,10 +41,11 @@ const ( statusbarHeight = 1 ) -func NewLayout(width, height int, statusline *component.StatusBar, menu *component.Menu) *Layout { +func NewLayout(width, height int, statusline *component.StatusBar, menu *component.Menu, intro *component.Intro) *Layout { block := *ui.NewBlock() block.SetRect(0, 0, width, height) + intro.SetRect(0, 0, width, height) statusline.SetRect(0, height-statusbarHeight, width, height) return &Layout{ @@ -49,6 +53,7 @@ func NewLayout(width, height int, statusline *component.StatusBar, menu *compone Components: make([]*component.Component, 0), statusbar: statusline, menu: menu, + intro: intro, mode: ModeDefault, selection: 0, ChangeModeEvents: make(chan Mode, 10), @@ -59,12 +64,19 @@ func (l *Layout) AddComponent(cpt *component.Component) { l.Components = append(l.Components, cpt) } +func (l *Layout) RunIntro() { + l.mode = ModeIntro +} + func (l *Layout) changeMode(m Mode) { l.mode = m l.ChangeModeEvents <- m } func (l *Layout) HandleMouseClick(x int, y int) { + if l.mode == ModeIntro { + return + } l.menu.Idle() selected, i := l.findComponentAtPoint(image.Point{X: x, Y: y}) if selected == nil { @@ -118,6 +130,14 @@ func (l *Layout) HandleKeyboardEvent(e string) { case ModeComponentResize: l.menu.Idle() l.changeMode(ModeDefault) + break + case ModeIntro: + page := l.intro.GetSelectedPage() + if page == component.IntroPageWelcome { + l.intro.NextPage() + } else { + l.changeMode(ModeDefault) + } } case console.KeyEsc: l.resetAlerts() @@ -180,6 +200,8 @@ func (l *Layout) HandleKeyboardEvent(e string) { selected.Move(0, -1) case ModeComponentResize: selected.Resize(0, -1) + case ModeIntro: + l.intro.Up() } case console.KeyDown: switch l.mode { @@ -195,6 +217,8 @@ func (l *Layout) HandleKeyboardEvent(e string) { selected.Move(0, 1) case ModeComponentResize: selected.Resize(0, 1) + case ModeIntro: + l.intro.Down() } } } @@ -232,21 +256,21 @@ func (l *Layout) moveSelection(direction string) { switch direction { case console.KeyLeft: - previouslySelectedCornerPoint = component.GetRectLeftSideCenter(previouslySelected.GetRect()) - newlySelectedCornerPoint = component.GetRectRightSideCenter(l.getComponent(newlySelectedIndex).GetRect()) - currentCornerPoint = component.GetRectRightSideCenter(current.GetRect()) + previouslySelectedCornerPoint = util.GetRectLeftSideCenter(previouslySelected.GetRect()) + newlySelectedCornerPoint = util.GetRectRightSideCenter(l.getComponent(newlySelectedIndex).GetRect()) + currentCornerPoint = util.GetRectRightSideCenter(current.GetRect()) case console.KeyRight: - previouslySelectedCornerPoint = component.GetRectRightSideCenter(previouslySelected.GetRect()) - newlySelectedCornerPoint = component.GetRectLeftSideCenter(l.getComponent(newlySelectedIndex).GetRect()) - currentCornerPoint = component.GetRectLeftSideCenter(current.GetRect()) + previouslySelectedCornerPoint = util.GetRectRightSideCenter(previouslySelected.GetRect()) + newlySelectedCornerPoint = util.GetRectLeftSideCenter(l.getComponent(newlySelectedIndex).GetRect()) + currentCornerPoint = util.GetRectLeftSideCenter(current.GetRect()) case console.KeyUp: - previouslySelectedCornerPoint = component.GetRectTopSideCenter(previouslySelected.GetRect()) - newlySelectedCornerPoint = component.GetRectBottomSideCenter(l.getComponent(newlySelectedIndex).GetRect()) - currentCornerPoint = component.GetRectBottomSideCenter(current.GetRect()) + previouslySelectedCornerPoint = util.GetRectTopSideCenter(previouslySelected.GetRect()) + newlySelectedCornerPoint = util.GetRectBottomSideCenter(l.getComponent(newlySelectedIndex).GetRect()) + currentCornerPoint = util.GetRectBottomSideCenter(current.GetRect()) case console.KeyDown: - previouslySelectedCornerPoint = component.GetRectBottomSideCenter(previouslySelected.GetRect()) - newlySelectedCornerPoint = component.GetRectTopSideCenter(l.getComponent(newlySelectedIndex).GetRect()) - currentCornerPoint = component.GetRectTopSideCenter(current.GetRect()) + previouslySelectedCornerPoint = util.GetRectBottomSideCenter(previouslySelected.GetRect()) + newlySelectedCornerPoint = util.GetRectTopSideCenter(l.getComponent(newlySelectedIndex).GetRect()) + currentCornerPoint = util.GetRectTopSideCenter(current.GetRect()) } switch direction { @@ -279,6 +303,12 @@ func (l *Layout) Draw(buffer *ui.Buffer) { columnWidth := float64(l.GetRect().Dx()) / float64(console.ColumnsCount) rowHeight := float64(l.GetRect().Dy()-statusbarHeight) / float64(console.RowsCount) + if l.mode == ModeIntro { + l.intro.SetRect(l.Min.X, l.Min.Y, l.Max.X, l.Max.Y) + l.intro.Draw(buffer) + return + } + for _, c := range l.Components { rectangle := calculateComponentCoordinates(c, columnWidth, rowHeight) c.SetRect(rectangle.Min.X, rectangle.Min.Y, rectangle.Max.X, rectangle.Max.Y) diff --git a/component/menu.go b/component/menu.go index ecc6e49..4c611c9 100644 --- a/component/menu.go +++ b/component/menu.go @@ -2,6 +2,7 @@ package component import ( ui "github.com/gizak/termui/v3" + "github.com/sqshq/sampler/component/util" "github.com/sqshq/sampler/config" "github.com/sqshq/sampler/console" "image" @@ -130,14 +131,14 @@ func (m *Menu) renderHighlight(buffer *ui.Buffer) { buffer.SetString( optionsText, ui.NewStyle(console.ColorDarkGrey), - getMiddlePoint(m.Block.Rectangle, optionsText, -1), + util.GetMiddlePoint(m.Block.Rectangle, optionsText, -1), ) return } m.printAllDirectionsArrowSign(buffer, -2) - arrowsTextPoint := getMiddlePoint(m.Block.Rectangle, arrowsText, 2) + arrowsTextPoint := util.GetMiddlePoint(m.Block.Rectangle, arrowsText, 2) if arrowsTextPoint.Y+1 < m.Inner.Max.Y { buffer.SetString( arrowsText, @@ -146,16 +147,16 @@ func (m *Menu) renderHighlight(buffer *ui.Buffer) { ) } - optionsTextPoint := getMiddlePoint(m.Block.Rectangle, optionsText, 3) + optionsTextPoint := util.GetMiddlePoint(m.Block.Rectangle, optionsText, 3) if optionsTextPoint.Y+1 < m.Inner.Max.Y { buffer.SetString( optionsText, ui.NewStyle(console.ColorDarkGrey), - getMiddlePoint(m.Block.Rectangle, optionsText, 3), + util.GetMiddlePoint(m.Block.Rectangle, optionsText, 3), ) } - resumeTextPoint := getMiddlePoint(m.Block.Rectangle, resumeText, 4) + resumeTextPoint := util.GetMiddlePoint(m.Block.Rectangle, resumeText, 4) if resumeTextPoint.Y+1 < m.Inner.Max.Y { buffer.SetString( resumeText, @@ -170,12 +171,12 @@ func (m *Menu) renderMoveAndResize(buffer *ui.Buffer) { saveText := " to save changes" if m.Dy() <= minimalMenuHeight { - buffer.SetString(saveText, ui.NewStyle(console.ColorDarkGrey), getMiddlePoint(m.Block.Rectangle, saveText, -1)) + buffer.SetString(saveText, ui.NewStyle(console.ColorDarkGrey), util.GetMiddlePoint(m.Block.Rectangle, saveText, -1)) return } m.printAllDirectionsArrowSign(buffer, -1) - buffer.SetString(saveText, ui.NewStyle(console.ColorDarkGrey), getMiddlePoint(m.Block.Rectangle, saveText, 3)) + buffer.SetString(saveText, ui.NewStyle(console.ColorDarkGrey), util.GetMiddlePoint(m.Block.Rectangle, saveText, 3)) } func (m *Menu) printAllDirectionsArrowSign(buffer *ui.Buffer, y int) { @@ -187,10 +188,10 @@ func (m *Menu) printAllDirectionsArrowSign(buffer *ui.Buffer, y int) { } for i, a := range arrows { - printString( + util.PrintString( a, ui.NewStyle(console.ColorOlive), - getMiddlePoint(m.Block.Rectangle, a, i+y), + util.GetMiddlePoint(m.Block.Rectangle, a, i+y), buffer, ) } @@ -211,7 +212,7 @@ func (m *Menu) renderOptions(buffer *ui.Buffer) { if option != MenuOptionPinpoint || m.component.Type == config.TypeRunChart { offset += 2 - point := getMiddlePoint(m.Block.Rectangle, string(option), offset-6) + point := util.GetMiddlePoint(m.Block.Rectangle, string(option), offset-6) buffer.SetString(string(option), style, point) } } @@ -239,15 +240,3 @@ func (m *Menu) drawInnerBorder(buffer *ui.Buffer) { buffer.SetCell(ui.Cell{ui.BOTTOM_LEFT, m.BorderStyle}, image.Pt(m.Min.X+2, m.Max.Y-2)) buffer.SetCell(ui.Cell{ui.BOTTOM_RIGHT, m.BorderStyle}, image.Pt(m.Max.X-3, m.Max.Y-2)) } - -// TODO move to utils -func getMiddlePoint(rectangle image.Rectangle, text string, offset int) image.Point { - return image.Pt(rectangle.Min.X+rectangle.Dx()/2-len(text)/2, rectangle.Max.Y-rectangle.Dy()/2+offset) -} - -// TODO move to utils -func printString(s string, style ui.Style, p image.Point, buffer *ui.Buffer) { - for i, char := range s { - buffer.SetCell(ui.Cell{Rune: char, Style: style}, image.Pt(p.X+i, p.Y)) - } -} diff --git a/component/textbox/textbox.go b/component/textbox/textbox.go index 306490e..78e3594 100644 --- a/component/textbox/textbox.go +++ b/component/textbox/textbox.go @@ -15,6 +15,7 @@ type TextBox struct { alert *data.Alert text string border bool + style ui.Style } func NewTextBox(c config.TextBoxConfig, palette console.Palette) *TextBox { @@ -22,6 +23,7 @@ func NewTextBox(c config.TextBoxConfig, palette console.Palette) *TextBox { box := TextBox{ Block: component.NewBlock(c.Title, *c.Border, palette), Consumer: data.NewConsumer(), + style: ui.NewStyle(palette.BaseColor), } go func() { @@ -54,6 +56,7 @@ func (t *TextBox) Draw(buffer *ui.Buffer) { row = ui.TrimCells(row, t.Inner.Dx()-2) for _, cx := range ui.BuildCellWithXArray(row) { x, cell := cx.X, cx.Cell + cell.Style = t.style buffer.SetCell(cell, image.Pt(x+1, y+1).Add(t.Inner.Min)) } } diff --git a/component/util/format.go b/component/util/format.go index 22aca80..1cec09c 100644 --- a/component/util/format.go +++ b/component/util/format.go @@ -2,6 +2,8 @@ package util import ( "bytes" + ui "github.com/gizak/termui/v3" + "image" "math" "strconv" "strings" @@ -74,3 +76,13 @@ func formatTrailingDigits(value string, scale int) string { return value } + +func GetMiddlePoint(rectangle image.Rectangle, text string, offset int) image.Point { + return image.Pt(rectangle.Min.X+rectangle.Dx()/2-len(text)/2, rectangle.Max.Y-rectangle.Dy()/2+offset) +} + +func PrintString(s string, style ui.Style, p image.Point, buffer *ui.Buffer) { + for i, char := range s { + buffer.SetCell(ui.Cell{Rune: char, Style: style}, image.Pt(p.X+i, p.Y)) + } +} diff --git a/component/geometry.go b/component/util/geometry.go similarity index 76% rename from component/geometry.go rename to component/util/geometry.go index f8e6811..dc10676 100644 --- a/component/geometry.go +++ b/component/util/geometry.go @@ -1,4 +1,4 @@ -package component +package util import ( "image" @@ -38,3 +38,9 @@ func GetDistance(p1 image.Point, p2 image.Point) float64 { y := math.Abs(float64(p1.Y - p2.Y)) return math.Sqrt(x*x + y*y) } + +func GetRectCoordinates(area image.Rectangle, width int, height int) (int, int, int, int) { + x1 := area.Min.X + area.Dx()/2 - width/2 + y1 := area.Min.Y + area.Dy()/2 - height + return x1, y1, x1 + width, y1 + height + 2 +} diff --git a/config/config.go b/config/config.go index bc2df34..10bdfc7 100644 --- a/config/config.go +++ b/config/config.go @@ -21,7 +21,7 @@ type Config struct { AsciiBoxes []AsciiBoxConfig `yaml:"asciiboxes,omitempty"` } -func Load() (Config, Options) { +func LoadConfig() (Config, Options) { var opt Options _, err := flags.Parse(&opt) @@ -114,7 +114,7 @@ func saveFile(config *Config, fileName string) { log.Fatalf("Can't marshal config file: %v", err) } - err = ioutil.WriteFile(fileName, file, 0644) + err = ioutil.WriteFile(fileName, file, os.ModePerm) if err != nil { log.Fatalf("Can't save config file: %v", err) } diff --git a/data/item.go b/data/item.go index d4a7304..e697d09 100644 --- a/data/item.go +++ b/data/item.go @@ -14,7 +14,7 @@ import ( "time" ) -const interactiveShellStartupTimeout = time.Second +const interactiveShellStartupTimeout = 100 * time.Millisecond type Item struct { Label string diff --git a/data/sampler.go b/data/sampler.go index c19f904..89d9475 100644 --- a/data/sampler.go +++ b/data/sampler.go @@ -29,7 +29,7 @@ func NewSampler(consumer *Consumer, items []*Item, triggers []*Trigger, options go func() { for ; true; <-ticker.C { for _, item := range sampler.items { - go sampler.sample(item, options) + sampler.sample(item, options) } } }() @@ -39,7 +39,7 @@ func NewSampler(consumer *Consumer, items []*Item, triggers []*Trigger, options select { case sample := <-sampler.triggersChannel: for _, t := range sampler.triggers { - go t.Execute(sample) + t.Execute(sample) } } } diff --git a/main.go b/main.go index 6d925a1..68605dc 100644 --- a/main.go +++ b/main.go @@ -15,6 +15,7 @@ import ( "github.com/sqshq/sampler/console" "github.com/sqshq/sampler/data" "github.com/sqshq/sampler/event" + "github.com/sqshq/sampler/storage" "time" ) @@ -31,12 +32,12 @@ func (s *Starter) start(drawable ui.Drawable, consumer *data.Consumer, component items := data.NewItems(itemsConfig, *componentConfig.RateMs) data.NewSampler(consumer, items, triggers, s.opt, s.cfg.Variables, *componentConfig.RateMs) s.lout.AddComponent(cpt) - time.Sleep(100 * time.Millisecond) // desync coroutines + time.Sleep(10 * time.Millisecond) // desync coroutines } func main() { - cfg, opt := config.Load() + cfg, opt := config.LoadConfig() console.Init() defer console.Close() @@ -47,10 +48,11 @@ func main() { palette := console.GetPalette(*cfg.Theme) width, height := ui.TerminalDimensions() - lout := layout.NewLayout(width, height, component.NewStatusLine(opt.ConfigFile, palette), component.NewMenu(palette)) - + lout := layout.NewLayout(width, height, component.NewStatusLine(opt.ConfigFile, palette), component.NewMenu(palette), component.NewIntro(palette)) starter := &Starter{lout, player, opt, cfg} + license := storage.GetLicense() + for _, c := range cfg.RunCharts { cpt := runchart.NewRunChart(c, palette) starter.start(cpt, cpt.Consumer, c.ComponentConfig, c.Items, c.Triggers) @@ -81,6 +83,15 @@ func main() { starter.start(cpt, cpt.Consumer, c.ComponentConfig, []config.Item{c.Item}, c.Triggers) } + if license == nil { + lout.RunIntro() + storage.InitLicense() + } else if !license.Purchased /* && random */ { + // TODO lout.showNagWindow() with timeout and OK button + // TODO verify license + // TODO send stats + } + handler := event.NewHandler(lout, opt) handler.HandleEvents() } diff --git a/storage/license.go b/storage/license.go new file mode 100644 index 0000000..c2625f1 --- /dev/null +++ b/storage/license.go @@ -0,0 +1,53 @@ +package storage + +import ( + "gopkg.in/yaml.v2" + "log" +) + +type License struct { + Purchased bool + Valid bool + Key *string + Username *string + Company *string +} + +const licenseFileName = "license.yml" + +func GetLicense() *License { + if !fileExists(licenseFileName) { + return nil + } else { + file := readStorageFile(getPlatformStoragePath(licenseFileName)) + + license := new(License) + err := yaml.Unmarshal(file, license) + + if err != nil { + log.Fatalf("Can't read license file: %v", err) + } + + return license + } +} + +func InitLicense() { + + license := License{ + Purchased: false, + Valid: false, + } + + file, err := yaml.Marshal(license) + if err != nil { + log.Fatalf("Can't marshal config file: %v", err) + } + + initStorage() + saveStorageFile(file, getPlatformStoragePath(licenseFileName)) +} + +func SaveLicense() { + // TODO +} diff --git a/storage/stats.go b/storage/stats.go new file mode 100644 index 0000000..eceebd3 --- /dev/null +++ b/storage/stats.go @@ -0,0 +1,6 @@ +package storage + +// TODO +// version +// launch count +// components count by type diff --git a/storage/storage.go b/storage/storage.go new file mode 100644 index 0000000..2a6a2fb --- /dev/null +++ b/storage/storage.go @@ -0,0 +1,56 @@ +package storage + +import ( + "io/ioutil" + "log" + "os" + "path/filepath" + "runtime" +) + +const ( + macOSDir = "/Library/Application Support/Sampler" + linuxDir = "/.config/Sampler" + windowsDir = "%LOCALAPPDATA%\\Sampler" +) + +func fileExists(filename string) bool { + _, err := os.Stat(getPlatformStoragePath(filename)) + return !os.IsNotExist(err) +} + +func getPlatformStoragePath(filename string) string { + home, _ := os.UserHomeDir() + switch runtime.GOOS { + case "darwin": + return filepath.Join(home, macOSDir, filename) + case "windows": + return filepath.Join(home, windowsDir, filename) + default: + return filepath.Join(linuxDir, filename) + } +} + +func initStorage() { + err := os.MkdirAll(getPlatformStoragePath(""), os.ModePerm) + if err != nil { + log.Fatalf("Failed to init storage: %v", err) + } +} + +func readStorageFile(path string) []byte { + + file, err := ioutil.ReadFile(path) + if err != nil { + log.Fatalf("Failed to the read storage file: %s", path) + } + + return file +} + +func saveStorageFile(file []byte, fileName string) { + err := ioutil.WriteFile(fileName, file, os.ModePerm) + if err != nil { + log.Fatalf("Failed to save the storage file: %v", err) + } +}