Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

View old history log files #30

Merged
merged 1 commit into from
Jan 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
318 changes: 160 additions & 158 deletions i18n/catalog.go

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions i18n/locales/en-US/messages.gotext.json
Original file line number Diff line number Diff line change
Expand Up @@ -1159,6 +1159,13 @@
"translatorComment": "Copied from source.",
"fuzzy": true
},
{
"id": "Latest",
"message": "Latest",
"translation": "Latest",
"translatorComment": "Copied from source.",
"fuzzy": true
},
{
"id": "Unknown",
"message": "Unknown",
Expand Down
5 changes: 5 additions & 0 deletions i18n/locales/es-ES/messages.gotext.json
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,11 @@
"message": "Open Log Folder",
"translation": "Abrir registro"
},
{
"id": "Latest",
"message": "Latest",
"translation": "Último"
},
{
"id": "Unknown",
"message": "Unknown",
Expand Down
5 changes: 5 additions & 0 deletions i18n/locales/ja-JP/messages.gotext.json
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,11 @@
"message": "Open Log Folder",
"translation": "ログフォルダを開く"
},
{
"id": "Latest",
"message": "Latest",
"translation": "最新"
},
{
"id": "Unknown",
"message": "Unknown",
Expand Down
5 changes: 5 additions & 0 deletions i18n/locales/ko-KR/messages.gotext.json
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,11 @@
"message": "Open Log Folder",
"translation": "로그 폴더 열기"
},
{
"id": "Latest",
"message": "Latest",
"translation": "최신"
},
{
"id": "Unknown",
"message": "Unknown",
Expand Down
5 changes: 5 additions & 0 deletions i18n/locales/zh-CN/messages.gotext.json
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,11 @@
"message": "Open Log Folder",
"translation": "打开日志文件夹"
},
{
"id": "Latest",
"message": "Latest",
"translation": "最新"
},
{
"id": "Unknown",
"message": "Unknown",
Expand Down
5 changes: 5 additions & 0 deletions i18n/locales/zh-TW/messages.gotext.json
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,11 @@
"message": "Open Log Folder",
"translation": "打開日誌文件夾"
},
{
"id": "Latest",
"message": "Latest",
"translation": "最新"
},
{
"id": "Unknown",
"message": "Unknown",
Expand Down
117 changes: 101 additions & 16 deletions ui/logpage.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,36 @@ import (
"github.com/lxn/walk"
. "github.com/lxn/walk/declarative"
"github.com/thoas/go-funk"
"sort"
"sync"
"time"
)

type LogPage struct {
*walk.TabPage
sync.Mutex

nameModel *ListModel
dateModel []*StringPair
logModel *LogModel
db *walk.DataBinder
logFileChan chan string
logFileChan chan logSelect

// Views
logView *walk.TableView
nameView *walk.ComboBox
dateView *walk.ComboBox
}

type logSelect struct {
path string
// main defines whether the log file is used by config now.
main bool
}

func NewLogPage() *LogPage {
v := new(LogPage)
v.logFileChan = make(chan string)
v.logFileChan = make(chan logSelect)
v.logModel = NewLogModel("")
return v
}
Expand All @@ -35,16 +46,20 @@ func (lp *LogPage) Page() TabPage {
Title: i18n.Sprintf("Log"),
Layout: VBox{},
Children: []Widget{
ComboBox{
AssignTo: &lp.nameView,
OnCurrentIndexChanged: func() {
index := lp.nameView.CurrentIndex()
if index < 0 || lp.nameModel == nil {
// No config selected, the log page should be empty
lp.logFileChan <- ""
return
}
lp.logFileChan <- lp.nameModel.items[index].Data.GetLogFile()
Composite{
Layout: HBox{MarginsZero: true},
Children: []Widget{
ComboBox{
AssignTo: &lp.nameView,
StretchFactor: 2,
OnCurrentIndexChanged: lp.switchLogName,
},
ComboBox{
AssignTo: &lp.dateView,
StretchFactor: 1,
DisplayMember: "DisplayName",
OnCurrentIndexChanged: lp.switchLogDate,
},
},
},
TableView{
Expand Down Expand Up @@ -83,23 +98,75 @@ func (lp *LogPage) OnCreate() {
ticker := time.NewTicker(time.Second * 5)
go func() {
defer ticker.Stop()
var lastLog string
for {
select {
case logFile := <-lp.logFileChan:
lp.logModel = NewLogModel(logFile)
// CurrentIndexChanged event may be triggered multiple times.
// Try to avoid duplicate operations.
if lastLog != "" && logFile.path == lastLog {
continue
}
lastLog = logFile.path

lp.Lock()
lp.logModel = NewLogModel(logFile.path)
lp.logModel.Reset()
// A change of main log name.
// The available date list need to be updated.
if logFile.main {
fl, dl, _ := util.FindLogFiles(logFile.path)
lp.dateModel = NewStringPairModel(fl, dl, i18n.Sprintf("Latest"))
sort.SliceStable(lp.dateModel, func(i, j int) bool {
t1, err := time.Parse("2006-01-02", lp.dateModel[i].DisplayName)
if err != nil {
// Put non-date string at top.
return true
}
t2, err := time.Parse("2006-01-02", lp.dateModel[j].DisplayName)
if err != nil {
// Put non-date string at top.
return false
}
return t1.After(t2)
})
}
lp.Unlock()

lp.Synchronize(func() {
lp.Lock()
defer lp.Unlock()
lp.db.Reset()
lp.logView.SetModel(lp.logModel)
if logFile.main {
// A change of main log name always reads the latest log file.
// So there's no need to trigger another same operation.
// Moreover, the update of model in date view will trigger
// CurrentIndexChanged event which may cause a deadlock.
// Thus, we must disable this event first.
lp.dateView.CurrentIndexChanged().Detach(0)
lp.dateView.SetModel(lp.dateModel)
if len(lp.dateModel) > 0 {
lp.dateView.SetCurrentIndex(0)
}
// We are safe to restore the event now.
lp.dateView.CurrentIndexChanged().Attach(lp.switchLogDate)
}
lp.scrollToBottom()
})
case <-ticker.C:
// We should only read log when the log page is visible
if !lp.Visible() {
// We should only read log when the log page is visible.
// Also, there's no need to reload the backup log.
if !lp.Visible() || lp.dateView.CurrentIndex() > 0 {
continue
}
if err := lp.logModel.Reset(); err == nil {
lp.Lock()
err := lp.logModel.Reset()
lp.Unlock()
if err == nil {
lp.Synchronize(func() {
lp.Lock()
defer lp.Unlock()
lp.logView.SetModel(lp.logModel)
lp.scrollToBottom()
})
Expand Down Expand Up @@ -153,3 +220,21 @@ func (lp *LogPage) hasLogFile() bool {
}
return false
}

func (lp *LogPage) switchLogName() {
index := lp.nameView.CurrentIndex()
if index < 0 || lp.nameModel == nil {
// No config selected, the log page should be empty
lp.logFileChan <- logSelect{"", true}
return
}
lp.logFileChan <- logSelect{lp.nameModel.items[index].Data.GetLogFile(), true}
}

func (lp *LogPage) switchLogDate() {
index := lp.dateView.CurrentIndex()
if index < 0 || lp.dateModel == nil {
return
}
lp.logFileChan <- logSelect{lp.dateModel[index].Name, false}
}
26 changes: 20 additions & 6 deletions ui/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,17 +88,31 @@ func (m *ProxyModel) Items() interface{} {
return m.items
}

// DefaultListModel has a default item at the top of the model
type DefaultListModel struct {
// StringPair is a simple struct to hold a pair of strings.
type StringPair struct {
Name string
DisplayName string
}

func NewDefaultListModel(items []string, defaultKey string, defaultName string) []*DefaultListModel {
listItems := make([]*DefaultListModel, 0, len(items)+1)
listItems = append(listItems, &DefaultListModel{Name: defaultKey, DisplayName: defaultName})
// NewDefaultListModel creates a default item at the top of the model.
func NewDefaultListModel(items []string, defaultKey string, defaultName string) []*StringPair {
listItems := make([]*StringPair, 0, len(items)+1)
listItems = append(listItems, &StringPair{Name: defaultKey, DisplayName: defaultName})
for _, item := range items {
listItems = append(listItems, &DefaultListModel{Name: item, DisplayName: item})
listItems = append(listItems, &StringPair{Name: item, DisplayName: item})
}
return listItems
}

// NewStringPairModel creates a slice of string pair from two string slices.
func NewStringPairModel(keys []string, values []string, defaultValue string) []*StringPair {
listItems := make([]*StringPair, 0, len(keys))
for i, k := range keys {
pair := &StringPair{Name: k, DisplayName: values[i]}
if pair.DisplayName == "" {
pair.DisplayName = defaultValue
}
listItems = append(listItems, pair)
}
return listItems
}
Expand Down