Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Retain checkbox for outbox messages #167

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/cli-input.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ var cliCommands = []cliCommand{
{"remove", removeCommand{}, "Remove an attachment or detachment from a draft message", contextDraft},
{"rename", renameCommand{}, "Rename an existing contact", contextContact},
{"reply", replyCommand{}, "Reply to the current message", contextInbox},
{"retain", retainCommand{}, "Retain the current message", contextInbox},
{"retain", retainCommand{}, "Retain the current message", contextInbox | contextOutbox},
{"dont-retain", dontRetainCommand{}, "Do not retain the current message", contextInbox},
{"save", saveCommand{}, "Save a numbered attachment to disk", contextInbox},
{"save-key", saveKeyCommand{}, "Save the key to a detachment to disk", contextInbox},
Expand Down
28 changes: 17 additions & 11 deletions client/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -1611,24 +1611,29 @@ Handle:
}

case retainCommand:
msg, ok := c.currentObj.(*InboxMessage)
if !ok {
c.Printf("%s Select inbox message first\n", termWarnPrefix)
if msg, ok := c.currentObj.(*InboxMessage); ok {
msg.retained = true
} else if msg, ok := c.currentObj.(*queuedMessage); ok {
msg.retained = true
} else {
c.Printf("%s Select inbox or outbox message first\n", termWarnPrefix)
return
}
msg.retained = true
c.save()

case dontRetainCommand:
msg, ok := c.currentObj.(*InboxMessage)
if !ok {
c.Printf("%s Select inbox message first\n", termWarnPrefix)
if msg, ok := c.currentObj.(*InboxMessage); ok {
msg.retained = false
msg.exposureTime = c.Now()
// TODO: the CLI needs to expire messages when open as the GUI
// does. See guiClient.processTimer.
} else if msg, ok := c.currentObj.(*queuedMessage); ok {
msg.retained = false
msg.exposureTime = c.Now()
} else {
c.Printf("%s Select inbox or outbox message first\n", termWarnPrefix)
return
}
msg.retained = false
msg.exposureTime = c.Now()
// TODO: the CLI needs to expire messages when open as the GUI
// does. See guiClient.processTimer.
c.save()

default:
Expand Down Expand Up @@ -1775,6 +1780,7 @@ func (c *cliClient) showOutbox(msg *queuedMessage) {
cliRow{cols: []string{"Sent", sentTime}},
cliRow{cols: []string{"Acknowledged", formatTime(msg.acked)}},
cliRow{cols: []string{"Erase", eraseTime}},
cliRow{cols: []string{"Retain", fmt.Sprintf("%t", msg.retained)}},
},
}
table.WriteTo(c.term)
Expand Down
11 changes: 11 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,17 @@ type queuedMessage struct {
// identity this message for the duration of the session. It's not
// saved to disk.
cliId cliId
// retained is true if the user has chosen to retain this message -
// i.e. to opt it out of the usual, time-based, auto-deletion.
retained bool
// exposureTime contains the time when the message was last "exposed".
// This is used to allow a small period of time for the user to mark a
// message as retained (messageGraceTime). For example, if a message is
// loaded at startup and has expired then it's a candidate for
// deletion, but the exposureTime will be the startup time, which
// ensures that we leave it a few minutes before deletion. Setting
// retained to false also resets the exposureTime.
exposureTime time.Time
}

func (qm *queuedMessage) indicator(contact *Contact) Indicator {
Expand Down
107 changes: 60 additions & 47 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2071,85 +2071,98 @@ func TestRetainMessage(t *testing.T) {
t.Fatalf("Bad initial number of messages in listUI: %d", n)
}

msg := client2.inbox[0]
if msg.retained {
t.Fatalf("Retained flag is initially set")
retainedCheck := func(b bool, s string) {
msg1 := client1.outbox[0]
msg2 := client2.inbox[0]
if msg2.retained != b || msg1.retained != b {
t.Fatalf(s)
}
}
retainedCheck(false, "Retained flag is initially set")

client2.gui.events <- Click{
name: client2.inboxUI.entries[0].boxName,
}
client2.AdvanceTo(uiStateInbox)
client2.gui.events <- Click{
name: "retain",
checks: map[string]bool{"retain": true},
retainedClick := func(client *TestClient, st int, l *listUI, b bool) {
client.gui.events <- Click{
name: l.entries[0].boxName,
}
client.AdvanceTo(st)
client.gui.events <- Click{
name: "retain",
checks: map[string]bool{"retain": b},
}
client.AdvanceTo(st)
}
client2.AdvanceTo(uiStateInbox)
retainedClick(client1, uiStateOutbox, client1.outboxUI, true)
retainedClick(client2, uiStateInbox, client2.inboxUI, true)

if !msg.retained {
t.Fatalf("Retained flag not set")
}
retainedCheck(true, "Retained flag not set")

client1.Reload()
client1.AdvanceTo(uiStateMain)

client2.Reload()
client2.AdvanceTo(uiStateMain)

msg = client2.inbox[0]
if !msg.retained {
t.Fatalf("Retained flag lost")
}
retainedCheck(true, "Retained flag lost")

baseTime := time.Now()
client2.nowFunc = func() time.Time {
return baseTime.Add(messageLifetime + 10*time.Second)
bumpTime := func(client *TestClient, t time.Duration) {
client.nowFunc = func() time.Time {
return baseTime.Add(t)
}
client.testTimerChan <- baseTime
client.AdvanceTo(uiStateTimerComplete)
}

client2.testTimerChan <- baseTime
client2.AdvanceTo(uiStateTimerComplete)

bumpTime(client1, messageLifetime+10*time.Second)
if n := len(client1.outbox); n != 1 {
t.Fatalf("Message was deleted while retain flag set")
}
bumpTime(client2, messageLifetime+10*time.Second)
if n := len(client2.inbox); n != 1 {
t.Fatalf("Message was deleted while retain flag set")
}

client2.gui.events <- Click{
name: client2.inboxUI.entries[0].boxName,
}
client2.AdvanceTo(uiStateInbox)
client2.gui.events <- Click{
name: "retain",
checks: map[string]bool{"retain": false},
}
client2.AdvanceTo(uiStateInbox)
retainedClick(client1, uiStateOutbox, client1.outboxUI, false)
retainedClick(client2, uiStateInbox, client2.inboxUI, false)

if msg.retained {
t.Fatalf("Retain flag not cleared")
retainedCheck(false, "Retained flag not cleared")

client1.testTimerChan <- baseTime
client1.AdvanceTo(uiStateTimerComplete)
if n := len(client1.outbox); n != 1 {
t.Fatalf("Message was deleted while in grace period")
}

client2.testTimerChan <- baseTime
client2.AdvanceTo(uiStateTimerComplete)

if n := len(client2.inbox); n != 1 {
t.Fatalf("Message was deleted while in grace period")
}

client2.nowFunc = func() time.Time {
return baseTime.Add(messageLifetime + messageGraceTime + 20*time.Second)
bumpTime(client1, messageLifetime+messageGraceTime+20*time.Second)
if n := len(client1.outbox); n != 1 {
t.Fatalf("Message deleted while selected")
}

client2.testTimerChan <- baseTime
client2.AdvanceTo(uiStateTimerComplete)

bumpTime(client2, messageLifetime+messageGraceTime+20*time.Second)
if n := len(client2.inbox); n != 1 {
t.Fatalf("Message deleted while selected")
}

client2.gui.events <- Click{
name: "compose",
elsewhere := func(client *TestClient) {
client.gui.events <- Click{
name: "compose",
}
client.AdvanceTo(uiStateCompose)

client.testTimerChan <- baseTime
client.AdvanceTo(uiStateTimerComplete)
}
client2.AdvanceTo(uiStateCompose)

client2.testTimerChan <- baseTime
client2.AdvanceTo(uiStateTimerComplete)
elsewhere(client1)
elsewhere(client2)

if n := len(client1.outbox); n != 0 {
t.Fatalf("Message not deleted")
}
if n := len(client2.inbox); n != 0 {
t.Fatalf("Message not deleted")
}
Expand Down
13 changes: 8 additions & 5 deletions client/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,12 @@ func (c *client) unmarshal(state *disk.State) error {

for _, m := range state.Outbox {
msg := &queuedMessage{
id: *m.Id,
to: *m.To,
server: *m.Server,
created: time.Unix(*m.Created, 0),
id: *m.Id,
to: *m.To,
server: *m.Server,
created: time.Unix(*m.Created, 0),
retained: m.GetRetained(),
exposureTime: now,
}
c.registerId(msg.id)
if len(m.Message) > 0 {
Expand Down Expand Up @@ -328,7 +330,7 @@ func (c *client) marshal() []byte {

var outbox []*disk.Outbox
for _, msg := range c.outbox {
if time.Since(msg.created) > messageLifetime {
if time.Since(msg.created) > messageLifetime && !msg.retained {
continue
}
m := &disk.Outbox{
Expand All @@ -337,6 +339,7 @@ func (c *client) marshal() []byte {
Server: proto.String(msg.server),
Created: proto.Int64(msg.created.Unix()),
Revocation: proto.Bool(msg.revocation),
Retained: proto.Bool(msg.retained),
}
if msg.message != nil {
if m.Message, err = proto.Marshal(msg.message); err != nil {
Expand Down
Loading