Skip to content

Commit 2f74295

Browse files
committed
keys: Add a key to view a message in the browser.
1 parent 4cb5c6f commit 2f74295

File tree

6 files changed

+53
-2
lines changed

6 files changed

+53
-2
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ autohide=autohide
101101
| Search Streams | <kbd>q</kbd> |
102102
| Add/remove thumbs-up reaction to the current message | <kbd>+</kbd> |
103103
| Add/remove star status of the current message | <kbd>*</kbd> |
104+
| View the current message in a web-browser | <kbd>v</kbd> |
104105
| Jump to the Beginning of line | <kbd>Ctrl</kbd> + <kbd>A</kbd> |
105106
| Jump backward one character | <kbd>Ctrl</kbd> + <kbd>B</kbd> / <kbd>←</kbd> |
106107
| Jump backward one word | <kbd>Meta</kbd> + <kbd>B</kbd> |

tests/core/test_core.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import os
12
from platform import platform
23
from typing import Any
34

@@ -173,6 +174,18 @@ def test_show_all_starred(self, mocker, controller, index_all_starred):
173174
msg_ids = {widget.original_widget.message['id'] for widget in widgets}
174175
assert msg_ids == id_list
175176

177+
def test_view_message_in_browser(self, mocker, controller):
178+
# Set DISPLAY environ to be able to run test in Travis
179+
os.environ['DISPLAY'] = ':0'
180+
mock_open = mocker.patch('webbrowser.open', mocker.Mock())
181+
message_id = 123456
182+
controller.model.server_url = 'https://foo.zulipchat.com/'
183+
controller.view_in_browser(message_id)
184+
assert mock_open.call_count == 1
185+
url = mock_open.call_args[0][0]
186+
assert url.startswith(controller.model.server_url)
187+
assert url.endswith('/{}'.format(message_id))
188+
176189
def test_main(self, mocker, controller):
177190
ret_mock = mocker.Mock()
178191
mock_loop = mocker.patch('urwid.MainLoop', return_value=ret_mock)

zulipterminal/config/keys.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@
136136
'keys': {'ctrl c'},
137137
'help_text': 'Quit',
138138
}),
139+
('VIEW_IN_BROWSER', {
140+
'keys': {'v'},
141+
'help_text': 'View the current message in a web browser',
142+
}),
139143
]) # type: OrderedDict[str, KeyBinding]
140144

141145

zulipterminal/core.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
import sys
55
import time
66
import signal
7+
import webbrowser
78

89
import urwid
910
import zulip
1011

1112
from zulipterminal.version import ZT_VERSION
12-
from zulipterminal.helper import asynch
13+
from zulipterminal.helper import asynch, suppress_output
1314
from zulipterminal.model import Model, GetMessagesArgs, ServerConnectionFailure
1415
from zulipterminal.ui import View, Screen
1516
from zulipterminal.ui_tools.utils import create_msg_box_list
@@ -252,6 +253,16 @@ def show_all_starred(self, button: Any) -> None:
252253

253254
self._finalize_show(w_list)
254255

256+
def view_in_browser(self, message_id: int) -> None:
257+
url = '{}#narrow/near/{}'.format(self.model.server_url, message_id)
258+
if (sys.platform != 'darwin' and sys.platform[:3] != 'win' and
259+
not os.environ.get('DISPLAY') and os.environ.get('TERM')):
260+
# Don't try to open web browser if running without a GUI
261+
return
262+
with suppress_output():
263+
# Suppress anything on stdout or stderr when opening the browser
264+
webbrowser.open(url)
265+
255266
def _finalize_show(self, w_list: List[Any]) -> None:
256267
focus_position = self.model.get_focus_in_current_narrow()
257268

zulipterminal/helper.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
from functools import wraps
44
from threading import Thread
55
from typing import (
6-
Any, Dict, List, Set, Tuple, Optional, DefaultDict, FrozenSet, Union
6+
Any, Dict, List, Set, Tuple, Optional, DefaultDict, FrozenSet, Union,
7+
Iterator
78
)
89
from mypy_extensions import TypedDict
10+
from contextlib import contextmanager
911

1012
import os
1113

@@ -347,3 +349,21 @@ def match_user(user: Any, text: str) -> bool:
347349
if keyword.startswith(text.lower()):
348350
return True
349351
return False
352+
353+
354+
@contextmanager
355+
def suppress_output() -> Iterator[Any]:
356+
"""Context manager to redirect stdout and stderr to /dev/null.
357+
358+
Adapted from https://stackoverflow.com/a/2323563
359+
"""
360+
out = os.dup(1)
361+
err = os.dup(2)
362+
os.close(1)
363+
os.close(2)
364+
os.open(os.devnull, os.O_RDWR)
365+
try:
366+
yield
367+
finally:
368+
os.dup2(out, 1)
369+
os.dup2(err, 2)

zulipterminal/ui_tools/boxes.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,8 @@ def keypress(self, size: Tuple[int, int], key: str) -> str:
625625
self.model.controller.view.write_box.msg_write_box.set_edit_pos(
626626
len(quote))
627627
self.model.controller.view.middle_column.set_focus('footer')
628+
elif is_command_key('VIEW_IN_BROWSER', key):
629+
self.model.controller.view_in_browser(self.message['id'])
628630
return key
629631

630632

0 commit comments

Comments
 (0)