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

Add option to remove obsolete drafts #1627

Open
wants to merge 1 commit 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
59 changes: 53 additions & 6 deletions alot/commands/envelope.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,48 @@ async def apply(self, ui):
await ui.apply_command(globals.PromptCommand(cmdstring))


async def delete_previous_draft(envelope, ui):
try:
if envelope.previous_draft is None:
return
except AttributeError:
return
if settings.get('envelope_always_delete_old_drafts'):
del_old_draft = True
else:
msg = 'Do you want to delete the old draft?'
del_old_draft = (await ui.choice(msg, cancel='no',
msg_position='left'))
del_old_draft = (del_old_draft == 'yes')
if del_old_draft:
try:
message_path = envelope.previous_draft.get_filename()
except Exception as e:
logging.error(e)
ui.notify('could not get draft path:\n%s' % e,
priority='error', block=True)
return

try:
ui.dbman.remove_message(envelope.previous_draft)
await ui.apply_command(globals.FlushCommand())
except DatabaseError as e:
logging.error(e)
ui.notify('could not remove draft from db:\n%s' % e,
priority='error', block=True)
return

try:
os.unlink(message_path)
except OSError as e:
logging.error(e)
ui.notify('could not delete draft file:\n%s' % e,
priority='error', block=True)
return

envelope.previous_draft = None


@registerCommand(MODE, 'save')
class SaveCommand(Command):
"""save draft"""
Expand All @@ -138,23 +180,26 @@ async def apply(self, ui):
path = account.store_draft_mail(
mail.as_string(policy=email.policy.SMTP, maxheaderlen=sys.maxsize))

msg = 'draft saved successfully'

# add mail to index if maildir path available
if path is not None:
ui.notify(msg + ' to %s' % path)
ui.notify('draft saved successfully to %s' % path)
logging.debug('adding new mail to index')
try:
ui.dbman.add_message(path, account.draft_tags + envelope.tags)
await ui.apply_command(globals.FlushCommand())
await ui.apply_command(commands.globals.BufferCloseCommand())
except DatabaseError as e:
logging.error(str(e))
ui.notify('could not index message:\n%s' % str(e),
priority='error',
block=True)
else:
await ui.apply_command(commands.globals.BufferCloseCommand())
return

await delete_previous_draft(envelope, ui)

# strip the outside '<' and '>' characters from the id
mid = mail['Message-ID'][1:-1]
envelope.previous_draft = ui.dbman.get_message(mid)
await ui.apply_command(commands.globals.BufferCloseCommand())


@registerCommand(MODE, 'send')
Expand Down Expand Up @@ -315,6 +360,8 @@ async def apply(self, ui):
ui.dbman.add_message(path, account.sent_tags + initial_tags)
await ui.apply_command(globals.FlushCommand())

await delete_previous_draft(self.envelope, ui)


@registerCommand(MODE, 'edit', arguments=[
(['--spawn'], {'action': cargparse.BooleanAction, 'default': None,
Expand Down
8 changes: 7 additions & 1 deletion alot/commands/thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,9 +465,15 @@ async def apply(self, ui):
tags.difference_update({'inbox', 'sent', 'draft', 'killed', 'replied',
'signed', 'encrypted', 'unread', 'attachment'})
tags = list(tags)

draft = None
if 'draft' in self.message.get_tags():
draft = self.message

# set body text
mailcontent = self.message.get_body_text()
envelope = Envelope(bodytext=mailcontent, tags=tags)
envelope = Envelope(bodytext=mailcontent, tags=tags,
previous_draft=draft)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite see how this is picket up by my editor then in envelope mode?
Am I missing something or is then envelope -> edit command simply ignoring this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your $EDITOR? Is that necessary? SendCommand and SaveCommand simply pick it up from this field in the envelope.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I was confused and only now understood the logic behind this patch.
Question: What if I answer "no" to the prompt to remove the draft, then create another draft and end up with two drafts, then send the last and say "yes" to the prompt.
As far as I can see, the current logic would only remove the very last draft, not the first one?
If so, is that intentional?

I could not test this myself at the minute because I have problems installing the dev version with poetry.. :D

Copy link
Contributor Author

@milhnl milhnl Aug 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's how it currently works. Removing every revision would require persisting their id's somehow, and that is not how @stevenengler built it. I'm not sure how I would implement that (maybe a custom header?). Currently it remembers the saved draft at runtime in envelope mode, and when it is not deleted, there is no relationship between the old and the new. I'd think that would even be the least surprising behavior.

I tested it by installing by sudo ./setup.py install which immediately says I shouldn't do it that way ;)


# copy selected headers
to_copy = ['Subject', 'From', 'To', 'Cc', 'Bcc', 'In-Reply-To',
Expand Down
3 changes: 2 additions & 1 deletion alot/db/envelope.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class Envelope:
def __init__(
self, template=None, bodytext=None, headers=None, attachments=None,
sign=False, sign_key=None, encrypt=False, tags=None, replied=None,
passed=None, account=None):
passed=None, account=None, previous_draft=None):
"""
:param template: if not None, the envelope will be initialised by
:meth:`parsing <parse_template>` this string before
Expand Down Expand Up @@ -92,6 +92,7 @@ def __init__(
self.tags = tags or [] # tags to add after successful sendout
self.replied = replied # message being replied to
self.passed = passed # message being passed on
self.previous_draft = previous_draft
self.sent_time = None
self.modified_since_sent = False
self.sending = False # semaphore to avoid accidental double sendout
Expand Down
2 changes: 1 addition & 1 deletion alot/db/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ def _with_notmuch_message(self, mid):
mode = Database.MODE.READ_ONLY
with Database(path=self.path, mode=mode) as db:
try:
yield db.find_message(mid)
yield db.find(mid)
except:
errmsg = 'no message with id %s exists!' % mid
raise NonexistantObjectError(errmsg)
Expand Down
4 changes: 4 additions & 0 deletions alot/defaults/alot.rc.spec
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,10 @@ auto_replyto_mailinglist = boolean(default=False)
# prefer plaintext alternatives over html content in multipart/alternative
prefer_plaintext = boolean(default=False)

# When sending an email or saving a new draft, always delete the old draft.
# If set to False, the user will be prompted every time.
envelope_always_delete_old_drafts = boolean(default=False)

# always edit the given body text alternative when editing outgoing messages in envelope mode.
# alternative, and not the html source, even if that is currently displayed.
# If unset, html content will be edited unless the current envelope shows the plaintext alternative.
Expand Down