diff --git a/main.go b/main.go index 4338295..6ab01e6 100644 --- a/main.go +++ b/main.go @@ -6,18 +6,24 @@ import ( "os" "time" + "sync" + "github.com/atotto/clipboard" "github.com/limetext/qml-go" ) +const ( + timeoutTickDuration = 10 * time.Millisecond + clipboardTimeout = 15 * time.Second +) + // UI is the model for the password UI type UI struct { Status string query string - Countdown float64 - countingDown bool - countdownDone chan bool + Countdown float64 + counter Counter ShowMetadata bool @@ -29,6 +35,49 @@ type UI struct { } } +type Counter struct { + sync.Mutex + t *time.Ticker + remaining time.Duration + countingDown bool +} + +func (c *Counter) isRunning() bool { + c.Lock() + defer c.Unlock() + return c.countingDown +} + +func (c *Counter) start(onTick func(remaining float64)) { + c.Lock() + defer c.Unlock() + c.remaining = clipboardTimeout + c.countingDown = true + c.t = time.NewTicker(timeoutTickDuration) + go func() { + for { + <-c.t.C + c.Lock() + c.remaining -= timeoutTickDuration + c.Unlock() + onTick(c.remaining.Seconds()) + } + }() +} + +func (c *Counter) stop() { + c.Lock() + defer c.Unlock() + c.countingDown = false + c.t.Stop() +} + +func (c *Counter) reset() { + c.Lock() + defer c.Unlock() + c.remaining = clipboardTimeout +} + // Passwords is the model for the password list type Passwords struct { Selected int @@ -66,33 +115,24 @@ func (p *Passwords) Get(index int) Password { // ClearClipboard clears the clipboard func (ui *UI) ClearClipboard() { - if ui.countingDown { - ui.countdownDone <- true + + if ui.counter.isRunning() { + ui.counter.reset() + return } - ui.countingDown = true - tick := 10 * time.Millisecond - t := time.NewTicker(tick) - remaining := 15.0 - for { - select { - case <-ui.countdownDone: - t.Stop() - ui.countingDown = false - return - case <-t.C: - ui.setCountdown(remaining) - ui.setStatus(fmt.Sprintf("Will clear in %.f seconds", remaining)) - remaining -= tick.Seconds() - if remaining <= 0 { - clipboard.WriteAll("") - ui.Clearmetadata() - ui.setStatus("Clipboard cleared") - ui.countingDown = false - t.Stop() - return - } + + onTick := func(remaining float64) { + ui.setCountdown(ui.counter.remaining.Seconds()) + ui.setStatus(fmt.Sprintf("Will clear in %.f seconds", remaining)) + if remaining <= 0 { + clipboard.WriteAll("") + ui.Clearmetadata() + ui.setStatus("Clipboard cleared") + ui.counter.stop() } } + + ui.counter.start(onTick) } // CopyToClipboard copies the selected password to the system clipboard @@ -102,12 +142,17 @@ func (p *Passwords) CopyToClipboard(selected int) { return } pw := (p.hits)[selected] - pass := pw.Password() + pass, err := pw.Password() + if err != nil { + ui.setStatus("Cancelled") + return + } + if err := clipboard.WriteAll(pass); err != nil { panic(err) } ui.setStatus("Copied to clipboard") - go ui.ClearClipboard() + ui.ClearClipboard() p.Update("") // Trigger a manual update, since the key is probably unlocked now } diff --git a/pass.go b/pass.go index 2c058aa..840abbb 100644 --- a/pass.go +++ b/pass.go @@ -59,11 +59,14 @@ func (p *Password) Metadata() string { return metadata } -func (p *Password) Password() string { - decrypted, _ := p.decrypt() +func (p *Password) Password() (string, error) { + decrypted, err := p.decrypt() + if err != nil { + return "", err + } nr := bufio.NewReader(decrypted) password, _ := nr.ReadString('\n') - return password + return password, nil } // NewPasswordStore creates a new password store