Clarify lifecycle loading and target selection
This commit is contained in:
@@ -61,8 +61,10 @@ const (
|
||||
)
|
||||
|
||||
type uiBanner struct {
|
||||
Kind bannerKind
|
||||
Message string
|
||||
Kind bannerKind
|
||||
Message string
|
||||
Detail string
|
||||
Dismissable bool
|
||||
}
|
||||
|
||||
type uiSurface struct {
|
||||
@@ -200,6 +202,9 @@ type ui struct {
|
||||
pickVaultPath widget.Clickable
|
||||
pickKeyFile widget.Clickable
|
||||
pickSyncLocalPath widget.Clickable
|
||||
clearVaultSelection widget.Clickable
|
||||
clearRemoteSelection widget.Clickable
|
||||
dismissBanner widget.Clickable
|
||||
addEntry widget.Clickable
|
||||
saveEntry widget.Clickable
|
||||
duplicateEntry widget.Clickable
|
||||
@@ -1688,21 +1693,60 @@ func (u *ui) describeActionError(label string, err error) string {
|
||||
func (u *ui) bannerSurface() uiBanner {
|
||||
switch {
|
||||
case strings.TrimSpace(u.loadingMessage) != "":
|
||||
return uiBanner{Kind: bannerLoading, Message: strings.TrimSpace(u.loadingMessage)}
|
||||
return uiBanner{
|
||||
Kind: bannerLoading,
|
||||
Message: strings.TrimSpace(u.loadingMessage),
|
||||
Detail: u.loadingDetailMessage(),
|
||||
}
|
||||
case strings.TrimSpace(u.state.ErrorMessage) != "":
|
||||
return uiBanner{Kind: bannerError, Message: strings.TrimSpace(u.state.ErrorMessage)}
|
||||
return uiBanner{
|
||||
Kind: bannerError,
|
||||
Message: strings.TrimSpace(u.state.ErrorMessage),
|
||||
Dismissable: true,
|
||||
}
|
||||
case strings.TrimSpace(u.state.StatusMessage) != "":
|
||||
if !u.statusExpiresAt.IsZero() && !u.now().Before(u.statusExpiresAt) {
|
||||
u.state.StatusMessage = ""
|
||||
u.statusExpiresAt = time.Time{}
|
||||
return uiBanner{}
|
||||
}
|
||||
return uiBanner{Kind: bannerStatus, Message: strings.TrimSpace(u.state.StatusMessage)}
|
||||
return uiBanner{
|
||||
Kind: bannerStatus,
|
||||
Message: strings.TrimSpace(u.state.StatusMessage),
|
||||
Dismissable: true,
|
||||
}
|
||||
default:
|
||||
return uiBanner{}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *ui) loadingDetailMessage() string {
|
||||
if !u.shouldShowLifecycleSetup() {
|
||||
return ""
|
||||
}
|
||||
if u.lifecycleMode == "remote" {
|
||||
baseURL := strings.TrimSpace(u.remoteBaseURL.Text())
|
||||
path := strings.TrimSpace(u.remotePath.Text())
|
||||
switch {
|
||||
case baseURL != "" && path != "":
|
||||
return fmt.Sprintf(
|
||||
"Target: %s (%s)",
|
||||
friendlyRecentRemoteLabel(recentRemoteRecord{BaseURL: baseURL, Path: path}),
|
||||
path,
|
||||
)
|
||||
case baseURL != "":
|
||||
return "Target: " + baseURL
|
||||
default:
|
||||
return "Preparing remote vault access"
|
||||
}
|
||||
}
|
||||
path := strings.TrimSpace(u.vaultPath.Text())
|
||||
if path == "" {
|
||||
return "Preparing local vault access"
|
||||
}
|
||||
return "Target: " + path
|
||||
}
|
||||
|
||||
func (u *ui) sessionSurface() uiSurface {
|
||||
if u.state.Session == nil {
|
||||
return uiSurface{}
|
||||
@@ -1739,6 +1783,10 @@ func (u *ui) shouldShowLifecycleSetup() bool {
|
||||
return !u.hasOpenVault()
|
||||
}
|
||||
|
||||
func (u *ui) lifecycleBusy() bool {
|
||||
return u.shouldShowLifecycleSetup() && strings.TrimSpace(u.loadingMessage) != ""
|
||||
}
|
||||
|
||||
func (u *ui) shouldUseLockedSinglePane() bool {
|
||||
return u.isVaultLocked() && !u.shouldShowLifecycleSetup()
|
||||
}
|
||||
@@ -1958,12 +2006,21 @@ func (u *ui) layout(gtx layout.Context) layout.Dimensions {
|
||||
u.showAPIAuditSection()
|
||||
}
|
||||
for u.showLocalLifecycle.Clicked(gtx) {
|
||||
if u.lifecycleBusy() {
|
||||
continue
|
||||
}
|
||||
u.lifecycleMode = "local"
|
||||
}
|
||||
for u.showRemoteLifecycle.Clicked(gtx) {
|
||||
if u.lifecycleBusy() {
|
||||
continue
|
||||
}
|
||||
u.lifecycleMode = "remote"
|
||||
}
|
||||
for u.toggleLifecycleAdvanced.Clicked(gtx) {
|
||||
if u.lifecycleBusy() {
|
||||
continue
|
||||
}
|
||||
u.lifecycleAdvancedHidden = !u.lifecycleAdvancedHidden
|
||||
u.saveUIPreferences()
|
||||
}
|
||||
@@ -2059,9 +2116,15 @@ func (u *ui) layout(gtx layout.Context) layout.Dimensions {
|
||||
u.loadSelectedEntryIntoEditor()
|
||||
}
|
||||
for u.pickVaultPath.Clicked(gtx) {
|
||||
if u.lifecycleBusy() {
|
||||
continue
|
||||
}
|
||||
u.runAction("choose vault path", func() error { return u.chooseExistingFileAction(&u.vaultPath) })
|
||||
}
|
||||
for u.pickKeyFile.Clicked(gtx) {
|
||||
if u.lifecycleBusy() {
|
||||
continue
|
||||
}
|
||||
u.runAction("choose key file", func() error { return u.chooseExistingFileAction(&u.keyFilePath) })
|
||||
}
|
||||
for u.pickSyncLocalPath.Clicked(gtx) {
|
||||
@@ -2069,18 +2132,50 @@ func (u *ui) layout(gtx layout.Context) layout.Dimensions {
|
||||
}
|
||||
for i := range u.recentVaultClicks {
|
||||
for u.recentVaultClicks[i].Clicked(gtx) {
|
||||
if u.lifecycleBusy() {
|
||||
continue
|
||||
}
|
||||
if i < len(u.recentVaults) {
|
||||
u.lifecycleMode = "local"
|
||||
u.vaultPath.SetText(u.recentVaults[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := range u.recentRemoteClicks {
|
||||
for u.recentRemoteClicks[i].Clicked(gtx) {
|
||||
if u.lifecycleBusy() {
|
||||
continue
|
||||
}
|
||||
if i < len(u.recentRemotes) {
|
||||
u.lifecycleMode = "remote"
|
||||
u.applyRecentRemoteRecord(u.recentRemotes[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
for u.clearVaultSelection.Clicked(gtx) {
|
||||
if u.lifecycleBusy() {
|
||||
continue
|
||||
}
|
||||
u.vaultPath.SetText("")
|
||||
u.state.ErrorMessage = ""
|
||||
u.state.StatusMessage = ""
|
||||
}
|
||||
for u.clearRemoteSelection.Clicked(gtx) {
|
||||
if u.lifecycleBusy() {
|
||||
continue
|
||||
}
|
||||
u.remoteBaseURL.SetText("")
|
||||
u.remotePath.SetText("")
|
||||
u.remoteUsername.SetText("")
|
||||
u.remotePassword.SetText("")
|
||||
u.state.ErrorMessage = ""
|
||||
u.state.StatusMessage = ""
|
||||
}
|
||||
for u.dismissBanner.Clicked(gtx) {
|
||||
u.state.ErrorMessage = ""
|
||||
u.state.StatusMessage = ""
|
||||
u.statusExpiresAt = time.Time{}
|
||||
}
|
||||
for u.addEntry.Clicked(gtx) {
|
||||
u.state.BeginNewEntry()
|
||||
u.loadSelectedEntryIntoEditor()
|
||||
@@ -3304,9 +3399,35 @@ func (u *ui) banner(gtx layout.Context) layout.Dimensions {
|
||||
|
||||
return layout.Background{}.Layout(gtx, fill(bg), func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.UniformInset(unit.Dp(12)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
lbl := material.Label(u.theme, unit.Sp(14), banner.Message)
|
||||
lbl.Color = fg
|
||||
return lbl.Layout(gtx)
|
||||
return layout.Flex{Alignment: layout.Middle}.Layout(gtx,
|
||||
layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
|
||||
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
lbl := material.Label(u.theme, unit.Sp(14), banner.Message)
|
||||
lbl.Color = fg
|
||||
return lbl.Layout(gtx)
|
||||
}),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if strings.TrimSpace(banner.Detail) == "" {
|
||||
return layout.Dimensions{}
|
||||
}
|
||||
return layout.Inset{Top: unit.Dp(2)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
lbl := material.Label(u.theme, unit.Sp(12), banner.Detail)
|
||||
lbl.Color = fg
|
||||
return lbl.Layout(gtx)
|
||||
})
|
||||
}),
|
||||
)
|
||||
}),
|
||||
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||
if !banner.Dismissable {
|
||||
return layout.Dimensions{}
|
||||
}
|
||||
return layout.Inset{Left: unit.Dp(10)}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
return tonedButton(gtx, u.theme, &u.dismissBanner, "Dismiss")
|
||||
})
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user