feat: l for details; details for vaults, scopes and files; getting started; the age of cached lists
#17
No files matched your search
@@ -56,6 +56,7 @@ const (
|
|||||||
slotDescribe // extra metadata for the selected object
|
slotDescribe // extra metadata for the selected object
|
||||||
slotCopy // fetch a value straight to the clipboard
|
slotCopy // fetch a value straight to the clipboard
|
||||||
slotVersions // count the selected object's versions
|
slotVersions // count the selected object's versions
|
||||||
|
slotDetails // a vault's, scope's or file's details
|
||||||
slotCount
|
slotCount
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -591,7 +592,14 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.groups[msg.provider] = groupsState{names: msg.names, err: msg.err}
|
m.groups[msg.provider] = groupsState{names: msg.names, err: msg.err}
|
||||||
|
|
||||||
case detailsMsg:
|
case detailsMsg:
|
||||||
|
if msg.req == m.reqID[slotDetails] {
|
||||||
|
m.done(slotDetails)
|
||||||
|
}
|
||||||
|
if errors.Is(msg.err, context.Canceled) || errors.Is(msg.err, context.DeadlineExceeded) {
|
||||||
|
delete(m.details, msg.key) // tried again when shown again
|
||||||
|
} else {
|
||||||
m.details[msg.key] = detailsState{attrs: msg.attrs, err: msg.err}
|
m.details[msg.key] = detailsState{attrs: msg.attrs, err: msg.err}
|
||||||
|
}
|
||||||
|
|
||||||
case crawlMsg:
|
case crawlMsg:
|
||||||
cmds = append(cmds, m.handleCrawl(msg))
|
cmds = append(cmds, m.handleCrawl(msg))
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ type groupsMsg struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A vault's, scope's or file's details beyond its listing load once a
|
// A vault's, scope's or file's details beyond its listing load once a
|
||||||
// session, the first time it's shown, and again with refresh.
|
// session, when the cursor rests on it, and again with refresh. They're
|
||||||
|
// extra: moving on cancels the load, and search never waits for them.
|
||||||
type detailsState struct {
|
type detailsState struct {
|
||||||
attrs []provider.Attr
|
attrs []provider.Attr
|
||||||
err error
|
err error
|
||||||
@@ -38,6 +39,7 @@ type detailsState struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type detailsMsg struct {
|
type detailsMsg struct {
|
||||||
|
req int
|
||||||
key string
|
key string
|
||||||
attrs []provider.Attr
|
attrs []provider.Attr
|
||||||
err error
|
err error
|
||||||
@@ -211,15 +213,23 @@ func (m *Model) loadDetails(c provider.Container) tea.Cmd {
|
|||||||
if _, seen := m.details[key]; !ok || seen {
|
if _, seen := m.details[key]; !ok || seen {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
m.details[key] = detailsState{loading: true}
|
// One at a time: the previous one, if it's still loading, is
|
||||||
timeout := m.timeout
|
// cancelled and forgotten, to load when it's shown again.
|
||||||
return func() tea.Msg {
|
for k, st := range m.details {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
if st.loading {
|
||||||
defer cancel()
|
delete(m.details, k)
|
||||||
attrs, err := d.Details(ctx, c)
|
|
||||||
return detailsMsg{key: key, attrs: attrs, err: err}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
m.details[key] = detailsState{loading: true}
|
||||||
|
ctx, req := m.begin(slotDetails)
|
||||||
|
return m.settled(ctx, func() tea.Msg {
|
||||||
|
attrs, err := d.Details(ctx, c)
|
||||||
|
if ctx.Err() != nil {
|
||||||
|
err = ctx.Err()
|
||||||
|
}
|
||||||
|
return detailsMsg{req: req, key: key, attrs: attrs, err: err}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// problemFields show why an account has nothing to show, wrapped to the
|
// problemFields show why an account has nothing to show, wrapped to the
|
||||||
// pane, in red.
|
// pane, in red.
|
||||||
|
|||||||
Reference in new issue
Block a user