refactor: extract changePinModel into its own sub-model
The Change PIN screen was the only screen with its state (pinInput, pinStage, pinMessage) stored directly on the top-level model. Extract it into a changePinModel in screen_changepin.go to match the pattern used by all other screens. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -42,10 +42,8 @@ type model struct {
|
||||
transfer transferModel
|
||||
history historyModel
|
||||
messages messagesModel
|
||||
admin adminModel
|
||||
pinInput string
|
||||
pinStage int // 0=old, 1=new, 2=confirm, 3=done
|
||||
pinMessage string
|
||||
admin adminModel
|
||||
changePin changePinModel
|
||||
}
|
||||
|
||||
func newModel(sess *shell.SessionContext, bankName, terminalID, region string) *model {
|
||||
@@ -130,7 +128,7 @@ func (m *model) View() string {
|
||||
case screenMessages:
|
||||
content = m.messages.View()
|
||||
case screenChangePin:
|
||||
content = m.viewChangePin()
|
||||
content = m.changePin.View()
|
||||
case screenAdmin:
|
||||
content = m.admin.View()
|
||||
}
|
||||
@@ -182,9 +180,7 @@ func (m *model) updateMenu(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return m, tea.Batch(tea.ClearScreen, logAction(m.sess, "MENU 5", "SECURE MESSAGES"))
|
||||
case "6":
|
||||
m.screen = screenChangePin
|
||||
m.pinInput = ""
|
||||
m.pinStage = 0
|
||||
m.pinMessage = ""
|
||||
m.changePin = newChangePinModel()
|
||||
return m, tea.Batch(tea.ClearScreen, logAction(m.sess, "MENU 6", "CHANGE PIN"))
|
||||
case "7":
|
||||
m.quitting = true
|
||||
@@ -278,95 +274,19 @@ func (m *model) updateMessages(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
|
||||
func (m *model) updateChangePin(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
keyMsg, ok := msg.(tea.KeyMsg)
|
||||
if !ok {
|
||||
return m, nil
|
||||
prevStage := m.changePin.stage
|
||||
var cmd tea.Cmd
|
||||
m.changePin, cmd = m.changePin.Update(msg)
|
||||
|
||||
// Log successful PIN change.
|
||||
if m.changePin.stage == 3 && prevStage != 3 {
|
||||
cmd = tea.Batch(cmd, logAction(m.sess, "CHANGE PIN", "PIN CHANGED SUCCESSFULLY"))
|
||||
}
|
||||
|
||||
if m.pinStage == 3 {
|
||||
return m, m.goToMenu()
|
||||
if m.changePin.done {
|
||||
return m, tea.Batch(cmd, m.goToMenu())
|
||||
}
|
||||
|
||||
switch keyMsg.Type {
|
||||
case tea.KeyEnter:
|
||||
switch m.pinStage {
|
||||
case 0:
|
||||
if m.pinInput != "" {
|
||||
m.pinStage = 1
|
||||
m.pinInput = ""
|
||||
}
|
||||
case 1:
|
||||
if len(m.pinInput) >= 4 {
|
||||
m.pinMessage = m.pinInput
|
||||
m.pinStage = 2
|
||||
m.pinInput = ""
|
||||
}
|
||||
case 2:
|
||||
if m.pinInput == m.pinMessage {
|
||||
m.pinStage = 3
|
||||
return m, logAction(m.sess, "CHANGE PIN", "PIN CHANGED SUCCESSFULLY")
|
||||
}
|
||||
m.pinInput = ""
|
||||
m.pinMessage = ""
|
||||
m.pinStage = 1
|
||||
}
|
||||
case tea.KeyEscape:
|
||||
return m, m.goToMenu()
|
||||
case tea.KeyBackspace:
|
||||
if len(m.pinInput) > 0 {
|
||||
m.pinInput = m.pinInput[:len(m.pinInput)-1]
|
||||
}
|
||||
default:
|
||||
ch := keyMsg.String()
|
||||
if len(ch) == 1 && ch[0] >= 32 && ch[0] < 127 && len(m.pinInput) < 12 {
|
||||
m.pinInput += ch
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m *model) viewChangePin() string {
|
||||
var b strings.Builder
|
||||
|
||||
b.WriteString("\n")
|
||||
b.WriteString(centerText("CHANGE PIN"))
|
||||
b.WriteString("\n\n")
|
||||
b.WriteString(thinDivider())
|
||||
b.WriteString("\n\n")
|
||||
|
||||
if m.pinStage == 3 {
|
||||
b.WriteString(titleStyle.Render(" PIN CHANGED SUCCESSFULLY"))
|
||||
b.WriteString("\n\n")
|
||||
b.WriteString(baseStyle.Render(" YOUR NEW PIN IS NOW ACTIVE."))
|
||||
b.WriteString("\n")
|
||||
b.WriteString(baseStyle.Render(" PLEASE USE YOUR NEW PIN FOR ALL FUTURE TRANSACTIONS."))
|
||||
b.WriteString("\n\n")
|
||||
b.WriteString(dimStyle.Render(" PRESS ANY KEY TO RETURN TO MAIN MENU"))
|
||||
} else {
|
||||
prompts := []string{" CURRENT PIN: ", " NEW PIN: ", " CONFIRM PIN: "}
|
||||
for i := 0; i < m.pinStage; i++ {
|
||||
b.WriteString(baseStyle.Render(prompts[i]))
|
||||
b.WriteString(baseStyle.Render(strings.Repeat("*", 4)))
|
||||
b.WriteString("\n")
|
||||
}
|
||||
if m.pinStage < 3 {
|
||||
b.WriteString(titleStyle.Render(prompts[m.pinStage]))
|
||||
masked := strings.Repeat("*", len(m.pinInput))
|
||||
b.WriteString(inputStyle.Render(masked))
|
||||
b.WriteString(inputStyle.Render("_"))
|
||||
b.WriteString("\n")
|
||||
}
|
||||
b.WriteString("\n")
|
||||
if m.pinStage == 1 {
|
||||
b.WriteString(dimStyle.Render(" PIN MUST BE AT LEAST 4 CHARACTERS"))
|
||||
b.WriteString("\n")
|
||||
}
|
||||
b.WriteString("\n")
|
||||
b.WriteString(dimStyle.Render(" PRESS ESC TO RETURN TO MAIN MENU"))
|
||||
}
|
||||
b.WriteString("\n")
|
||||
|
||||
return b.String()
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
func (m *model) updateAdmin(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
|
||||
111
internal/shell/banking/screen_changepin.go
Normal file
111
internal/shell/banking/screen_changepin.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package banking
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
type changePinModel struct {
|
||||
input string
|
||||
stage int // 0=old, 1=new, 2=confirm, 3=done
|
||||
newPin string
|
||||
done bool
|
||||
}
|
||||
|
||||
func newChangePinModel() changePinModel {
|
||||
return changePinModel{}
|
||||
}
|
||||
|
||||
func (m changePinModel) Update(msg tea.Msg) (changePinModel, tea.Cmd) {
|
||||
keyMsg, ok := msg.(tea.KeyMsg)
|
||||
if !ok {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
if m.stage == 3 {
|
||||
m.done = true
|
||||
return m, nil
|
||||
}
|
||||
|
||||
switch keyMsg.Type {
|
||||
case tea.KeyEnter:
|
||||
switch m.stage {
|
||||
case 0:
|
||||
if m.input != "" {
|
||||
m.stage = 1
|
||||
m.input = ""
|
||||
}
|
||||
case 1:
|
||||
if len(m.input) >= 4 {
|
||||
m.newPin = m.input
|
||||
m.stage = 2
|
||||
m.input = ""
|
||||
}
|
||||
case 2:
|
||||
if m.input == m.newPin {
|
||||
m.stage = 3
|
||||
} else {
|
||||
m.input = ""
|
||||
m.newPin = ""
|
||||
m.stage = 1
|
||||
}
|
||||
}
|
||||
case tea.KeyEscape:
|
||||
m.done = true
|
||||
case tea.KeyBackspace:
|
||||
if len(m.input) > 0 {
|
||||
m.input = m.input[:len(m.input)-1]
|
||||
}
|
||||
default:
|
||||
ch := keyMsg.String()
|
||||
if len(ch) == 1 && ch[0] >= 32 && ch[0] < 127 && len(m.input) < 12 {
|
||||
m.input += ch
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m changePinModel) View() string {
|
||||
var b strings.Builder
|
||||
|
||||
b.WriteString("\n")
|
||||
b.WriteString(centerText("CHANGE PIN"))
|
||||
b.WriteString("\n\n")
|
||||
b.WriteString(thinDivider())
|
||||
b.WriteString("\n\n")
|
||||
|
||||
if m.stage == 3 {
|
||||
b.WriteString(titleStyle.Render(" PIN CHANGED SUCCESSFULLY"))
|
||||
b.WriteString("\n\n")
|
||||
b.WriteString(baseStyle.Render(" YOUR NEW PIN IS NOW ACTIVE."))
|
||||
b.WriteString("\n")
|
||||
b.WriteString(baseStyle.Render(" PLEASE USE YOUR NEW PIN FOR ALL FUTURE TRANSACTIONS."))
|
||||
b.WriteString("\n\n")
|
||||
b.WriteString(dimStyle.Render(" PRESS ANY KEY TO RETURN TO MAIN MENU"))
|
||||
} else {
|
||||
prompts := []string{" CURRENT PIN: ", " NEW PIN: ", " CONFIRM PIN: "}
|
||||
for i := 0; i < m.stage; i++ {
|
||||
b.WriteString(baseStyle.Render(prompts[i]))
|
||||
b.WriteString(baseStyle.Render(strings.Repeat("*", 4)))
|
||||
b.WriteString("\n")
|
||||
}
|
||||
if m.stage < 3 {
|
||||
b.WriteString(titleStyle.Render(prompts[m.stage]))
|
||||
masked := strings.Repeat("*", len(m.input))
|
||||
b.WriteString(inputStyle.Render(masked))
|
||||
b.WriteString(inputStyle.Render("_"))
|
||||
b.WriteString("\n")
|
||||
}
|
||||
b.WriteString("\n")
|
||||
if m.stage == 1 {
|
||||
b.WriteString(dimStyle.Render(" PIN MUST BE AT LEAST 4 CHARACTERS"))
|
||||
b.WriteString("\n")
|
||||
}
|
||||
b.WriteString("\n")
|
||||
b.WriteString(dimStyle.Render(" PRESS ESC TO RETURN TO MAIN MENU"))
|
||||
}
|
||||
b.WriteString("\n")
|
||||
|
||||
return b.String()
|
||||
}
|
||||
Reference in New Issue
Block a user