Skip to content

Commit

Permalink
Show training for characters in own tab
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikKalkoken committed Dec 14, 2024
1 parent 6f678a0 commit 5d6cb9a
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 45 deletions.
44 changes: 5 additions & 39 deletions internal/app/ui/overview.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ type overviewCharacter struct {
lastLoginAt optional.Optional[time.Time]
name string
security float64
totalSP optional.Optional[int]
training optional.Optional[time.Duration]
unallocatedSP optional.Optional[int]
unreadCount optional.Optional[int]
walletBalance optional.Optional[float64]
}
Expand Down Expand Up @@ -67,9 +64,6 @@ func (a *overviewArea) makeTable() *widget.Table {
{"Alliance", 20},
{"Security", 5},
{"Unread", 5},
{"Total SP", 5},
{"Unall. SP", 5},
{"Training", 5},
{"Wallet", 5},
{"Assets", 5},
{"Last Login", 10},
Expand Down Expand Up @@ -112,29 +106,16 @@ func (a *overviewArea) makeTable() *widget.Table {
text = ihumanize.Optional(c.unreadCount, "?")
l.Alignment = fyne.TextAlignTrailing
case 5:
text = ihumanize.Optional(c.totalSP, "?")
l.Alignment = fyne.TextAlignTrailing
case 6:
text = ihumanize.Optional(c.unallocatedSP, "?")
l.Alignment = fyne.TextAlignTrailing
case 7:
if c.training.IsEmpty() {
text = "Inactive"
l.Importance = widget.WarningImportance
} else {
text = ihumanize.Duration(c.training.MustValue())
}
case 8:
text = ihumanize.OptionalFloat(c.walletBalance, 1, "?")
l.Alignment = fyne.TextAlignTrailing
case 9:
case 6:
text = ihumanize.OptionalFloat(c.assetValue, 1, "?")
l.Alignment = fyne.TextAlignTrailing
case 10:
case 7:
text = ihumanize.Optional(c.lastLoginAt, "?")
case 11:
case 8:
text = entityNameOrFallback(c.home, "?")
case 12:
case 9:
text = humanize.RelTime(c.birthday, time.Now(), "", "")
l.Alignment = fyne.TextAlignTrailing
}
Expand Down Expand Up @@ -176,14 +157,12 @@ func (a *overviewArea) refresh() {
}
walletText := ihumanize.OptionalFloat(totals.wallet, 1, "?")
assetsText := ihumanize.OptionalFloat(totals.assets, 1, "?")
spText := ihumanize.Optional(totals.sp, "?")
unreadText := ihumanize.Optional(totals.unread, "?")
s := fmt.Sprintf(
"%d characters • %s ISK wallet • %s ISK assets • %s SP • %s unread",
"%d characters • %s ISK wallet • %s ISK assets • %s unread",
len(a.characters),
walletText,
assetsText,
spText,
unreadText,
)
return s, widget.MediumImportance, nil
Expand All @@ -199,7 +178,6 @@ func (a *overviewArea) refresh() {
}

type overviewTotals struct {
sp optional.Optional[int]
unread optional.Optional[int]
wallet optional.Optional[float64]
assets optional.Optional[float64]
Expand All @@ -223,8 +201,6 @@ func (a *overviewArea) updateCharacters() (overviewTotals, error) {
id: m.ID,
name: m.EveCharacter.Name,
security: m.EveCharacter.SecurityStatus,
totalSP: m.TotalSP,
unallocatedSP: m.UnallocatedSP,
walletBalance: m.WalletBalance,
}
if m.Home != nil {
Expand All @@ -235,13 +211,6 @@ func (a *overviewArea) updateCharacters() (overviewTotals, error) {
}
cc[i] = c
}
for i, c := range cc {
v, err := a.u.CharacterService.GetCharacterTotalTrainingTime(ctx, c.id)
if err != nil {
return totals, err
}
cc[i].training = v
}
for i, c := range cc {
total, unread, err := a.u.CharacterService.GetCharacterMailCounts(ctx, c.id)
if err != nil {
Expand All @@ -259,9 +228,6 @@ func (a *overviewArea) updateCharacters() (overviewTotals, error) {
cc[i].assetValue = v
}
for _, c := range cc {
if !c.totalSP.IsEmpty() {
totals.sp.Set(totals.sp.ValueOrZero() + c.totalSP.MustValue())
}
if !c.unreadCount.IsEmpty() {
totals.unread.Set(totals.unread.ValueOrZero() + c.unreadCount.MustValue())
}
Expand Down
175 changes: 175 additions & 0 deletions internal/app/ui/training.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package ui

import (
"context"
"fmt"
"log/slog"
"strings"
"time"

"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/widget"

ihumanize "github.com/ErikKalkoken/evebuddy/internal/app/humanize"
"github.com/ErikKalkoken/evebuddy/internal/optional"
)

type trainingCharacter struct {
id int32
name string
totalSP optional.Optional[int]
training optional.Optional[time.Duration]
unallocatedSP optional.Optional[int]
}

// trainingArea is the UI area that shows an overview of all the user's characters.
type trainingArea struct {
characters []trainingCharacter
content *fyne.Container
table *widget.Table
top *widget.Label
u *UI
}

func (u *UI) newTrainingArea() *trainingArea {
a := trainingArea{
characters: make([]trainingCharacter, 0),
top: widget.NewLabel(""),
u: u,
}
a.top.TextStyle.Bold = true

top := container.NewVBox(a.top, widget.NewSeparator())
a.table = a.makeTable()
a.content = container.NewBorder(top, nil, nil, nil, a.table)
return &a
}

func (a *trainingArea) makeTable() *widget.Table {
var headers = []struct {
text string
maxChars int
}{
{"Name", 20},
{"SP", 5},
{"Unall. SP", 5},
{"Training", 5},
}

t := widget.NewTable(
func() (rows int, cols int) {
return len(a.characters), len(headers)
},
func() fyne.CanvasObject {
return widget.NewLabel("Template")
},
func(tci widget.TableCellID, co fyne.CanvasObject) {
l := co.(*widget.Label)
if tci.Row >= len(a.characters) || tci.Row < 0 {
return
}
c := a.characters[tci.Row]
l.Alignment = fyne.TextAlignLeading
l.Importance = widget.MediumImportance
var text string
switch tci.Col {
case 0:
text = c.name
case 1:
text = ihumanize.Optional(c.totalSP, "?")
l.Alignment = fyne.TextAlignTrailing
case 2:
text = ihumanize.Optional(c.unallocatedSP, "?")
l.Alignment = fyne.TextAlignTrailing
case 3:
if c.training.IsEmpty() {
text = "Inactive"
l.Importance = widget.WarningImportance
} else {
text = ihumanize.Duration(c.training.MustValue())
}
}
l.Text = text
l.Truncation = fyne.TextTruncateClip
l.Refresh()
},
)
t.ShowHeaderRow = true
t.StickyColumnCount = 1
t.CreateHeader = func() fyne.CanvasObject {
return widget.NewLabel("Template")
}
t.UpdateHeader = func(tci widget.TableCellID, co fyne.CanvasObject) {
s := headers[tci.Col]
label := co.(*widget.Label)
label.SetText(s.text)
}
t.OnSelected = func(tci widget.TableCellID) {
defer t.UnselectAll()
}

for i, h := range headers {
x := widget.NewLabel(strings.Repeat("w", h.maxChars))
w := x.MinSize().Width
t.SetColumnWidth(i, w)
}
return t
}

func (a *trainingArea) refresh() {
t, i, err := func() (string, widget.Importance, error) {
totalSP, err := a.updateCharacters()
if err != nil {
return "", 0, err
}
if len(a.characters) == 0 {
return "No characters", widget.LowImportance, nil
}
spText := ihumanize.Optional(totalSP, "?")
s := fmt.Sprintf("%d characters • %s Total SP", len(a.characters), spText)
return s, widget.MediumImportance, nil
}()
if err != nil {
slog.Error("Failed to refresh overview UI", "err", err)
t = "ERROR"
i = widget.DangerImportance
}
a.top.Text = t
a.top.Importance = i
a.table.Refresh()
}

func (a *trainingArea) updateCharacters() (optional.Optional[int], error) {
var totalSP optional.Optional[int]
var err error
ctx := context.TODO()
mycc, err := a.u.CharacterService.ListCharacters(ctx)
if err != nil {
return totalSP, err
}
cc := make([]trainingCharacter, len(mycc))
for i, m := range mycc {
c := trainingCharacter{
id: m.ID,
name: m.EveCharacter.Name,
totalSP: m.TotalSP,
unallocatedSP: m.UnallocatedSP,
}
cc[i] = c
}
for i, c := range cc {
v, err := a.u.CharacterService.GetCharacterTotalTrainingTime(ctx, c.id)
if err != nil {
return totalSP, err
}
cc[i].training = v
}
for _, c := range cc {
if !c.totalSP.IsEmpty() {
totalSP.Set(totalSP.ValueOrZero() + c.totalSP.ValueOrZero())
}
}
a.characters = cc
return totalSP, nil
}
10 changes: 7 additions & 3 deletions internal/app/ui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ type UI struct {
statusBarArea *statusBarArea
statusWindow fyne.Window
tabs *container.AppTabs
trainingArea *trainingArea
toolbarArea *toolbarArea
walletJournalArea *walletJournalArea
walletTab *container.TabItem
Expand Down Expand Up @@ -133,13 +134,15 @@ func NewUI(fyneApp fyne.App, ad appdirs.AppDirs) *UI {

u.overviewArea = u.newOverviewArea()
u.locationsArea = u.newLocationsArea()
u.trainingArea = u.newTrainingArea()
u.assetSearchArea = u.newAssetSearchArea()
u.coloniesArea = u.newColoniesArea()
u.wealthArea = u.newWealthArea()
u.overviewTab = container.NewTabItemWithIcon("Characters",
theme.NewThemedResource(resourceGroupSvg), container.NewAppTabs(
container.NewTabItem("Overview", u.overviewArea.content),
container.NewTabItem("Locations", u.locationsArea.content),
container.NewTabItem("Training", u.trainingArea.content),
container.NewTabItem("Assets", u.assetSearchArea.content),
container.NewTabItem("Colonies", u.coloniesArea.content),
container.NewTabItem("Wealth", u.wealthArea.content),
Expand Down Expand Up @@ -427,12 +430,13 @@ func (u *UI) setAnyCharacter() error {
func (u *UI) refreshCrossPages() {
ff := map[string]func(){
"assetSearch": u.assetSearchArea.refresh,
"overview": u.overviewArea.refresh,
"colony": u.coloniesArea.refresh,
"locations": u.locationsArea.refresh,
"overview": u.overviewArea.refresh,
"statusBar": u.statusBarArea.refreshCharacterCount,
"toolbar": u.toolbarArea.refresh,
"colony": u.coloniesArea.refresh,
"training": u.trainingArea.refresh,
"wealth": u.wealthArea.refresh,
"statusBar": u.statusBarArea.refreshCharacterCount,
}
runFunctionsWithProgressModal("Updating characters", ff, u.window)
}
Expand Down
13 changes: 10 additions & 3 deletions internal/app/ui/updateticker.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,14 @@ func (u *UI) updateCharacterSectionAndRefreshIfNeeded(ctx context.Context, chara
case app.SectionLocation,
app.SectionOnline,
app.SectionShip:
if isShown && hasChanged {
if hasChanged {
u.locationsArea.refresh()
}
case app.SectionPlanets:
if isShown && hasChanged {
u.planetArea.refresh()
}
if hasChanged {
u.coloniesArea.refresh()
}
case app.SectionMailLabels,
Expand Down Expand Up @@ -181,9 +183,11 @@ func (u *UI) updateCharacterSectionAndRefreshIfNeeded(ctx context.Context, chara
if isShown && hasChanged {
u.skillCatalogueArea.refresh()
u.shipsArea.refresh()
u.overviewArea.refresh()
u.planetArea.refresh()
}
if hasChanged {
u.trainingArea.refresh()
}
case app.SectionSkillqueue:
if u.fyneApp.Preferences().BoolWithFallback(settingNotifyTrainingEnabled, settingNotifyTrainingEnabledDefault) {
err := u.CharacterService.EnableTrainingWatcher(ctx, characterID)
Expand All @@ -194,8 +198,11 @@ func (u *UI) updateCharacterSectionAndRefreshIfNeeded(ctx context.Context, chara
if isShown {
u.skillqueueArea.refresh()
}
if hasChanged {
u.trainingArea.refresh()
}
case app.SectionWalletBalance:
if isShown && hasChanged {
if hasChanged {
u.overviewArea.refresh()
u.wealthArea.refresh()
}
Expand Down

0 comments on commit 5d6cb9a

Please sign in to comment.