Skip to content

Commit 6dfa9cd

Browse files
some improvements to link crud, crud, tags type, util and slicest
1 parent b36b6de commit 6dfa9cd

13 files changed

Lines changed: 372 additions & 109 deletions

File tree

tags/bun.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ const (
2525
type bunMode bool
2626

2727
func ToBunString(tags Tags) string {
28-
strs := slicest.Map(tags, func(tag Tag) string { return string(tag) })
29-
return bunTagDelimiter + strings.Join(strs, bunTagDelimiter) + bunTagDelimiter
28+
return bunTagDelimiter + strings.Join(tags.Slice(), bunTagDelimiter) + bunTagDelimiter
3029
}
3130

3231
func FromBunString(str string) Tags {

tags/tags.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ func (t Tags) String() string {
2222
return Stringify(t)
2323
}
2424

25+
func (t Tags) Slice() []string {
26+
return slicest.Map(t, func(tag Tag) string { return string(tag) })
27+
}
28+
2529
func Parse(str string) Tags {
2630
strs := strings.Split(str, SEPERATOR)
2731
// remove whitespace
@@ -31,6 +35,5 @@ func Parse(str string) Tags {
3135
}
3236

3337
func Stringify(tags Tags) string {
34-
strs := slicest.Map(tags, func(tag Tag) string { return string(tag) })
35-
return strings.Join(strs, SEPERATOR+" ")
38+
return strings.Join(tags.Slice(), SEPERATOR+" ")
3639
}

ui/tui/models/helpers/crud/create_model.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type CreateModel[
2828
// state
2929
publicKey client.PublicKey
3030
focussed bool
31-
preset *TRecordCreate
31+
preset TRecordCreate
3232

3333
// util
3434
size util.Size
@@ -43,7 +43,7 @@ func NewCreate[
4343
TRecordUpdate comparable,
4444
TRecordId comparable,
4545
TFilter comparable,
46-
](crud *Crud[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter], preset *TRecordCreate) *CreateModel[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter] {
46+
](crud *Crud[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter], preset TRecordCreate) *CreateModel[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter] {
4747
return &CreateModel[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter]{
4848
crud: crud,
4949
preset: preset,
@@ -83,7 +83,7 @@ func (m *CreateModel[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter])
8383
}),
8484
form.WithOnDiscardGuard[TRecordCreate](discardGuard),
8585
// data
86-
form.WithInitialData(util.DerefOrZeroValue(m.preset)),
86+
form.WithInitialData(m.preset),
8787
)
8888

8989
m.form = util.NewPointer(form.New(formOpts...))
@@ -128,6 +128,11 @@ func (m *CreateModel[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter])
128128
}
129129

130130
func (m *CreateModel[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter]) Focus(parentKeyMap help.KeyMap) tea.Cmd {
131+
if m.crud.ReloadOnNextFocus {
132+
m.crud.ReloadOnNextFocus = false
133+
return m.Init()
134+
// no need to focus or announce anything, as the popup interceptor will take it away again.
135+
}
131136
m.focussed = true
132137
return tea.Batch(
133138
windowtitle.Announce(m.crud.Texts.EntityNameMultiple),

ui/tui/models/helpers/crud/crud.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ type Crud[
3535
buildListTable func(records []TRecord, width int) ([]table.Column, []table.Row)
3636
recordToRecordUpdate func(record TRecord) TRecordUpdate
3737

38-
createFormRows func() []form.FormOpt[TRecordCreate]
39-
updateFormRows func() []form.FormOpt[TRecordUpdate]
38+
createFormRows func() []form.FormOpt[TRecordCreate]
39+
updateFormRows func() []form.FormOpt[TRecordUpdate]
40+
createRecordPreset func() TRecordCreate
4041

4142
listGlobalKeyMap form.GlobalKeyMap
4243

@@ -49,12 +50,14 @@ type Crud[
4950
updateMsgInterceptors []UpdateMsgInterceptor[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter]
5051

5152
listReloadAfterChange bool
53+
54+
ReloadOnNextFocus bool
5255
}
5356

5457
func (c *Crud[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter]) OpenList() tea.Cmd {
5558
return c.routerControll.Push(util.ModelPointer(NewList(c)))
5659
}
57-
func (c *Crud[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter]) OpenCreate(preset *TRecordCreate) tea.Cmd {
60+
func (c *Crud[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter]) OpenCreate(preset TRecordCreate) tea.Cmd {
5861
return c.routerControll.Push(util.ModelPointer(NewCreate(c, preset)))
5962
}
6063
func (c *Crud[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter]) OpenEdit(record TRecord) tea.Cmd {

ui/tui/models/helpers/crud/list_model.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ func (m *ListModel[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter]) U
129129
}
130130
switch {
131131
case key.Matches(msg, ListBaseKeyMap.Create):
132-
return m.crud.routerControll.Push(util.ModelPointer(NewCreate(m.crud, nil)))
132+
return m.crud.routerControll.Push(util.ModelPointer(NewCreate(m.crud, m.crud.createRecordPreset())))
133133

134134
case key.Matches(msg, ListBaseKeyMap.Edit):
135135
selectedRecord := m.selectedRecord()
@@ -192,6 +192,11 @@ func (m *ListModel[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter]) V
192192

193193
// Focus implements util.Model.
194194
func (m *ListModel[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter]) Focus(parentKeyMap help.KeyMap) tea.Cmd {
195+
if m.crud.ReloadOnNextFocus {
196+
m.crud.ReloadOnNextFocus = false
197+
return m.reload()
198+
// no need to focus or announce anything, as the popup interceptor will take it away again.
199+
}
195200
m.focussed = true
196201
m.table.Focus()
197202
return tea.Batch(

ui/tui/models/helpers/crud/new.go

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ func New[
6060

6161
buildListTable: buildListTable,
6262
recordToRecordUpdate: recordToRecordUpdate,
63+
createRecordPreset: func() TRecordCreate { return util.NewZero[TRecordCreate]() },
6364

6465
createFormRows: createFormRows,
6566
updateFormRows: updateFormRows,
@@ -134,30 +135,6 @@ func WithListReloadAfterChange[
134135
}
135136
}
136137

137-
func WithListDuplicateActionOld[
138-
TRecord any,
139-
TRecordCreate comparable,
140-
TRecordUpdate comparable,
141-
TRecordId comparable,
142-
TFilter comparable,
143-
](recordToRecordCreate func(record TRecord) TRecordCreate) Option[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter] {
144-
return func(c *Crud[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter]) {
145-
// add list key binding
146-
WithListKeyBindings[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter](keys.Duplicate())(c)
147-
148-
// add list msg interceptor
149-
WithListMsgInterceptor(func(msg tea.Msg, ctx ListMsgInterceptorCtx[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter]) (tea.Cmd, bool) {
150-
if msg, ok := msg.(tea.KeyMsg); ok && key.Matches(msg, keys.Duplicate()) {
151-
if ctx.SelectedRecord == nil {
152-
return popupviews.OpenMessage(popupviews.MessageError, "Please select a "+ctx.Crud.Texts.EntityNameSingular+" to duplicate.", nil), true
153-
}
154-
return ctx.Crud.OpenCreate(util.NewPointer(recordToRecordCreate(*ctx.SelectedRecord))), true
155-
}
156-
return nil, false
157-
})(c)
158-
}
159-
}
160-
161138
func WithListAction[
162139
TRecord any,
163140
TRecordCreate comparable,
@@ -191,6 +168,18 @@ func WithListDuplicateAction[
191168
return popupviews.OpenMessage(popupviews.MessageError, "Please select a "+ctx.Crud.Texts.EntityNameSingular+" to duplicate.", nil)
192169
}
193170

194-
return ctx.Crud.OpenCreate(util.NewPointer(recordToRecordCreate(*ctx.SelectedRecord)))
171+
return ctx.Crud.OpenCreate(recordToRecordCreate(*ctx.SelectedRecord))
195172
}, keys.Duplicate())
196173
}
174+
175+
func WithCreateRecordPreset[
176+
TRecord any,
177+
TRecordCreate comparable,
178+
TRecordUpdate comparable,
179+
TRecordId comparable,
180+
TFilter comparable,
181+
](createRecordPreset func() TRecordCreate) Option[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter] {
182+
return func(c *Crud[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter]) {
183+
c.createRecordPreset = createRecordPreset
184+
}
185+
}

ui/tui/models/helpers/crud/update_model.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ func (m *UpdateModel[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter])
132132
}
133133

134134
func (m *UpdateModel[TRecord, TRecordCreate, TRecordUpdate, TRecordId, TFilter]) Focus(parentKeyMap help.KeyMap) tea.Cmd {
135+
if m.crud.ReloadOnNextFocus {
136+
m.crud.ReloadOnNextFocus = false
137+
return m.Init()
138+
// no need to focus or announce anything, as the popup interceptor will take it away again.
139+
}
135140
m.focussed = true
136141
return tea.Batch(
137142
windowtitle.Announce(m.crud.Texts.EntityNameMultiple),

ui/tui/models/views/account/crud.go

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ import (
2222
)
2323

2424
type recordT = struct {
25-
account client.Account
26-
isDirty bool
27-
linkCount int
28-
linkedPublicKeyCount int
29-
expiredLinkCount int
30-
expiredLinkedPublicKeyCount int
25+
account client.Account
26+
isDirty bool
27+
activeLinkCount int
28+
activeLinkedPublicKeyCount int
29+
totalLinkCount int
30+
totalLinkedPublicKeyCount int
3131
}
3232

3333
type recordCreateT = struct {
@@ -45,22 +45,22 @@ type recordIdT = client.AccountId
4545
type filterT = struct{}
4646

4747
func accountToRecord(ctx context.Context, c client.Client, account client.Account) (recordT, error) {
48-
links, err := c.ListLinksForAccount(ctx, account.Id, false)
48+
activeLinks, err := c.ListLinksForAccount(ctx, account.Id, false)
4949
if err != nil {
5050
return recordT{}, err
5151
}
5252

53-
publicKeys, err := c.ListPublicKeysLinkedToAccount(ctx, account.Id, false)
53+
activePublicKeys, err := c.ListPublicKeysLinkedToAccount(ctx, account.Id, false)
5454
if err != nil {
5555
return recordT{}, err
5656
}
5757

58-
expiredLinks, err := c.ListLinksForAccount(ctx, account.Id, true)
58+
allLinks, err := c.ListLinksForAccount(ctx, account.Id, true)
5959
if err != nil {
6060
return recordT{}, err
6161
}
6262

63-
expiredPublicKeys, err := c.ListPublicKeysLinkedToAccount(ctx, account.Id, true)
63+
allPublicKeys, err := c.ListPublicKeysLinkedToAccount(ctx, account.Id, true)
6464
if err != nil {
6565
return recordT{}, err
6666
}
@@ -73,10 +73,10 @@ func accountToRecord(ctx context.Context, c client.Client, account client.Accoun
7373
return recordT{
7474
account,
7575
isDirty,
76-
len(links),
77-
len(publicKeys),
78-
len(expiredLinks),
79-
len(expiredPublicKeys),
76+
len(activeLinks),
77+
len(activePublicKeys),
78+
len(allLinks),
79+
len(allPublicKeys),
8080
}, nil
8181
}
8282

@@ -169,10 +169,10 @@ func NewCrud(c client.Client, rc router.Controll) *crud.Crud[recordT, recordCrea
169169
{Title: "Deploy Method", View: func(r recordT) string { return r.account.DeployMethod }},
170170
{Title: "Dirty", View: func(r recordT) string { return fmt.Sprint(r.isDirty) }},
171171
{Title: "Links (active/total)", View: func(r recordT) string {
172-
return fmt.Sprintf("%d/%d", r.linkCount-r.expiredLinkCount, r.linkCount)
172+
return fmt.Sprintf("%d/%d", r.activeLinkCount, r.totalLinkedPublicKeyCount)
173173
}},
174174
{Title: "Public Keys (active/total)", View: func(r recordT) string {
175-
return fmt.Sprintf("%d/%d", r.linkedPublicKeyCount-r.expiredLinkedPublicKeyCount, r.linkedPublicKeyCount)
175+
return fmt.Sprintf("%d/%d", r.activeLinkedPublicKeyCount, r.totalLinkedPublicKeyCount)
176176
}},
177177
}),
178178
func(record recordT) recordUpdateT {
@@ -205,6 +205,7 @@ func NewCrud(c client.Client, rc router.Controll) *crud.Crud[recordT, recordCrea
205205
return popupviews.OpenMessage(popupviews.MessageError, "Please select a "+ctx.Crud.Texts.EntityNameSingular+".", nil)
206206
}
207207

208+
ctx.Crud.ReloadOnNextFocus = true
208209
return linkaccount.NewCrud(c, rc, ctx.SelectedRecord.account).OpenList()
209210
},
210211
key.NewBinding(

ui/tui/models/views/linkaccount/crud.go

Lines changed: 6 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package linkaccount
66
import (
77
"context"
88
"fmt"
9-
"time"
109

1110
"github.com/toeirei/keymaster/client"
1211
"github.com/toeirei/keymaster/tags"
@@ -15,18 +14,10 @@ import (
1514
"github.com/toeirei/keymaster/ui/tui/models/helpers/form"
1615
formelement "github.com/toeirei/keymaster/ui/tui/models/helpers/form/element"
1716
"github.com/toeirei/keymaster/ui/tui/models/helpers/table"
17+
"github.com/toeirei/keymaster/ui/tui/util"
1818
"github.com/toeirei/keymaster/util/slicest"
1919
)
2020

21-
const (
22-
timeLayout1 string = "2006.01.02 15:04:05"
23-
timeLayout2 string = "2006.01.02 15:04"
24-
timeLayout3 string = "2006.01.02"
25-
timeLayout4 string = "02.01.2006 15:04:05"
26-
timeLayout5 string = "02.01.2006 15:04"
27-
timeLayout6 string = "02.01.2006"
28-
)
29-
3021
type recordT = struct {
3122
link client.Link
3223
linkedPublicKeyCount int
@@ -52,21 +43,6 @@ func linkToRecord(ctx context.Context, c client.Client, link client.Link) (recor
5243
return recordT{link, len(publicKeys)}, nil
5344
}
5445

55-
func parseExpiresAt(expiresAtStr string) (time.Time, error) {
56-
timeParseLayouts := []string{timeLayout1, timeLayout2, timeLayout3, timeLayout4, timeLayout5, timeLayout6}
57-
58-
var expiresAt time.Time
59-
var err error
60-
for _, layout := range timeParseLayouts {
61-
expiresAt, err = time.Parse(layout, expiresAtStr)
62-
if err == nil {
63-
break
64-
}
65-
}
66-
67-
return expiresAt, err
68-
}
69-
7046
func formRows[T comparable]() []form.FormOpt[T] {
7147
return []form.FormOpt[T]{
7248
form.WithRowItem[T]("tag_matcher", formelement.NewText("Tag Matcher", "text to match tags of public keys")),
@@ -103,7 +79,7 @@ func NewCrud(c client.Client, rc router.Controll, account client.Account) *crud.
10379
return recordT{}, err
10480
}
10581

106-
expiresAt, err := parseExpiresAt(recordCreate.ExpiresAt)
82+
expiresAt, err := util.ParseTime(recordCreate.ExpiresAt)
10783
if err != nil {
10884
return recordT{}, err
10985
}
@@ -126,7 +102,7 @@ func NewCrud(c client.Client, rc router.Controll, account client.Account) *crud.
126102
return recordT{}, err
127103
}
128104

129-
expiresAt, err := parseExpiresAt(recordUpdate.ExpiresAt)
105+
expiresAt, err := util.ParseTime(recordUpdate.ExpiresAt)
130106
if err != nil {
131107
return recordT{}, err
132108
}
@@ -153,15 +129,15 @@ func NewCrud(c client.Client, rc router.Controll, account client.Account) *crud.
153129
},
154130

155131
table.NewBubblesTableRenderer(table.Columns[recordT]{
156-
{Title: "Account", View: func(r recordT) string { return account.String() }},
157132
{Title: "Tag Matcher", View: func(r recordT) string { return r.link.TagMatcher }},
158133
{Title: "Expires At", View: func(r recordT) string { return fmt.Sprint(r.link.ExpiresAt) }},
134+
{Title: "Account", View: func(r recordT) string { return account.String() }},
159135
{Title: "Public Keys", View: func(r recordT) string { return fmt.Sprint(r.linkedPublicKeyCount) }},
160136
}),
161137
func(record recordT) recordUpdateT {
162138
return recordUpdateT{
163139
record.link.TagMatcher,
164-
record.link.ExpiresAt.Format(timeLayout1),
140+
util.StringifyTime(record.link.ExpiresAt),
165141
}
166142
},
167143

@@ -173,7 +149,7 @@ func NewCrud(c client.Client, rc router.Controll, account client.Account) *crud.
173149
crud.WithListDuplicateAction[recordT, recordCreateT, recordUpdateT, recordIdT, filterT](func(record recordT) recordCreateT {
174150
return recordCreateT{
175151
record.link.TagMatcher,
176-
record.link.ExpiresAt.Format(timeLayout1),
152+
util.StringifyTime(record.link.ExpiresAt),
177153
}
178154
}),
179155
crud.WithListReloadAfterChange[recordT, recordCreateT, recordUpdateT, recordIdT, filterT](true),

0 commit comments

Comments
 (0)