This commit is contained in:
zhaocy 2022-08-05 10:54:30 +08:00
parent 6cce9c8fe5
commit 6eb86a4028
51 changed files with 1616 additions and 265 deletions

View File

@ -1,36 +0,0 @@
package common
const (
// app
APP_NAME = "protocol"
// toolbar
TOOLBAR_WELCOME = "welcome"
TOOLBAR_TESTER = "tester"
//button
BUTTON_LOGIN = "login"
BUTTON_REGISTE = "registe"
BUTTON_RANDOM = "random"
BUTTON_OK = "ok"
BUTTON_CANCEL = "cancel"
//label
LABEL_CHOOSE = "welcome,The service you choose is "
LABEL_NICKNAME = "nickname"
LABEL_ACCOUNT = "account"
LABEL_WELCOME = "welcome "
// form title
FORM_TITLE_CREATEROLE = "create role"
FORM_TITLE_LOGIN = "login"
// info
INFO_WAIT = "Please wait"
// menu
MENU_FILE = "file"
// datetiem
DATETIME = "datetime"
)

View File

@ -1,72 +0,0 @@
package main
import (
"fmt"
"go_dreamfactory/cmd/ptt/service"
"go_dreamfactory/cmd/ptt/ui"
"os"
"fyne.io/fyne/v2/app"
"github.com/sirupsen/logrus"
)
var (
connService service.ConnService
pttService service.PttService
logger *logrus.Logger
)
//
func init() {
var err error
// initialize logger
if err = setupLogger(); err != nil {
fmt.Println(err)
os.Exit(1)
}
if err = setupWsConn(); err != nil {
fmt.Println(err)
os.Exit(1)
}
if err = setupPtt(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func main() {
logrus.Info("Starting...")
// create a new ui
appUI := ui.NewUI(app.NewWithID("protocol-test-tool"), connService, pttService)
mainWindow := ui.NewMainWindow(appUI)
mainWindow.CreateWindow("协议测试工具", 1366, 768, true)
appUI.Run()
}
func setupPtt() (err error) {
pttService = service.NewPttService(connService)
return
}
func setupWsConn() (err error) {
connService = service.NewConnService()
return
}
func setupLogger() (err error) {
logrus.SetFormatter(&logrus.TextFormatter{
DisableColors: true,
FullTimestamp: true,
})
logrus.SetLevel(logrus.DebugLevel)
logrus.SetOutput(os.Stdout)
//TODO
return nil
}

View File

@ -1,29 +0,0 @@
package ui
import (
"go_dreamfactory/cmd/ptt/lib/common"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
type appTester struct {
appAdapter
}
func (a *appTester) GetAppName() string {
return common.TOOLBAR_TESTER
}
func (a *appTester) LazyInit() error {
a.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_TESTER, theme.AccountIcon(), nil)
a.tabItem.Content = container.NewCenter(
container.NewVBox(
widget.NewLabelWithStyle(common.TOOLBAR_TESTER,
fyne.TextAlignCenter,
fyne.TextStyle{Bold: true})),
)
return nil
}

View File

@ -1,17 +0,0 @@
package ui
import "fyne.io/fyne/v2"
type mainMenu struct {
*fyne.MainMenu
helpMenu *fyne.Menu
aboutSelf *fyne.MenuItem
}
func newMainMenu() *mainMenu{
var mm mainMenu
return &mm
}

View File

@ -1,52 +0,0 @@
package ui
import (
"go_dreamfactory/cmd/ptt/service"
"sync"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/theme"
)
type UI interface {
AddWindow(name string, w fyne.Window)
Run()
Stop()
}
type UIImpl struct {
app fyne.App
windows map[string]fyne.Window
winMux *sync.Mutex
connService service.ConnService
pttService service.PttService
}
func NewUI(
app fyne.App,
connService service.ConnService,
pttService service.PttService,
) *UIImpl {
app.Settings().SetTheme(theme.DarkTheme())
return &UIImpl{
app: app,
windows: make(map[string]fyne.Window),
winMux: &sync.Mutex{},
connService: connService,
pttService: pttService,
}
}
func (ui *UIImpl) AddWindow(name string, w fyne.Window) {
ui.winMux.Lock()
defer ui.winMux.Unlock()
ui.windows[name] = w
}
func (ui *UIImpl) Run() {
ui.app.Run()
}
func (ui *UIImpl) Stop() {
ui.app.Quit()
}

View File

@ -10,3 +10,12 @@ const (
WindowAspect_Normal WindowAspect = iota WindowAspect_Normal WindowAspect = iota
WindowAspect_FullScreen WindowAspect_FullScreen
) )
const (
DEFAULT_RESOURCE_PATH = "resources"
)
const (
CONFIG_SERVICE_NAME = "name"
CONFIG_SERVICE_URL = "url"
)

139
cmd/v2/lib/common/lang.go Normal file
View File

@ -0,0 +1,139 @@
package common
// en
// const (
// // app
// APP_NAME = "protocol"
// app_testcase
// APP_TESTCASE_INTRO = "select on left"
// APP_TESTCASE_TITLE = "protocol test"
// APP_TESTCASE_TITLE = "path"
// APP_TESTCASE_BTN_CLEARLOG = "Clear Log"
//form
// APP_TESTCASE_FORM_LABEL_HEROOBJID = "HeroObjID"
// APP_TESTCASE_FORM_LABEL_RACECARD = "RaceObjID"
// APP_TESTCASE_FORM_LABEL_CARD = "Hero"
// APP_TESTCASE_FORM_LABEL_EXPCARDS = "ExpCardID"
// APP_TESTCASE_FORM_LABEL_OID = "HeroObjOID"
// APP_TESTCASE_FORM_LABEL_NUM = "Amount"
// APP_TESTCASE_FORM_TASKTAG = "TaskTag"
// APP_TESTCASE_FORM_TASK_OID = "TaskID"
// APP_TESTCASE_FORM_NICKNAME = "NickName"
// APP_TESTCASE_OPTIONS = "Option"
// AAP_TESTCASE_FORM_TASK_DAY = "Day"
// AAP_TESTCASE_FORM_TASK_WEEK = "Week"
// AAP_TESTCASE_FORM_TASK_ACHIEVE = "Achieve"
// // toolbar
// TOOLBAR_WELCOME = "welcome"
// TOOLBAR_TESTER = "tester"
// //button
// BUTTON_LOGIN = "login"
// BUTTON_REGISTE = "registe"
// BUTTON_RANDOM = "random"
// BUTTON_OK = "ok"
// BUTTON_CANCEL = "cancel"
// //label
// LABEL_CHOOSE = "welcome,The service you choose is "
// LABEL_NICKNAME = "nickname"
// LABEL_ACCOUNT = "account"
// LABEL_WELCOME = "welcome "
// // form title
// FORM_TITLE_CREATEROLE = "create role"
// FORM_TITLE_LOGIN = "login"
// // info
// INFO_WAIT = "Please wait"
// // menu
// MENU_FILE = "file"
// //toy
// // datetiem
// DATETIME = "datetime"
// //userinfo
// USERINFO_TITLE = "用户信息"
// USERINFO_UID = "UID"
// USERINFO_ACC = "Acc"
// USERINFO_NAME = "Name"
// USERINFO_LV = "Lv"
// USERINFO_GOLD = "Gold"
// USERINFO_EXP = "Exp"
// USERINFO_VIP = "VIP"
// USERINFO_AVATAR = "Ava"
// USERINFO_BTN_COPY = "COPY"
// )
// zh
const (
// app
APP_NAME = "机器人"
// app_testcase
APP_TESTCASE_TITLE = "接口测试"
APP_TESTCASE_LABEL_PATH = "路径"
APP_TESTCASE_BTN_CLEARLOG = "清除"
APP_TESTCASE_INTRO = "选择左侧的测试接口"
//form
APP_TESTCASE_FORM_LABEL_HEROOBJID = "卡片OID"
APP_TESTCASE_FORM_LABEL_RACECARD = "种族卡片"
APP_TESTCASE_FORM_LABEL_CARD = "卡片"
APP_TESTCASE_FORM_LABEL_EXPCARDS = "经验卡"
APP_TESTCASE_FORM_LABEL_OID = "卡片OID"
APP_TESTCASE_FORM_LABEL_NUM = "卡片数量"
APP_TESTCASE_FORM_TASKTAG = "任务类型"
APP_TESTCASE_FORM_TASK_OID = "任务OID"
APP_TESTCASE_FORM_NICKNAME = "昵称"
APP_TESTCASE_OPTIONS = "选填"
AAP_TESTCASE_FORM_TASK_DAY = "日常任务"
AAP_TESTCASE_FORM_TASK_WEEK = "周常任务"
AAP_TESTCASE_FORM_TASK_ACHIEVE = "成就"
// toolbar
TOOLBAR_WELCOME = "欢迎"
TOOLBAR_TESTER = "tester"
//button
BUTTON_LOGIN = "登录"
BUTTON_REGISTE = "注册"
BUTTON_RANDOM = "随机"
BUTTON_OK = "确定"
BUTTON_CANCEL = "取消"
//label
LABEL_CHOOSE = "欢迎,您登录的服务是"
LABEL_NICKNAME = "昵称"
LABEL_ACCOUNT = "账号"
LABEL_WELCOME = "欢迎 "
// form title
FORM_TITLE_CREATEROLE = "创角"
FORM_TITLE_LOGIN = "登录"
// info
INFO_WAIT = "请稍后"
// menu
MENU_FILE = "文件"
// toy
// datetiem
DATETIME = "时钟"
// userinfo
USERINFO_BTN_COPY = "复制UID"
USERINFO_TITLE = "用户信息"
USERINFO_UID = "UID"
USERINFO_ACC = "账号"
USERINFO_NAME = "昵称"
USERINFO_LV = "等级"
USERINFO_GOLD = "金币"
USERINFO_EXP = "经验"
USERINFO_VIP = "贵宾"
USERINFO_AVATAR = "头像"
)

View File

@ -2,7 +2,7 @@ package common
import ( import (
"fmt" "fmt"
"go_dreamfactory/cmd/ptt/model" "go_dreamfactory/cmd/v2/model"
"go_dreamfactory/utils" "go_dreamfactory/utils"
"time" "time"

View File

@ -0,0 +1,12 @@
package common
import (
"bytes"
"encoding/json"
)
func FormatJson(data string) (string, error) {
var out bytes.Buffer
err := json.Indent(&out, []byte(data), "", " ")
return out.String(), err
}

106
cmd/v2/main.go Normal file
View File

@ -0,0 +1,106 @@
package main
import (
"fmt"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
"go_dreamfactory/cmd/v2/ui"
"io"
"os"
"fyne.io/fyne/v2/app"
"github.com/sirupsen/logrus"
)
var (
connService service.ConnService
pttService service.PttService
configService service.ConfigService
obs = observer.NewObserver()
logger *logrus.Logger
)
//
func init() {
var err error
if err = setupConfig(); err != nil {
fmt.Println(err)
os.Exit(1)
}
// initialize logger
if err = setupLogger(); err != nil {
fmt.Println(err)
os.Exit(1)
}
if err = setupWsConn(); err != nil {
fmt.Println(err)
os.Exit(1)
}
if err = setupPtt(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func main() {
logrus.Info("Starting...")
// create a new ui
appUI := ui.NewUI(app.NewWithID("protocol-test-tool"), configService, connService, pttService, obs)
mainWindow := ui.NewMainWindow(appUI)
mainWindow.CreateWindow("robot_v2", 1366, 768, true)
appUI.Run()
}
func setupPtt() (err error) {
pttService = service.NewPttService(connService)
return
}
func setupWsConn() (err error) {
connService = service.NewConnService(obs)
return
}
func setupConfig() (err error) {
configService, err = service.NewConfigService()
if err != nil {
return
}
if err = configService.ApplyConfig(); err != nil {
return
}
return
}
func setupLogger() (err error) {
logrus.SetFormatter(&logrus.TextFormatter{
DisableColors: true,
FullTimestamp: true,
})
logrus.SetLevel(logrus.DebugLevel)
logrus.SetOutput(os.Stdout)
//设置output,默认为stderr,可以为任何io.Writer比如文件*os.File
file, err := os.OpenFile("robot_v2.log", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
writers := []io.Writer{
file,
os.Stdout}
//同时写文件和屏幕
fileAndStdoutWriter := io.MultiWriter(writers...)
if err == nil {
logrus.SetOutput(fileAndStdoutWriter)
} else {
logrus.Fatal("failed to log to file.")
}
return nil
}

15
cmd/v2/model/testcase.go Normal file
View File

@ -0,0 +1,15 @@
package model
import "google.golang.org/protobuf/proto"
type TestCase struct {
Id string //用例ID 如果没有指定,会自动赋值uuid
Desc string //用例描述
MainType string //协议类型 L1
SubType string //协议类型 L2
Req proto.Message //请求类型
Rsp proto.Message //响应类型
Enabled bool //是否启用
// View MyCaseView //视图
Print func(rsp proto.Message) string //定义打印
}

View File

@ -1,10 +1,18 @@
## install tool ## install tool
go install fyne.io/fyne/v2/cmd/fyne@latest go install fyne.io/fyne/v2/cmd/fyne@latest
## install font ## install font
### fyne CLI 打包 ### fyne CLI 打包
fyne bundle msyh.ttc >> bundled.go fyne bundle msyh.ttc >> bundled.go
-- 打包粗体 -- 打包粗体
fyne bundle -append msyhbd.ttc >> bundled.go fyne bundle -append msyhbd.ttc >> bundled.go
(不要使用powershell 或vscode自带终端) (不要使用powershell 或vscode自带终端)
## 开发协议参数表单
1.
2.
3.

View File

@ -0,0 +1,17 @@
services:
- service:
sid: 1
name: service1
url: ws://10.0.0.100:7891/gateway
- service:
sid: 2
name: 赵长远
url: ws://10.0.0.238:7891/gateway
- service:
sid: 3
name: service3
url: ws://10.0.0.101:7891/gateway
- service:
sid: 4
name: service4
url: ws://10.0.0.101:7891/gateway

View File

@ -0,0 +1,105 @@
package service
import (
"fmt"
"go_dreamfactory/cmd/v2/lib/common"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
)
type ConfigService interface {
GetConfig() *Config
LoadConfig() error
ApplyConfig() error
}
type ConfigServiceImpl struct {
ResourcePath string
Config *Config
}
type ServiceConf struct {
SId int32 `yaml:"sid"`
Name string `yaml:"name"`
Url string `yaml:"url"`
}
type Services struct {
Service *ServiceConf `yaml:"service"`
}
type Config struct {
Services []*Services `yaml:"services"`
}
func NewConfigService() (ConfigService, error) {
srv := &ConfigServiceImpl{
ResourcePath: common.DEFAULT_RESOURCE_PATH,
Config: &Config{},
}
err := srv.init()
return srv, err
}
func (c *ConfigServiceImpl) LoadConfig() error {
viper.AddConfigPath("./" + c.ResourcePath)
return viper.ReadInConfig()
}
func (c *ConfigServiceImpl) ApplyConfig() error {
if err := c.LoadConfig(); err != nil {
logrus.Error(err)
return err
}
if err := viper.Unmarshal(c.Config); err != nil {
logrus.Error(err)
return err
}
return nil
}
func (c *ConfigServiceImpl) Unmarshal() error {
if sArr, ok := viper.Get("services").([]interface{}); ok {
for _, service := range sArr {
if services, ok := service.(map[interface{}]interface{}); ok {
for _, v := range services {
if vv, ok := v.(map[interface{}]interface{}); ok {
for k, _ := range vv {
kk := k.(string)
logrus.Debug(vv[kk])
srvConf := &ServiceConf{}
switch kk {
case "sid":
srvConf.SId = vv[kk].(int32)
case "name":
srvConf.Name = vv[kk].(string)
case "url":
srvConf.Url = vv[kk].(string)
default:
return fmt.Errorf("config key[%s] not foud", kk)
}
c.Config.Services = append(c.Config.Services, &Services{})
}
}
logrus.Debug(v)
// if v
}
}
}
}
return nil
}
func (c *ConfigServiceImpl) GetConfig() *Config {
return c.Config
}
func (c *ConfigServiceImpl) init() error {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
return nil
}

View File

@ -1,14 +1,23 @@
package service package service
import ( import (
"go_dreamfactory/cmd/v2/model"
"go_dreamfactory/cmd/v2/service/observer"
"go_dreamfactory/comm" "go_dreamfactory/comm"
"go_dreamfactory/pb" "go_dreamfactory/pb"
"time"
"github.com/golang/protobuf/ptypes"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/spf13/cast"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
) )
var (
conn *ConnServiceImpl
)
type ConnService interface { type ConnService interface {
Connect(wsUrl string) error Connect(wsUrl string) error
SendMsg(msg *pb.UserMessage, rsp proto.Message) (err error) SendMsg(msg *pb.UserMessage, rsp proto.Message) (err error)
@ -16,16 +25,25 @@ type ConnService interface {
} }
type ConnServiceImpl struct { type ConnServiceImpl struct {
ws *websocket.Conn ws *websocket.Conn
obs observer.Observer
} }
func NewConnService() ConnService { func NewConnService(obs observer.Observer) ConnService {
return &ConnServiceImpl{} conn = &ConnServiceImpl{obs: obs}
return conn
}
func GetConnService() *ConnServiceImpl {
return conn
} }
// connect ... // connect ...
func (c *ConnServiceImpl) Connect(wsUrl string) error { func (c *ConnServiceImpl) Connect(wsUrl string) error {
ws, _, err := websocket.DefaultDialer.Dial(wsUrl, nil) dialer := &websocket.Dialer{
HandshakeTimeout: 5 * time.Second,
}
ws, _, err := dialer.Dial(wsUrl, nil)
if err != nil { if err != nil {
logrus.Errorf("websocket conn err:%v", err) logrus.Errorf("websocket conn err:%v", err)
return err return err
@ -81,3 +99,28 @@ func (c *ConnServiceImpl) handleNotify(msg *pb.UserMessage) (code pb.ErrorCode)
} }
return return
} }
// response
func (c *ConnServiceImpl) RespHandle(t *model.TestCase) {
for {
if code, msg := c.ReceiveMsg(); code != pb.ErrorCode_Success {
c.obs.Notify(observer.EVENT_APP_LOG, cast.ToString(code))
} else {
if msg.MainType == t.MainType && msg.SubType == t.SubType {
if t.Print == nil {
if a, err := ptypes.MarshalAny(msg.Data); err != nil {
c.obs.Notify(observer.EVENT_APP_LOG, err.Error())
} else {
c.obs.Notify(observer.EVENT_APP_LOG, a.String())
}
} else {
if !comm.ProtoUnmarshal(msg, t.Rsp) {
return
}
c.obs.Notify(observer.EVENT_APP_LOG, t.Print(t.Rsp))
}
break
}
}
}
}

View File

@ -0,0 +1,6 @@
package observer
const (
EVENT_USERINFO Event = "userinfo"
EVENT_APP_LOG Event = "app_log"
)

View File

@ -0,0 +1,56 @@
package observer
import "sync"
type (
Event string
OnNotify func(data interface{}, args ...interface{})
Listener struct {
OnNotify OnNotify
}
Observer interface {
AddListener(event Event, listener Listener)
Remove(event Event)
Notify(event Event, data interface{}, args ...interface{})
}
ObserverImpl struct {
listeners map[Event][]Listener
}
)
var (
obs *ObserverImpl
singletonObserver sync.Once
)
func NewObserver() *ObserverImpl {
singletonObserver.Do(func() {
obs = &ObserverImpl{}
})
return obs
}
func (o *ObserverImpl) AddListener(event Event, listener Listener) {
if o.listeners == nil {
o.listeners = map[Event][]Listener{}
}
o.listeners[event] = append(o.listeners[event], listener)
}
func (o *ObserverImpl) Remove(event Event) {
delete(o.listeners, event)
}
func (o *ObserverImpl) Notify(event Event, data interface{}, args ...interface{}) {
if listeners, ok := o.listeners[event]; !ok {
return
} else {
for _, listener := range listeners {
go listener.OnNotify(data, args...)
}
}
}

View File

@ -1,12 +1,17 @@
package service package service
import ( import (
"go_dreamfactory/cmd/ptt/lib/common" "go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/comm" "go_dreamfactory/comm"
"go_dreamfactory/modules/user" "go_dreamfactory/modules/user"
"go_dreamfactory/pb" "go_dreamfactory/pb"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"google.golang.org/protobuf/proto"
)
var (
ptt PttService
) )
type PttService interface { type PttService interface {
@ -14,6 +19,7 @@ type PttService interface {
CreateRole(nickName string) (code pb.ErrorCode, rsp *pb.UserCreateResp) CreateRole(nickName string) (code pb.ErrorCode, rsp *pb.UserCreateResp)
GetUser() *UserInfo GetUser() *UserInfo
SetUser(dbUser *pb.DBUser, dbUserExpand *pb.DBUserExpand) SetUser(dbUser *pb.DBUser, dbUserExpand *pb.DBUserExpand)
SendToClient(msg *pb.UserMessage, rsp proto.Message) error
} }
type PttServiceImpl struct { type PttServiceImpl struct {
@ -27,9 +33,14 @@ type UserInfo struct {
} }
func NewPttService(connService ConnService) PttService { func NewPttService(connService ConnService) PttService {
return &PttServiceImpl{ ptt = &PttServiceImpl{
connService: connService, connService: connService,
} }
return ptt
}
func GetPttService() PttService {
return ptt
} }
func (p *PttServiceImpl) GetUser() *UserInfo { func (p *PttServiceImpl) GetUser() *UserInfo {
@ -40,6 +51,14 @@ func (p *PttServiceImpl) SetUser(dbUser *pb.DBUser, dbUserExpand *pb.DBUserExpan
p.user = &UserInfo{DbUser: dbUser, DbUserExpand: dbUserExpand} p.user = &UserInfo{DbUser: dbUser, DbUserExpand: dbUserExpand}
} }
func (p *PttServiceImpl) SendToClient(msg *pb.UserMessage, rsp proto.Message) (err error) {
msg.Sec = common.BuildSecStr(p.user.DbUser.Sid, p.user.DbUser.Binduid)
if err = p.connService.SendMsg(msg, rsp); err != nil {
logrus.WithField("err", err).Error(err)
}
return
}
// Login // Login
func (p *PttServiceImpl) Login(sid int32, account string) (code pb.ErrorCode, rsp *pb.UserLoginResp) { func (p *PttServiceImpl) Login(sid int32, account string) (code pb.ErrorCode, rsp *pb.UserLoginResp) {
head := &pb.UserMessage{MainType: string(comm.ModuleUser), SubType: user.UserSubTypeLogin} head := &pb.UserMessage{MainType: string(comm.ModuleUser), SubType: user.UserSubTypeLogin}

12
cmd/v2/theme/bundled.go Normal file

File diff suppressed because one or more lines are too long

29
cmd/v2/theme/mytheme.go Normal file
View File

@ -0,0 +1,29 @@
package theme
import (
"image/color"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/theme"
)
type MyTheme struct{}
var _ fyne.Theme = (*MyTheme)(nil)
// return bundled font resource
// ResourceSourceHanSansTtf 即是 bundle.go 文件中 var 的变量名
func (m MyTheme) Font(s fyne.TextStyle) fyne.Resource {
return resourceMsyhTtc
}
func (*MyTheme) Color(n fyne.ThemeColorName, v fyne.ThemeVariant) color.Color {
return theme.DarkTheme().Color(n, v)
}
func (*MyTheme) Icon(n fyne.ThemeIconName) fyne.Resource {
return theme.DarkTheme().Icon(n)
}
func (*MyTheme) Size(n fyne.ThemeSizeName) float32 {
return theme.DarkTheme().Size(n)
}

View File

@ -1,14 +1,19 @@
package ui package ui
import "fyne.io/fyne/v2/container" import (
"go_dreamfactory/cmd/v2/service/observer"
"fyne.io/fyne/v2/container"
)
type appContainer struct { type appContainer struct {
appStatusMap map[string]appStatus appStatusMap map[string]appStatus
container.DocTabs container.DocTabs
obs observer.Observer
} }
func newAppContainer() *appContainer { func newAppContainer(obs observer.Observer) *appContainer {
at := &appContainer{} at := &appContainer{obs: obs}
at.appStatusMap = make(map[string]appStatus) at.appStatusMap = make(map[string]appStatus)
at.CloseIntercept = at.closeHandle at.CloseIntercept = at.closeHandle
@ -77,7 +82,7 @@ func (at *appContainer) openDefaultApp() (string, error) {
func (at *appContainer) initApp(app appInterface) error { func (at *appContainer) initApp(app appInterface) error {
st, ok := at.appStatusMap[app.GetAppName()] st, ok := at.appStatusMap[app.GetAppName()]
if !ok || !st.lazyInit { if !ok || !st.lazyInit {
err := app.LazyInit() err := app.LazyInit(at.obs)
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,9 +1,13 @@
package ui package ui
import "fyne.io/fyne/v2/container" import (
"go_dreamfactory/cmd/v2/service/observer"
"fyne.io/fyne/v2/container"
)
type appInterface interface { type appInterface interface {
LazyInit() error LazyInit(obs observer.Observer) error
GetTabItem() *container.TabItem GetTabItem() *container.TabItem
GetAppName() string GetAppName() string
OpenDefault() bool OpenDefault() bool
@ -15,6 +19,7 @@ var (
&appWelcome{}, &appWelcome{},
&appTester{}, &appTester{},
} }
) )
type appAdapter struct { type appAdapter struct {

157
cmd/v2/ui/app_testcase.go Normal file
View File

@ -0,0 +1,157 @@
package ui
import (
"fmt"
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/model"
"go_dreamfactory/cmd/v2/service/observer"
"go_dreamfactory/cmd/v2/ui/formview"
"go_dreamfactory/comm"
"go_dreamfactory/modules/hero"
"go_dreamfactory/modules/task"
"go_dreamfactory/modules/user"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
)
// register views
var (
viewRegister = map[string]MyCaseView{
//user
ff(comm.ModuleUser, user.UserSubTypeModifyName): &formview.UserModifynameView{},
//task
ff(comm.ModuleTask, task.TaskSubTypeList): &formview.TaskListView{},
ff(comm.ModuleTask, task.TaskSubTypeReceive): &formview.TaskReceiveView{},
ff(comm.ModuleTask, task.TaskSubTypeActiveList): &formview.TaskActiveListView{},
ff(comm.ModuleTask, task.TaskSubTypeActiveReceive): &formview.TaskActiveReceiveView{},
// hero
ff(comm.ModuleHero, hero.HeroSubTypeList): &formview.HeroListView{},
ff(comm.ModuleHero, hero.StrengthenUplv): &formview.HeroStrengthenUplvView{},
ff(comm.ModuleHero, hero.StrengthenUpStar): &formview.HeroStrengthenUpStarView{},
}
)
type appTester struct {
appAdapter
}
func (a *appTester) GetAppName() string {
return common.TOOLBAR_TESTER
}
func (a *appTester) disEnabled(t *model.TestCase) bool {
return !t.Enabled
}
func (a *appTester) LazyInit(obs observer.Observer) error {
a.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_TESTER, theme.AccountIcon(), nil)
content := container.NewMax()
title := widget.NewLabel(common.APP_TESTCASE_TITLE)
intro := widget.NewLabel("")
intro.Wrapping = fyne.TextWrapWord
caseContent := container.NewBorder(
container.NewVBox(title, widget.NewSeparator(), intro), nil, nil, nil, content)
setNav := func(t *model.TestCase) {
viewKey := fmt.Sprintf("%s.%s", t.MainType, t.SubType)
title.SetText(fmt.Sprintf("%s : %s", common.APP_TESTCASE_LABEL_PATH, viewKey))
intro.SetText(t.Desc)
content.Objects = []fyne.CanvasObject{}
if view, ok := viewRegister[viewKey]; ok {
view.Init()
resLog := widget.NewMultiLineEntry()
obs.AddListener(observer.EVENT_APP_LOG, observer.Listener{
OnNotify: func(data interface{}, args ...interface{}) {
if data == nil {
return
}
resLog.SetText(data.(string))
},
})
resLog.Wrapping = fyne.TextWrapBreak
formReq := view.CreateForm(t)
// required! Refresh updates the widget
formReq.Refresh()
formCard := widget.NewCard("", "", formReq)
pos := widget.NewLabel("")
clearBtn := widget.NewButtonWithIcon(common.APP_TESTCASE_BTN_CLEARLOG, theme.DeleteIcon(), func() {
resLog.SetText("")
})
resPanel := container.NewBorder(container.NewHBox(clearBtn, layout.NewSpacer(), pos), nil, nil, nil, resLog)
panel := container.NewVSplit(
formCard,
resPanel,
)
content.Objects = append(content.Objects, panel)
} else {
logrus.WithFields(logrus.Fields{"mainType": t.MainType, "subType": t.SubType}).Warn("no view")
}
content.Refresh()
}
split := container.NewHSplit(a.makeNav(setNav), caseContent)
split.Offset = 0.2
a.tabItem.Content = split
return nil
}
func (a *appTester) makeNav(setNav func(testCase *model.TestCase)) fyne.CanvasObject {
tree := &widget.Tree{
ChildUIDs: func(uid string) (c []widget.TreeNodeID) {
return CaseIndex[uid]
},
IsBranch: func(uid string) bool {
children, ok := CaseIndex[uid]
return ok && len(children) > 0
},
CreateNode: func(branch bool) fyne.CanvasObject {
return widget.NewLabel("(empty)")
},
UpdateNode: func(uid string, branch bool, obj fyne.CanvasObject) {
t, ok := CaseNav[uid]
if !ok {
logrus.WithField("id", uid).Warnf("Missing tutorial panel")
return
}
if a.disEnabled(t) {
obj.(*widget.Label).TextStyle = fyne.TextStyle{Italic: true}
if branch {
obj.(*widget.Label).SetText(fmt.Sprintf("%s(dis)", t.MainType))
} else {
obj.(*widget.Label).SetText(fmt.Sprintf("%s(dis)", t.SubType))
}
} else {
obj.(*widget.Label).TextStyle = fyne.TextStyle{}
if branch {
obj.(*widget.Label).SetText(t.MainType)
} else {
obj.(*widget.Label).SetText(t.SubType)
}
}
},
OnSelected: func(uid string) {
if t, ok := CaseNav[uid]; ok {
logrus.WithFields(logrus.Fields{"mainType": t.MainType, "subType": t.SubType}).Debug("select")
if a.disEnabled(t) {
return
}
if t.SubType == "" {
return
}
setNav(t)
}
},
}
return container.NewBorder(nil, nil, nil, nil, tree)
}

View File

@ -1,7 +1,8 @@
package ui package ui
import ( import (
"go_dreamfactory/cmd/ptt/lib/common" "go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/service/observer"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
@ -13,7 +14,7 @@ type appWelcome struct {
appAdapter appAdapter
} }
func (a *appWelcome) LazyInit() error { func (a *appWelcome) LazyInit(obs observer.Observer) error {
a.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_WELCOME, theme.ComputerIcon(), nil) a.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_WELCOME, theme.ComputerIcon(), nil)
@ -33,3 +34,7 @@ func (a *appWelcome) OpenDefault() bool {
func (a *appWelcome) GetAppName() string { func (a *appWelcome) GetAppName() string {
return common.TOOLBAR_WELCOME return common.TOOLBAR_WELCOME
} }
func (a appWelcome) OnClose() bool {
return false
}

View File

@ -1,6 +1,6 @@
package ui package ui
import "go_dreamfactory/cmd/ptt/lib/common" import "go_dreamfactory/cmd/v2/lib/common"
type WindowDefaultOptions struct { type WindowDefaultOptions struct {
windowAction common.WindowAction windowAction common.WindowAction

View File

@ -0,0 +1,16 @@
package formview
import (
"go_dreamfactory/cmd/v2/lib/common"
"fyne.io/fyne/v2/widget"
)
type BaseformView struct {
form *widget.Form
}
func (this *BaseformView) Init() {
this.form = widget.NewForm()
this.form.SubmitText = common.BUTTON_OK
}

View File

@ -0,0 +1,29 @@
package formview
import (
"go_dreamfactory/cmd/v2/lib/common"
"fyne.io/fyne/v2/widget"
)
func getTaskTagSelect() *widget.Select {
var tagSelect *widget.Select
tagSelect = widget.NewSelect([]string{
common.AAP_TESTCASE_FORM_TASK_DAY,
common.AAP_TESTCASE_FORM_TASK_WEEK,
common.AAP_TESTCASE_FORM_TASK_ACHIEVE}, func(s string) {
switch s {
case common.AAP_TESTCASE_FORM_TASK_DAY:
tagSelect.Selected = "1"
case common.AAP_TESTCASE_FORM_TASK_WEEK:
tagSelect.Selected = "2"
case common.AAP_TESTCASE_FORM_TASK_ACHIEVE:
tagSelect.Selected = "3"
default:
tagSelect.Selected = "0"
}
})
tagSelect.SetSelectedIndex(0)
return tagSelect
}

View File

@ -0,0 +1,26 @@
package formview
import (
"go_dreamfactory/cmd/v2/model"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/pb"
"fyne.io/fyne/v2"
"github.com/sirupsen/logrus"
)
type HeroListView struct {
BaseformView
}
func (this *HeroListView) CreateForm(t *model.TestCase) fyne.CanvasObject {
this.form.OnSubmit = func() {
head := &pb.UserMessage{MainType: t.MainType, SubType: t.SubType}
if err := service.GetPttService().SendToClient(head, &pb.HeroListReq{}); err != nil {
logrus.Error(err)
}
go service.GetConnService().RespHandle(t)
}
return this.form
}

View File

@ -0,0 +1,98 @@
package formview
import (
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/model"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/pb"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
"github.com/spf13/cast"
)
type HeroStrengthenUpStarView struct {
BaseformView
}
func (this *HeroStrengthenUpStarView) CreateForm(t *model.TestCase) fyne.CanvasObject {
heroObjID := widget.NewEntry()
//HeroRace
heroRaceId := widget.NewEntry()
heroRaceId.PlaceHolder = common.APP_TESTCASE_FORM_LABEL_OID
heroRaceAmount := widget.NewEntry()
heroRaceAmount.PlaceHolder = common.APP_TESTCASE_FORM_LABEL_NUM
heroRaceId2 := widget.NewEntry()
heroRaceId2.PlaceHolder = common.APP_TESTCASE_OPTIONS
heroRaceAmount2 := widget.NewEntry()
heroRaceAmount2.PlaceHolder = common.APP_TESTCASE_OPTIONS
//Hero
heroId := widget.NewEntry()
heroId.PlaceHolder = common.APP_TESTCASE_FORM_LABEL_OID
heroAmount := widget.NewEntry()
heroAmount.PlaceHolder = common.APP_TESTCASE_FORM_LABEL_NUM
heroId2 := widget.NewEntry()
heroId2.PlaceHolder = common.APP_TESTCASE_OPTIONS
heroAmount2 := widget.NewEntry()
heroAmount2.PlaceHolder = common.APP_TESTCASE_OPTIONS
heroRace := container.NewGridWithColumns(2, heroRaceId, heroRaceAmount, heroRaceId2, heroRaceAmount2)
hero := container.NewGridWithColumns(2, heroId, heroAmount, heroId2, heroAmount2)
this.form.AppendItem(widget.NewFormItem(common.APP_TESTCASE_FORM_LABEL_HEROOBJID, heroObjID))
this.form.AppendItem(widget.NewFormItem(common.APP_TESTCASE_FORM_LABEL_RACECARD, heroRace))
this.form.AppendItem(widget.NewFormItem(common.APP_TESTCASE_FORM_LABEL_CARD, hero))
this.form.OnSubmit = func() {
// heroRace
var heroRace []*pb.CostCardData
if heroRaceId.Text != "" && heroRaceAmount.Text != "" {
heroRace = append(heroRace, &pb.CostCardData{
CostCardObj: heroRaceId.Text,
Amount: cast.ToInt32(heroRaceAmount.Text),
})
}
if heroRaceId2.Text != "" && heroRaceAmount2.Text != "" {
heroRace = append(heroRace, &pb.CostCardData{
CostCardObj: heroRaceId2.Text,
Amount: cast.ToInt32(heroRaceAmount2.Text),
})
}
//hero
var hero []*pb.CostCardData
if heroId.Text != "" && heroAmount.Text != "" {
hero = append(hero, &pb.CostCardData{
CostCardObj: heroId.Text,
Amount: cast.ToInt32(heroAmount.Text),
})
}
if heroId2.Text != "" && heroAmount2.Text != "" {
hero = append(hero, &pb.CostCardData{
CostCardObj: heroId2.Text,
Amount: cast.ToInt32(heroAmount2.Text),
})
}
head := &pb.UserMessage{MainType: t.MainType, SubType: t.SubType}
if err := service.GetPttService().SendToClient(head,
&pb.HeroStrengthenUpStarReq{
HeroObjID: heroObjID.Text,
HeroRace: heroRace,
Hero: hero,
}); err != nil {
logrus.Error(err)
return
}
go service.GetConnService().RespHandle(t)
}
return this.form
}

View File

@ -0,0 +1,68 @@
package formview
import (
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/model"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/pb"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
"github.com/spf13/cast"
)
type HeroStrengthenUplvView struct {
BaseformView
}
func (this *HeroStrengthenUplvView) CreateForm(t *model.TestCase) fyne.CanvasObject {
heroObjID := widget.NewEntry()
key := widget.NewEntry()
key.PlaceHolder = common.APP_TESTCASE_FORM_LABEL_OID
val := widget.NewEntry()
val.PlaceHolder = common.APP_TESTCASE_FORM_LABEL_NUM
key2 := widget.NewEntry()
key2.PlaceHolder = common.APP_TESTCASE_OPTIONS
val2 := widget.NewEntry()
val2.PlaceHolder = common.APP_TESTCASE_OPTIONS
expCards := container.NewGridWithColumns(2, key, val, key2, val2)
this.form.AppendItem(widget.NewFormItem(common.APP_TESTCASE_FORM_LABEL_HEROOBJID, heroObjID))
this.form.AppendItem(widget.NewFormItem(common.APP_TESTCASE_FORM_LABEL_EXPCARDS, expCards))
this.form.OnSubmit = func() {
var cards []*pb.MapStringInt32
if key.Text != "" && val.Text != "" {
cards = append(cards, &pb.MapStringInt32{
Key: key.Text,
Value: cast.ToInt32(val.Text),
})
}
if key2.Text != "" && val2.Text != "" {
cards = append(cards, &pb.MapStringInt32{
Key: key2.Text,
Value: cast.ToInt32(val2.Text),
})
}
head := &pb.UserMessage{MainType: t.MainType, SubType: t.SubType}
if err := service.GetPttService().SendToClient(head,
&pb.HeroStrengthenUplvReq{
HeroObjID: heroObjID.Text,
ExpCards: cards}); err != nil {
logrus.Error(err)
return
}
go service.GetConnService().RespHandle(t)
}
return this.form
}

View File

@ -0,0 +1,35 @@
package formview
import (
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/model"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/pb"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
"github.com/spf13/cast"
)
type TaskActiveListView struct {
BaseformView
}
func (this *TaskActiveListView) CreateForm(t *model.TestCase) fyne.CanvasObject {
tagSelect := getTaskTagSelect()
this.form.AppendItem(widget.NewFormItem(common.APP_TESTCASE_FORM_TASKTAG, tagSelect))
this.form.OnSubmit = func() {
head := &pb.UserMessage{MainType: t.MainType, SubType: t.SubType}
if err := service.GetPttService().SendToClient(head, &pb.TaskActiveListReq{
TaskTag: cast.ToInt32(tagSelect.Selected),
}); err != nil {
logrus.Error(err)
}
go service.GetConnService().RespHandle(t)
}
return this.form
}

View File

@ -0,0 +1,38 @@
package formview
import (
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/model"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/pb"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
"github.com/spf13/cast"
)
type TaskActiveReceiveView struct {
BaseformView
}
func (this *TaskActiveReceiveView) CreateForm(t *model.TestCase) fyne.CanvasObject {
id := widget.NewEntry()
tagSelect := getTaskTagSelect()
this.form.AppendItem(widget.NewFormItem(common.APP_TESTCASE_FORM_TASK_OID, id))
this.form.AppendItem(widget.NewFormItem(common.APP_TESTCASE_FORM_TASKTAG, tagSelect))
this.form.OnSubmit = func() {
head := &pb.UserMessage{MainType: t.MainType, SubType: t.SubType}
if err := service.GetPttService().SendToClient(head, &pb.TaskActiveReceiveReq{
Id: id.Text,
TaskTag: cast.ToInt32(tagSelect.Selected),
}); err != nil {
logrus.Error(err)
}
go service.GetConnService().RespHandle(t)
}
return this.form
}

View File

@ -0,0 +1,34 @@
package formview
import (
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/model"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/pb"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
"github.com/spf13/cast"
)
type TaskListView struct {
BaseformView
}
func (this *TaskListView) CreateForm(t *model.TestCase) fyne.CanvasObject {
tagSelect := getTaskTagSelect()
this.form.AppendItem(widget.NewFormItem(common.APP_TESTCASE_FORM_TASKTAG, tagSelect))
this.form.OnSubmit = func() {
head := &pb.UserMessage{MainType: t.MainType, SubType: t.SubType}
if err := service.GetPttService().SendToClient(head, &pb.TaskListReq{
TaskTag: cast.ToInt32(tagSelect.Selected),
}); err != nil {
logrus.Error(err)
}
go service.GetConnService().RespHandle(t)
}
return this.form
}

View File

@ -0,0 +1,42 @@
package formview
import (
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/model"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/comm"
"go_dreamfactory/modules/task"
"go_dreamfactory/pb"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
"github.com/spf13/cast"
)
type TaskReceiveView struct {
BaseformView
}
func (this *TaskReceiveView) CreateForm(t *model.TestCase) fyne.CanvasObject {
if t.MainType == string(comm.ModuleTask) && t.SubType == task.TaskSubTypeReceive {
id := widget.NewEntry()
tagSelect := getTaskTagSelect()
this.form.AppendItem(widget.NewFormItem(common.APP_TESTCASE_FORM_TASK_OID, id))
this.form.AppendItem(widget.NewFormItem(common.APP_TESTCASE_FORM_TASKTAG, tagSelect))
this.form.OnSubmit = func() {
head := &pb.UserMessage{MainType: t.MainType, SubType: t.SubType}
if err := service.GetPttService().SendToClient(head,
&pb.TaskReceiveReq{Id: id.Text, TaskTag: cast.ToInt32(tagSelect.Selected)}); err != nil {
logrus.Error(err)
return
}
go service.GetConnService().RespHandle(t)
}
}
return this.form
}

View File

@ -0,0 +1,33 @@
package formview
import (
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/model"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/pb"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/widget"
"github.com/sirupsen/logrus"
)
type UserModifynameView struct {
BaseformView
}
func (this *UserModifynameView) CreateForm(t *model.TestCase) fyne.CanvasObject {
name := widget.NewEntry()
this.form.AppendItem(widget.NewFormItem(common.APP_TESTCASE_FORM_NICKNAME, name))
this.form.OnSubmit = func() {
head := &pb.UserMessage{MainType: t.MainType, SubType: t.SubType}
if err := service.GetPttService().SendToClient(head, &pb.UserModifynameReq{Name: name.Text}); err != nil {
logrus.Error(err)
return
}
go service.GetConnService().RespHandle(t)
}
return this.form
}

49
cmd/v2/ui/log_viewer.go Normal file
View File

@ -0,0 +1,49 @@
package ui
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
type logViewer struct {
logArea *widget.Entry
refresh *widget.Button
Win fyne.Window
}
func newLogViewer() *logViewer {
var lv logViewer
lv.logArea = widget.NewMultiLineEntry()
lv.logArea.Disable()
lv.logArea.Wrapping = fyne.TextWrapBreak
// lv.initLogContent()
lv.refresh = widget.NewButtonWithIcon(string(theme.ColorNameFocus), theme.ViewRefreshIcon(), func() {
// lv.initLogContent()
})
lv.Win = fyne.CurrentApp().NewWindow("Show Log")
lv.Win.SetContent(container.NewBorder(
container.NewHBox(layout.NewSpacer(), lv.refresh), nil, nil, nil,
lv.logArea))
lv.Win.Resize(fyne.NewSize(800, 600))
lv.Win.CenterOnScreen()
return &lv
}
// func (lv *logViewer) initLogContent() {
// cont := ""
// globalLogWriter.Traversal(func(s string) {
// cont += s
// })
// lv.logArea.CursorRow = globalLogWriter.Size()
// lv.logArea.SetText(cont)
// }

31
cmd/v2/ui/main_menu.go Normal file
View File

@ -0,0 +1,31 @@
package ui
import (
"fyne.io/fyne/v2"
)
type mainMenu struct {
*fyne.MainMenu
helpMenu *fyne.Menu
sysLog *fyne.MenuItem
// aboutSelf *fyne.MenuItem
}
func newMainMenu() *mainMenu {
var mm mainMenu
// help
mm.sysLog = fyne.NewMenuItem("Show Log", func() {
newLogViewer().Win.Show()
})
mm.helpMenu = fyne.NewMenu("Help",
mm.sysLog,
// mm.aboutSelf,
)
mm.MainMenu = fyne.NewMainMenu(
mm.helpMenu,
)
return &mm
}

View File

@ -1,9 +1,10 @@
package ui package ui
import ( import (
"errors"
"fmt" "fmt"
"go_dreamfactory/cmd/ptt/lib/common" "go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
"go_dreamfactory/pb" "go_dreamfactory/pb"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
@ -28,8 +29,9 @@ type MainWindowImpl struct {
UIImpl UIImpl
WindowDefaultOptions WindowDefaultOptions
w fyne.Window w fyne.Window
mm *mainMenu
tb *toolBar //工具条 tb *toolBar //工具条
toys *toys // toys *toys // side
sb *statusBar //状态栏 sb *statusBar //状态栏
at *appContainer //tabs at *appContainer //tabs
} }
@ -41,6 +43,10 @@ func NewMainWindow(ui *UIImpl) MainWindow {
UIImpl: *ui, UIImpl: *ui,
} }
// main menu
// mw.mm = newMainMenu()
// mw.w.SetMainMenu(mw.mm.MainMenu)
// status bar // status bar
mw.sb = newStatusBar() mw.sb = newStatusBar()
@ -48,15 +54,19 @@ func NewMainWindow(ui *UIImpl) MainWindow {
mw.tb = newToolBar() mw.tb = newToolBar()
// Fun Toys // Fun Toys
mw.toys = newToys() mw.toys = newToys(ui.obs)
// main app tabs // main app tabs
mw.at = newAppContainer() mw.at = newAppContainer(ui.obs)
globalWin = mw globalWin = mw
return mw return mw
} }
func GetGlobalWin() *MainWindowImpl {
return globalWin
}
// createWindowContainer create a container with the window content // createWindowContainer create a container with the window content
func (ui *MainWindowImpl) createWindowContainer() { func (ui *MainWindowImpl) createWindowContainer() {
// create main layout // create main layout
@ -68,14 +78,18 @@ func (ui *MainWindowImpl) createWindowContainer() {
ui.tb = newToolBar() ui.tb = newToolBar()
// Fun Toys // Fun Toys
ui.toys = newToys() ui.toys = newToys(ui.obs)
// main app tabs // main app tabs
ui.at = newAppContainer() ui.at = newAppContainer(ui.obs)
content := container.NewBorder(ui.tb.toolbar, ui.sb.widget, nil, ui.toys.widget, ui.at) content := container.NewBorder(ui.tb.toolbar, ui.sb.widget, nil, ui.toys.widget, ui.at)
ui.w.SetContent(content) ui.w.SetContent(content)
} }
func (ui *MainWindowImpl) SetStatusMsg(msg string) {
ui.sb.setMessage(fmt.Sprintf("err:%s", msg))
}
// CreateWindow .... // CreateWindow ....
func (ui *MainWindowImpl) CreateWindow(title string, width, height float32, _ bool) { func (ui *MainWindowImpl) CreateWindow(title string, width, height float32, _ bool) {
// init window // init window
@ -90,7 +104,8 @@ func (ui *MainWindowImpl) CreateWindow(title string, width, height float32, _ bo
} }
// create main window menu // create main window menu
w.SetMainMenu(ui.createMainWindowMenu()) // ui.mm = newMainMenu()
// w.SetMainMenu(ui.mm.MainMenu)
// create window container // create window container
// mainLayout := ui.createWindowContainer() // mainLayout := ui.createWindowContainer()
@ -121,37 +136,35 @@ func (ui *MainWindowImpl) createChooseServerWindow(
title string, title string,
ch chan int32) fyne.Window { ch chan int32) fyne.Window {
makeButton := func(sid int32, w fyne.Window) *widget.Button { makeButton := func(s *service.ServiceConf, parent fyne.Window) *widget.Button {
serverName := "service " + cast.ToString(sid) btn := widget.NewButton(s.Name, func() {
btn := widget.NewButton(serverName, func() { d := dialog.NewInformation("", common.INFO_WAIT, parent)
d := dialog.NewInformation("req", common.INFO_WAIT, w)
d.SetDismissText(common.BUTTON_CANCEL) d.SetDismissText(common.BUTTON_CANCEL)
d.Show() d.Show()
defer d.Hide()
logrus.WithField("server", serverName).Debug("choose server") logrus.WithField("server", s.Name).Debug("choose server")
ui.sb.setMessage(serverName)
//conn server //conn server
if err := ui.connService.Connect("ws://localhost:7891/gateway"); err != nil { if err := ui.connService.Connect(s.Url); err != nil {
logrus.Error(err) d.Hide()
derr := errors.New("conn err") dialog.ShowError(err, parent)
dialog.ShowError(derr, w)
} else { } else {
ch <- sid ch <- s.SId
} }
}) })
//other btn setting //other btn setting
return btn return btn
} }
w := fyne.CurrentApp().NewWindow(title)
sGrid := container.NewGridWithColumns(2)
config := ui.configService.GetConfig()
if config != nil {
for _, s := range config.Services {
box := makeButton(s.Service, w)
sGrid.Add(box)
}
}
w := ui.app.NewWindow(title) w.SetContent(sGrid)
box1 := makeButton(1, w)
box2 := makeButton(2, w)
box3 := makeButton(3, w)
box4 := makeButton(4, w)
w.SetContent(container.NewGridWithColumns(2, box1, box2, box3, box4))
w.SetFixedSize(true) w.SetFixedSize(true)
w.Resize(fyne.NewSize(500, 200)) w.Resize(fyne.NewSize(500, 200))
w.Show() w.Show()
@ -163,6 +176,7 @@ func (ui *MainWindowImpl) createChooseServerWindow(
func (ui *MainWindowImpl) createLoginWin(sid int32) { func (ui *MainWindowImpl) createLoginWin(sid int32) {
//form //form
account := widget.NewEntry() account := widget.NewEntry()
account.Text = "user8120" //default account
// account.Validator = validation.NewRegexp(`^(\s*)$`, "account required") // account.Validator = validation.NewRegexp(`^(\s*)$`, "account required")
// password := widget.NewPasswordEntry() // password := widget.NewPasswordEntry()
items := []*widget.FormItem{ items := []*widget.FormItem{
@ -184,7 +198,8 @@ func (ui *MainWindowImpl) createLoginWin(sid int32) {
//show mainwindow //show mainwindow
logrus.Debug(rsp) logrus.Debug(rsp)
ui.pttService.SetUser(rsp.Data, rsp.Ex) ui.pttService.SetUser(rsp.Data, rsp.Ex)
// TODO isCreateRole ui.obs.Notify(observer.EVENT_USERINFO, ui.pttService.GetUser())
// isCreateRole
if rsp.Data.Created { if rsp.Data.Created {
// ui.renderUserContainer() // ui.renderUserContainer()
ui.createWindowContainer() ui.createWindowContainer()
@ -213,6 +228,7 @@ func (ui *MainWindowImpl) createMainWindowMenu() *fyne.MainMenu {
Label: common.MENU_FILE, Label: common.MENU_FILE,
Items: menuItems, Items: menuItems,
} }
return fyne.NewMainMenu(&menu) return fyne.NewMainMenu(&menu)
} }

125
cmd/v2/ui/protocol.go Normal file
View File

@ -0,0 +1,125 @@
package ui
import (
"fmt"
"go_dreamfactory/cmd/v2/model"
"go_dreamfactory/comm"
"go_dreamfactory/lego/core"
"go_dreamfactory/modules/hero"
"go_dreamfactory/modules/task"
"go_dreamfactory/pb"
"fyne.io/fyne/v2"
"google.golang.org/protobuf/proto"
)
type MyCaseView interface {
Init()
CreateForm(t *model.TestCase) fyne.CanvasObject
}
// nav
var (
CaseNav = map[string]*model.TestCase{
// user
"user": {
MainType: "user",
Enabled: true,
},
"user.modifyname": {
Desc: "用户昵称修改",
MainType: "user",
SubType: "modifyname",
Req: &pb.UserModifynameReq{},
Rsp: &pb.UserModifynameResp{},
Print: func(rsp proto.Message) string {
r := rsp.(*pb.UserModifynameResp)
return fmt.Sprintf("Uid:%s count:%d", r.Uid, r.Count)
},
Enabled: true,
},
// task
"task": {
MainType: "task",
Enabled: true,
},
"task.list": {
Desc: "用户任务列表",
MainType: "task",
SubType: "list",
Req: &pb.TaskListReq{},
Rsp: &pb.TaskListResp{},
Enabled: true,
},
"task.receive": {
Desc: "用户任务领取",
MainType: "task",
SubType: "receive",
Enabled: true,
},
ff(comm.ModuleTask, task.TaskSubTypeActiveList): {
Desc: "用户活跃度列表",
MainType: string(comm.ModuleTask),
SubType: task.TaskSubTypeActiveList,
Req: &pb.TaskActiveListReq{},
Rsp: &pb.TaskActiveListResp{},
Enabled: true,
},
ff(comm.ModuleTask, task.TaskSubTypeActiveReceive): {
Desc: "用户活跃度领取",
MainType: string(comm.ModuleTask),
SubType: task.TaskSubTypeActiveReceive,
Req: &pb.TaskActiveReceiveReq{},
Rsp: &pb.TaskActiveReceiveResp{},
Enabled: true,
},
// hero
string(comm.ModuleHero): {
MainType: string(comm.ModuleHero),
Enabled: true,
},
ff(comm.ModuleHero, hero.HeroSubTypeList): {
Desc: "英雄列表",
MainType: string(comm.ModuleHero),
SubType: hero.HeroSubTypeList,
Req: &pb.HeroListReq{},
Rsp: &pb.HeroListResp{},
// Print: func(rsp proto.Message) string{
// out := rsp.(*pb.HeroListResp)
// for i, v := range out.List {
// fmt.Printf("%d- %v\n", (i + 1), v)
// }
// },
Enabled: true,
},
ff(comm.ModuleHero, hero.StrengthenUplv): {
Desc: "英雄等级升级",
MainType: string(comm.ModuleHero),
SubType: hero.StrengthenUplv,
Req: &pb.HeroStrengthenUplvReq{},
Rsp: &pb.HeroStrengthenUplvResp{},
Enabled: true,
},
ff(comm.ModuleHero, hero.StrengthenUpStar): {
Desc: "英雄星级升级",
MainType: string(comm.ModuleHero),
SubType: hero.StrengthenUpStar,
Req: &pb.HeroStrengthenUpStarReq{},
Rsp: &pb.HeroStrengthenUpStarResp{},
Enabled: true,
},
}
)
var (
CaseIndex = map[string][]string{
"": {"user", "hero", "task"},
"user": {"user.modifyname"},
"hero": {"hero.list", "hero.strengthenuplv", "hero.strengthenupstar"},
"task": {"task.list", "task.receive", "task.activelist", "task.activereceive"},
}
)
func ff(s1 core.M_Modules, s2 string) string {
return fmt.Sprintf("%s.%s", s1, s2)
}

View File

@ -2,7 +2,7 @@ package ui
import ( import (
"fmt" "fmt"
"go_dreamfactory/cmd/ptt/lib/common" "go_dreamfactory/cmd/v2/lib/common"
"fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/widget"
@ -34,7 +34,7 @@ func openApp(name string) {
if app.GetAppName() == name { if app.GetAppName() == name {
err := globalWin.at.openApp(app) err := globalWin.at.openApp(app)
if err != nil { if err != nil {
logrus.Error(fmt.Errorf("theme.RunAppFailedFormat", app.GetAppName(), err)) logrus.Error(fmt.Errorf("%s %v", app.GetAppName(), err))
} }
} }
} }

View File

@ -1,7 +1,8 @@
package ui package ui
import ( import (
"go_dreamfactory/cmd/ptt/lib/common" "go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/service/observer"
"time" "time"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
@ -19,7 +20,7 @@ type toyDateTime struct {
time *canvas.Text time *canvas.Text
} }
func (tdt *toyDateTime) Init() error { func (tdt *toyDateTime) Init(obs observer.Observer) error {
tdt.date = canvas.NewText("", nil) tdt.date = canvas.NewText("", nil)
tdt.date.TextSize = 15 tdt.date.TextSize = 15

View File

@ -1,16 +1,20 @@
package ui package ui
import "fyne.io/fyne/v2/widget" import (
"go_dreamfactory/cmd/v2/service/observer"
"fyne.io/fyne/v2/widget"
)
type toyInterface interface { type toyInterface interface {
Init() error Init(obs observer.Observer) error
GetToyCard() *widget.Card GetToyCard() *widget.Card
} }
var ( var (
toyRegister = []toyInterface{ toyRegister = []toyInterface{
&toyDateTime{}, &toyDateTime{},
&toyUserInfo{},
} }
) )
@ -22,7 +26,7 @@ type toyAdapter struct {
widget *widget.Card widget *widget.Card
} }
func (t toyAdapter) Init() error { func (t toyAdapter) Init(obs observer.Observer) error {
panic("implement Init") panic("implement Init")
} }

84
cmd/v2/ui/toy_userinfo.go Normal file
View File

@ -0,0 +1,84 @@
package ui
import (
"fmt"
"go_dreamfactory/cmd/v2/lib/common"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/widget"
"github.com/atotto/clipboard"
"github.com/spf13/cast"
)
type toyUserInfo struct {
toyAdapter
dataList *widget.List
titleLabel *widget.Label
userInfo *service.UserInfo
data binding.ExternalStringList
obs observer.Observer
copyBtn *widget.Button
}
func (this *toyUserInfo) Init(obs observer.Observer) error {
this.obs = obs
this.titleLabel = widget.NewLabel(common.USERINFO_TITLE)
this.data = binding.BindStringList(&[]string{})
this.copyBtn = widget.NewButton(common.USERINFO_BTN_COPY, func() {
if this.userInfo != nil && this.userInfo.DbUser != nil {
_ = clipboard.WriteAll(this.userInfo.DbUser.Uid)
}
})
this.copyBtn.Disable()
this.dataList = widget.NewListWithData(this.data,
func() fyne.CanvasObject {
return widget.NewLabel("template")
},
func(i binding.DataItem, o fyne.CanvasObject) {
o.(*widget.Label).Bind(i.(binding.String))
},
)
this.widget = widget.NewCard("", "",
container.NewBorder(container.NewHBox(this.titleLabel, layout.NewSpacer(), this.copyBtn),
nil, nil, nil, container.NewVScroll(this.dataList)))
this.widget.Resize(fyne.NewSize(ToyWidth, 600))
this.Run()
return nil
}
func (this *toyUserInfo) Run() {
this.obs.AddListener(observer.EVENT_USERINFO, observer.Listener{
OnNotify: func(d interface{}, args ...interface{}) {
time.Sleep(time.Millisecond * 50)
if this.copyBtn.Disabled() {
this.copyBtn.Enable()
}
this.userInfo = d.(*service.UserInfo)
if this.userInfo == nil {
return
}
_ = this.data.Append(fmt.Sprintf("%-3s\t: %s", common.USERINFO_UID, this.userInfo.DbUser.Uid))
_ = this.data.Append(fmt.Sprintf("%-3s\t: %s", common.USERINFO_ACC, this.userInfo.DbUser.Binduid))
_ = this.data.Append(fmt.Sprintf("%-3s\t: %s", common.USERINFO_NAME, this.userInfo.DbUser.Name))
_ = this.data.Append(fmt.Sprintf("%-3s\t: %s", common.USERINFO_AVATAR, cast.ToString(this.userInfo.DbUser.Avatar)))
_ = this.data.Append(fmt.Sprintf("%-3s\t: %s", common.USERINFO_VIP, cast.ToString(this.userInfo.DbUser.Vip)))
_ = this.data.Append(fmt.Sprintf("%-3s\t: %s", common.USERINFO_LV, cast.ToString(this.userInfo.DbUser.Lv)))
_ = this.data.Append(fmt.Sprintf("%-3s\t: %s", common.USERINFO_GOLD, cast.ToString(this.userInfo.DbUser.Gold)))
_ = this.data.Append(fmt.Sprintf("%-3s\t: %s", common.USERINFO_EXP, cast.ToString(this.userInfo.DbUser.Exp)))
},
})
}

View File

@ -1,6 +1,8 @@
package ui package ui
import ( import (
"go_dreamfactory/cmd/v2/service/observer"
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/container"
) )
@ -9,13 +11,13 @@ type toys struct {
widget *fyne.Container widget *fyne.Container
} }
func newToys() *toys { func newToys(obs observer.Observer) *toys {
var t toys var t toys
cards := make([]fyne.CanvasObject, len(toyRegister)) cards := make([]fyne.CanvasObject, len(toyRegister))
for i, toy := range toyRegister { for i, toy := range toyRegister {
toy := toy toy := toy
err := toy.Init() err := toy.Init(obs)
if err != nil { if err != nil {
panic(err) panic(err)
} }

60
cmd/v2/ui/ui.go Normal file
View File

@ -0,0 +1,60 @@
package ui
import (
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
"sync"
"go_dreamfactory/cmd/v2/theme"
"fyne.io/fyne/v2"
)
type UI interface {
AddWindow(name string, w fyne.Window)
Run()
Stop()
}
type UIImpl struct {
app fyne.App
windows map[string]fyne.Window
winMux *sync.Mutex
connService service.ConnService
pttService service.PttService
configService service.ConfigService
obs observer.Observer
}
func NewUI(
app fyne.App,
configService service.ConfigService,
connService service.ConnService,
pttService service.PttService,
obs observer.Observer,
) *UIImpl {
app.Settings().SetTheme(&theme.MyTheme{})
return &UIImpl{
app: app,
windows: make(map[string]fyne.Window),
winMux: &sync.Mutex{},
configService: configService,
connService: connService,
pttService: pttService,
obs: obs,
}
}
func (ui *UIImpl) AddWindow(name string, w fyne.Window) {
ui.winMux.Lock()
defer ui.winMux.Unlock()
ui.windows[name] = w
}
func (ui *UIImpl) Run() {
ui.app.Run()
}
func (ui *UIImpl) Stop() {
ui.app.Quit()
}

9
go.mod
View File

@ -17,6 +17,8 @@ require (
github.com/json-iterator/go v1.1.12 github.com/json-iterator/go v1.1.12
github.com/modern-go/reflect2 v1.0.2 github.com/modern-go/reflect2 v1.0.2
github.com/nacos-group/nacos-sdk-go v1.0.8 github.com/nacos-group/nacos-sdk-go v1.0.8
github.com/natefinch/lumberjack v2.0.0+incompatible
github.com/pelletier/go-toml v1.9.3
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
github.com/rs/xid v1.3.0 github.com/rs/xid v1.3.0
@ -26,6 +28,7 @@ require (
github.com/spf13/cast v1.3.1 github.com/spf13/cast v1.3.1
github.com/spf13/cobra v1.2.1 github.com/spf13/cobra v1.2.1
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.8.1
github.com/tidwall/gjson v1.14.1 github.com/tidwall/gjson v1.14.1
github.com/ugorji/go/codec v1.2.7 github.com/ugorji/go/codec v1.2.7
github.com/valyala/fastrand v1.1.0 github.com/valyala/fastrand v1.1.0
@ -43,6 +46,7 @@ require (
github.com/andybalholm/cascadia v1.3.1 // indirect github.com/andybalholm/cascadia v1.3.1 // indirect
github.com/apache/thrift v0.16.0 // indirect github.com/apache/thrift v0.16.0 // indirect
github.com/armon/go-metrics v0.3.10 // indirect github.com/armon/go-metrics v0.3.10 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aws/aws-sdk-go v1.34.28 // indirect github.com/aws/aws-sdk-go v1.34.28 // indirect
github.com/cenk/backoff v2.2.1+incompatible // indirect github.com/cenk/backoff v2.2.1+incompatible // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect
@ -86,6 +90,7 @@ require (
github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/serf v0.9.7 // indirect github.com/hashicorp/serf v0.9.7 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c // indirect github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c // indirect
@ -102,6 +107,7 @@ require (
github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect
github.com/lucas-clemente/quic-go v0.27.0 // indirect github.com/lucas-clemente/quic-go v0.27.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect
github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect
github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect github.com/marten-seemann/qtls-go1-18 v0.1.1 // indirect
@ -125,9 +131,12 @@ require (
github.com/shirou/gopsutil/v3 v3.22.2 // indirect github.com/shirou/gopsutil/v3 v3.22.2 // indirect
github.com/smallnest/quick v0.0.0-20220103065406-780def6371e6 // indirect github.com/smallnest/quick v0.0.0-20220103065406-780def6371e6 // indirect
github.com/soheilhy/cmux v0.1.5 // indirect github.com/soheilhy/cmux v0.1.5 // indirect
github.com/spf13/afero v1.6.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 // indirect github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 // indirect
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 // indirect github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 // indirect
github.com/stretchr/testify v1.7.2 // indirect github.com/stretchr/testify v1.7.2 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b // indirect github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b // indirect
github.com/tevino/abool v1.2.0 // indirect github.com/tevino/abool v1.2.0 // indirect

9
go.sum
View File

@ -80,6 +80,8 @@ github.com/armon/go-metrics v0.3.10 h1:FR+drcQStOe+32sYyJYyZ7FIdgoGGBnwLl+flodp8
github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aws/aws-sdk-go v1.34.28 h1:sscPpn/Ns3i0F4HPEWAVcwdIRaZZCuL7llJ2/60yPIk= github.com/aws/aws-sdk-go v1.34.28 h1:sscPpn/Ns3i0F4HPEWAVcwdIRaZZCuL7llJ2/60yPIk=
github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ= github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ=
@ -379,6 +381,7 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
@ -472,6 +475,7 @@ github.com/lucor/goinfo v0.0.0-20210802170112-c078a2b0f08b/go.mod h1:PRq09yoB+Q2
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
@ -566,6 +570,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/peterbourgon/g2s v0.0.0-20140925154142-ec76db4c1ac1 h1:5Dl+ADmsGerAqHwWzyLqkNaUBQ+48DQwfDCaW1gHAQM= github.com/peterbourgon/g2s v0.0.0-20140925154142-ec76db4c1ac1 h1:5Dl+ADmsGerAqHwWzyLqkNaUBQ+48DQwfDCaW1gHAQM=
github.com/peterbourgon/g2s v0.0.0-20140925154142-ec76db4c1ac1/go.mod h1:1VcHEd3ro4QMoHfiNl/j7Jkln9+KQuorp0PItHMJYNg= github.com/peterbourgon/g2s v0.0.0-20140925154142-ec76db4c1ac1/go.mod h1:1VcHEd3ro4QMoHfiNl/j7Jkln9+KQuorp0PItHMJYNg=
@ -674,16 +679,19 @@ github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 h1:HunZiaEKNGVdhTRQOVpMmj5MQnGnv+e8uZNu3xFLgyM= github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 h1:HunZiaEKNGVdhTRQOVpMmj5MQnGnv+e8uZNu3xFLgyM=
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4= github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
@ -702,6 +710,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/tebeka/strftime v0.1.3 h1:5HQXOqWKYRFfNyBMNVc9z5+QzuBtIXy03psIhtdJYto= github.com/tebeka/strftime v0.1.3 h1:5HQXOqWKYRFfNyBMNVc9z5+QzuBtIXy03psIhtdJYto=