mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-02-03 13:21:56 +02:00
refactor credential handling
This commit is contained in:
parent
d0805616e4
commit
46e9946854
@ -87,7 +87,6 @@ func localisedTitle(mApp *app.App, str string) string {
|
||||
"commitMessage": tr.CommitMessageTitle,
|
||||
"commits": tr.CommitsTitle,
|
||||
"confirmation": tr.ConfirmationTitle,
|
||||
"credentials": tr.CredentialsTitle,
|
||||
"information": tr.InformationTitle,
|
||||
"main": tr.MainTitle,
|
||||
"patchBuilding": tr.PatchBuildingTitle,
|
||||
|
@ -120,6 +120,7 @@ func (gui *Gui) prepareConfirmationPanel(
|
||||
hasLoader bool,
|
||||
findSuggestionsFunc func(string) []*types.Suggestion,
|
||||
editable bool,
|
||||
mask bool,
|
||||
) error {
|
||||
x0, y0, x1, y1 := gui.getConfirmationPanelDimensions(true, prompt)
|
||||
// calling SetView on an existing view returns the same view, so I'm not bothering
|
||||
@ -136,6 +137,7 @@ func (gui *Gui) prepareConfirmationPanel(
|
||||
// for now we do not support wrapping in our editor
|
||||
gui.Views.Confirmation.Wrap = !editable
|
||||
gui.Views.Confirmation.FgColor = theme.GocuiDefaultTextColor
|
||||
gui.Views.Confirmation.Mask = runeForMask(mask)
|
||||
|
||||
gui.findSuggestions = findSuggestionsFunc
|
||||
if findSuggestionsFunc != nil {
|
||||
@ -154,7 +156,25 @@ func (gui *Gui) prepareConfirmationPanel(
|
||||
return nil
|
||||
}
|
||||
|
||||
func runeForMask(mask bool) rune {
|
||||
if mask {
|
||||
return '*'
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (gui *Gui) createPopupPanel(opts types.CreatePopupPanelOpts) error {
|
||||
// if a popup panel already appears we must ignore this current one. This is
|
||||
// not great but it prevents lost state. The proper solution is to have a stack of
|
||||
// popups. We could have a queue of types.CreatePopupPanelOpts so that if you
|
||||
// close a popup and there's another one in the queue we show that.
|
||||
// One important popup we don't want to interrupt is the credentials popup
|
||||
// or a process might get stuck waiting on user input.
|
||||
if gui.currentContext().GetKey() == context.CONFIRMATION_CONTEXT_KEY {
|
||||
gui.Log.Error("ignoring create popup panel because a popup panel is already open")
|
||||
return nil
|
||||
}
|
||||
|
||||
// remove any previous keybindings
|
||||
gui.clearConfirmationViewKeyBindings()
|
||||
|
||||
@ -164,6 +184,7 @@ func (gui *Gui) createPopupPanel(opts types.CreatePopupPanelOpts) error {
|
||||
opts.HasLoader,
|
||||
opts.FindSuggestionsFunc,
|
||||
opts.Editable,
|
||||
opts.Mask,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -24,7 +24,6 @@ const (
|
||||
MAIN_PATCH_BUILDING_CONTEXT_KEY types.ContextKey = "patchBuilding"
|
||||
MAIN_STAGING_CONTEXT_KEY types.ContextKey = "staging"
|
||||
MENU_CONTEXT_KEY types.ContextKey = "menu"
|
||||
CREDENTIALS_CONTEXT_KEY types.ContextKey = "credentials"
|
||||
CONFIRMATION_CONTEXT_KEY types.ContextKey = "confirmation"
|
||||
SEARCH_CONTEXT_KEY types.ContextKey = "search"
|
||||
COMMIT_MESSAGE_CONTEXT_KEY types.ContextKey = "commitMessage"
|
||||
@ -51,7 +50,6 @@ var AllContextKeys = []types.ContextKey{
|
||||
MAIN_PATCH_BUILDING_CONTEXT_KEY,
|
||||
MAIN_STAGING_CONTEXT_KEY, // not focusable for secondary view
|
||||
MENU_CONTEXT_KEY,
|
||||
CREDENTIALS_CONTEXT_KEY,
|
||||
CONFIRMATION_CONTEXT_KEY,
|
||||
SEARCH_CONTEXT_KEY,
|
||||
COMMIT_MESSAGE_CONTEXT_KEY,
|
||||
@ -80,7 +78,6 @@ type ContextTree struct {
|
||||
Staging types.Context
|
||||
PatchBuilding types.Context
|
||||
Merging types.Context
|
||||
Credentials types.Context
|
||||
Confirmation types.Context
|
||||
CommitMessage types.Context
|
||||
Search types.Context
|
||||
@ -103,7 +100,6 @@ func (self *ContextTree) Flatten() []types.Context {
|
||||
self.Stash,
|
||||
self.Menu,
|
||||
self.Confirmation,
|
||||
self.Credentials,
|
||||
self.CommitMessage,
|
||||
self.Normal,
|
||||
self.Staging,
|
||||
|
@ -114,18 +114,6 @@ func (gui *Gui) contextTree() *context.ContextTree {
|
||||
OnFocus: OnFocusWrapper(func() error { return gui.renderConflictsWithLock(true) }),
|
||||
},
|
||||
),
|
||||
Credentials: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.PERSISTENT_POPUP,
|
||||
ViewName: "credentials",
|
||||
WindowName: "credentials",
|
||||
Key: context.CREDENTIALS_CONTEXT_KEY,
|
||||
Focusable: true,
|
||||
}),
|
||||
context.ContextCallbackOpts{
|
||||
OnFocus: OnFocusWrapper(gui.handleAskFocused),
|
||||
},
|
||||
),
|
||||
Confirmation: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.TEMPORARY_POPUP,
|
||||
|
68
pkg/gui/controllers/helpers/credentials_helper.go
Normal file
68
pkg/gui/controllers/helpers/credentials_helper.go
Normal file
@ -0,0 +1,68 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
type CredentialsHelper struct {
|
||||
c *types.HelperCommon
|
||||
}
|
||||
|
||||
func NewCredentialsHelper(
|
||||
c *types.HelperCommon,
|
||||
) *CredentialsHelper {
|
||||
return &CredentialsHelper{
|
||||
c: c,
|
||||
}
|
||||
}
|
||||
|
||||
// promptUserForCredential wait for a username, password or passphrase input from the credentials popup
|
||||
func (self *CredentialsHelper) PromptUserForCredential(passOrUname oscommands.CredentialType) string {
|
||||
waitGroup := sync.WaitGroup{}
|
||||
waitGroup.Add(1)
|
||||
|
||||
userInput := ""
|
||||
|
||||
self.c.OnUIThread(func() error {
|
||||
title, mask := self.getTitleAndMask(passOrUname)
|
||||
|
||||
return self.c.Prompt(types.PromptOpts{
|
||||
Title: title,
|
||||
Mask: mask,
|
||||
HandleConfirm: func(input string) error {
|
||||
userInput = input
|
||||
|
||||
waitGroup.Done()
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
},
|
||||
HandleClose: func() error {
|
||||
waitGroup.Done()
|
||||
|
||||
return nil
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
// wait for username/passwords/passphrase input
|
||||
waitGroup.Wait()
|
||||
|
||||
return userInput + "\n"
|
||||
}
|
||||
|
||||
func (self *CredentialsHelper) getTitleAndMask(passOrUname oscommands.CredentialType) (string, bool) {
|
||||
switch passOrUname {
|
||||
case oscommands.Username:
|
||||
return self.c.Tr.CredentialsUsername, false
|
||||
case oscommands.Password:
|
||||
return self.c.Tr.CredentialsPassword, true
|
||||
case oscommands.Passphrase:
|
||||
return self.c.Tr.CredentialsPassphrase, true
|
||||
}
|
||||
|
||||
// should never land here
|
||||
panic("unexpected credential request")
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
type credentials chan string
|
||||
|
||||
// promptUserForCredential wait for a username, password or passphrase input from the credentials popup
|
||||
func (gui *Gui) promptUserForCredential(passOrUname oscommands.CredentialType) string {
|
||||
gui.credentials = make(chan string)
|
||||
gui.OnUIThread(func() error {
|
||||
credentialsView := gui.Views.Credentials
|
||||
switch passOrUname {
|
||||
case oscommands.Username:
|
||||
credentialsView.Title = gui.c.Tr.CredentialsUsername
|
||||
credentialsView.Mask = 0
|
||||
case oscommands.Password:
|
||||
credentialsView.Title = gui.c.Tr.CredentialsPassword
|
||||
credentialsView.Mask = '*'
|
||||
case oscommands.Passphrase:
|
||||
credentialsView.Title = gui.c.Tr.CredentialsPassphrase
|
||||
credentialsView.Mask = '*'
|
||||
}
|
||||
|
||||
if err := gui.c.PushContext(gui.State.Contexts.Credentials); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
// wait for username/passwords/passphrase input
|
||||
userInput := <-gui.credentials
|
||||
return userInput + "\n"
|
||||
}
|
||||
|
||||
func (gui *Gui) handleSubmitCredential() error {
|
||||
credentialsView := gui.Views.Credentials
|
||||
message := strings.TrimSpace(credentialsView.TextArea.GetContent())
|
||||
gui.credentials <- message
|
||||
credentialsView.ClearTextArea()
|
||||
if err := gui.c.PopContext(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCloseCredentialsView() error {
|
||||
gui.Views.Credentials.ClearTextArea()
|
||||
gui.credentials <- ""
|
||||
return gui.c.PopContext()
|
||||
}
|
@ -86,7 +86,6 @@ type Gui struct {
|
||||
Config config.AppConfigurer
|
||||
Updater *updates.Updater
|
||||
statusManager *statusManager
|
||||
credentials credentials
|
||||
waitForIntro sync.WaitGroup
|
||||
fileWatcher *fileWatcher
|
||||
viewBufferManagerMap map[string]*tasks.ViewBufferManager
|
||||
@ -247,7 +246,6 @@ type Views struct {
|
||||
Options *gocui.View
|
||||
Confirmation *gocui.View
|
||||
Menu *gocui.View
|
||||
Credentials *gocui.View
|
||||
CommitMessage *gocui.View
|
||||
CommitFiles *gocui.View
|
||||
Information *gocui.View
|
||||
@ -403,7 +401,6 @@ func initialViewContextMapping(contextTree *context.ContextTree) map[string]type
|
||||
"stash": contextTree.Stash,
|
||||
"menu": contextTree.Menu,
|
||||
"confirmation": contextTree.Confirmation,
|
||||
"credentials": contextTree.Credentials,
|
||||
"commitMessage": contextTree.CommitMessage,
|
||||
"main": contextTree.Normal,
|
||||
"secondary": contextTree.Normal,
|
||||
@ -448,17 +445,6 @@ func NewGui(
|
||||
InitialDir: initialDir,
|
||||
}
|
||||
|
||||
guiIO := oscommands.NewGuiIO(
|
||||
cmn.Log,
|
||||
gui.LogCommand,
|
||||
gui.getCmdWriter,
|
||||
gui.promptUserForCredential,
|
||||
)
|
||||
|
||||
osCommand := oscommands.NewOSCommand(cmn, oscommands.GetPlatform(), guiIO)
|
||||
|
||||
gui.os = osCommand
|
||||
|
||||
gui.watchFilesForChanges()
|
||||
|
||||
gui.PopupHandler = popup.NewPopupHandler(
|
||||
@ -475,6 +461,19 @@ func NewGui(
|
||||
guiCommon := &guiCommon{gui: gui, IPopupHandler: gui.PopupHandler}
|
||||
helperCommon := &types.HelperCommon{IGuiCommon: guiCommon, Common: cmn}
|
||||
|
||||
credentialsHelper := helpers.NewCredentialsHelper(helperCommon)
|
||||
|
||||
guiIO := oscommands.NewGuiIO(
|
||||
cmn.Log,
|
||||
gui.LogCommand,
|
||||
gui.getCmdWriter,
|
||||
credentialsHelper.PromptUserForCredential,
|
||||
)
|
||||
|
||||
osCommand := oscommands.NewOSCommand(cmn, oscommands.GetPlatform(), guiIO)
|
||||
|
||||
gui.os = osCommand
|
||||
|
||||
// storing this stuff on the gui for now to ease refactoring
|
||||
// TODO: reset these controllers upon changing repos due to state changing
|
||||
gui.c = helperCommon
|
||||
@ -751,7 +750,6 @@ func (gui *Gui) createAllViews() error {
|
||||
{viewPtr: &gui.Views.Search, name: "search"},
|
||||
{viewPtr: &gui.Views.SearchPrefix, name: "searchPrefix"},
|
||||
{viewPtr: &gui.Views.CommitMessage, name: "commitMessage"},
|
||||
{viewPtr: &gui.Views.Credentials, name: "credentials"},
|
||||
{viewPtr: &gui.Views.Menu, name: "menu"},
|
||||
{viewPtr: &gui.Views.Suggestions, name: "suggestions"},
|
||||
{viewPtr: &gui.Views.Confirmation, name: "confirmation"},
|
||||
@ -825,11 +823,6 @@ func (gui *Gui) createAllViews() error {
|
||||
|
||||
gui.Views.Confirmation.Visible = false
|
||||
|
||||
gui.Views.Credentials.Visible = false
|
||||
gui.Views.Credentials.Title = gui.c.Tr.CredentialsUsername
|
||||
gui.Views.Credentials.FgColor = theme.GocuiDefaultTextColor
|
||||
gui.Views.Credentials.Editable = true
|
||||
|
||||
gui.Views.Suggestions.Visible = false
|
||||
|
||||
gui.Views.Menu.Visible = false
|
||||
|
@ -65,3 +65,7 @@ func (self *guiCommon) Render() {
|
||||
func (self *guiCommon) OpenSearch() {
|
||||
_ = self.gui.handleOpenSearch(self.gui.currentViewName())
|
||||
}
|
||||
|
||||
func (self *guiCommon) OnUIThread(f func() error) {
|
||||
self.gui.OnUIThread(f)
|
||||
}
|
||||
|
@ -430,18 +430,6 @@ func (self *Gui) GetInitialKeybindings() ([]*types.Binding, []*gocui.ViewMouseBi
|
||||
Handler: self.handleCopySelectedSideContextItemToClipboard,
|
||||
Description: self.c.Tr.LcCopyCommitShaToClipboard,
|
||||
},
|
||||
{
|
||||
ViewName: "credentials",
|
||||
Key: opts.GetKey(opts.Config.Universal.Confirm),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.handleSubmitCredential,
|
||||
},
|
||||
{
|
||||
ViewName: "credentials",
|
||||
Key: opts.GetKey(opts.Config.Universal.Return),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.handleCloseCredentialsView,
|
||||
},
|
||||
{
|
||||
ViewName: "menu",
|
||||
Key: opts.GetKey(opts.Config.Universal.Return),
|
||||
|
@ -224,7 +224,6 @@ func (gui *Gui) onInitialViewsCreation() error {
|
||||
gui.Views.Menu,
|
||||
gui.Views.Suggestions,
|
||||
gui.Views.Confirmation,
|
||||
gui.Views.Credentials,
|
||||
|
||||
// this guy will cover everything else when it appears
|
||||
gui.Views.Limit,
|
||||
|
@ -109,7 +109,9 @@ func (self *RealPopupHandler) Prompt(opts types.PromptOpts) error {
|
||||
Prompt: opts.InitialContent,
|
||||
Editable: true,
|
||||
HandleConfirmPrompt: opts.HandleConfirm,
|
||||
HandleClose: opts.HandleClose,
|
||||
FindSuggestionsFunc: opts.FindSuggestionsFunc,
|
||||
Mask: opts.Mask,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,11 @@ type IGuiCommon interface {
|
||||
|
||||
GetAppState() *config.AppState
|
||||
SaveAppState() error
|
||||
|
||||
// Runs the given function on the UI thread (this is for things like showing a popup asking a user for input).
|
||||
// Only necessary to call if you're not already on the UI thread i.e. you're inside a goroutine.
|
||||
// All controller handlers are executed on the UI thread.
|
||||
OnUIThread(f func() error)
|
||||
}
|
||||
|
||||
type IPopupHandler interface {
|
||||
@ -73,6 +78,7 @@ type CreatePopupPanelOpts struct {
|
||||
HandlersManageFocus bool
|
||||
|
||||
FindSuggestionsFunc func(string) []*Suggestion
|
||||
Mask bool
|
||||
}
|
||||
|
||||
type AskOpts struct {
|
||||
@ -88,6 +94,9 @@ type PromptOpts struct {
|
||||
InitialContent string
|
||||
FindSuggestionsFunc func(string) []*Suggestion
|
||||
HandleConfirm func(string) error
|
||||
// CAPTURE THIS
|
||||
HandleClose func() error
|
||||
Mask bool
|
||||
}
|
||||
|
||||
type MenuItem struct {
|
||||
|
@ -80,7 +80,7 @@ func (gui *Gui) globalOptionsMap() map[string]string {
|
||||
}
|
||||
|
||||
func (gui *Gui) isPopupPanel(viewName string) bool {
|
||||
return viewName == "commitMessage" || viewName == "credentials" || viewName == "confirmation" || viewName == "menu"
|
||||
return viewName == "commitMessage" || viewName == "confirmation" || viewName == "menu"
|
||||
}
|
||||
|
||||
func (gui *Gui) popupPanelFocused() bool {
|
||||
|
@ -191,7 +191,6 @@ func chineseTranslationSet() TranslationSet {
|
||||
TagsTitle: "标签页面",
|
||||
MenuTitle: "菜单",
|
||||
RemotesTitle: "远程页面",
|
||||
CredentialsTitle: "证书",
|
||||
RemoteBranchesTitle: "远程分支(在远程页面中)",
|
||||
PatchBuildingTitle: "构建补丁中",
|
||||
InformationTitle: "信息",
|
||||
|
@ -161,7 +161,6 @@ func dutchTranslationSet() TranslationSet {
|
||||
TagsTitle: "Tags Tabblad",
|
||||
MenuTitle: "Menu",
|
||||
RemotesTitle: "Remotes Tabblad",
|
||||
CredentialsTitle: "Credentials",
|
||||
RemoteBranchesTitle: "Remote Branches (in Remotes tabblad)",
|
||||
PatchBuildingTitle: "Patch Bouwen",
|
||||
InformationTitle: "Informatie",
|
||||
|
@ -177,7 +177,6 @@ type TranslationSet struct {
|
||||
TagsTitle string
|
||||
MenuTitle string
|
||||
RemotesTitle string
|
||||
CredentialsTitle string
|
||||
RemoteBranchesTitle string
|
||||
PatchBuildingTitle string
|
||||
InformationTitle string
|
||||
@ -748,7 +747,6 @@ func EnglishTranslationSet() TranslationSet {
|
||||
TagsTitle: "Tags Tab",
|
||||
MenuTitle: "Menu",
|
||||
RemotesTitle: "Remotes Tab",
|
||||
CredentialsTitle: "Credentials",
|
||||
RemoteBranchesTitle: "Remote Branches (in Remotes tab)",
|
||||
PatchBuildingTitle: "Patch Building",
|
||||
InformationTitle: "Information",
|
||||
|
Loading…
x
Reference in New Issue
Block a user