Status ID + thread view
- Status ID on timeline list view - thread command to view a complete thread Display order: - ancestors - status - descendants
This commit is contained in:
parent
ec95ef3584
commit
ee417df60e
5 changed files with 134 additions and 3 deletions
|
@ -121,6 +121,7 @@ def test_delete(mock_delete, capsys):
|
|||
@mock.patch('toot.http.get')
|
||||
def test_timeline(mock_get, monkeypatch, capsys):
|
||||
mock_get.return_value = MockResponse([{
|
||||
'id': '111111111111111111',
|
||||
'account': {
|
||||
'display_name': 'Frank Zappa',
|
||||
'username': 'fz'
|
||||
|
@ -128,6 +129,7 @@ def test_timeline(mock_get, monkeypatch, capsys):
|
|||
'created_at': '2017-04-12T15:53:18.174Z',
|
||||
'content': "<p>The computer can't tell you the emotional story. It can give you the exact mathematical design, but what's missing is the eyebrows.</p>",
|
||||
'reblog': None,
|
||||
'in_reply_to_id': None
|
||||
}])
|
||||
|
||||
console.run_command(app, user, 'timeline', [])
|
||||
|
@ -139,7 +141,94 @@ def test_timeline(mock_get, monkeypatch, capsys):
|
|||
assert "but what's missing is the eyebrows." in out
|
||||
assert "Frank Zappa" in out
|
||||
assert "@fz" in out
|
||||
assert "id: 111111111111111111" in out
|
||||
assert "[RE]" not in out
|
||||
|
||||
@mock.patch('toot.http.get')
|
||||
def test_timeline_with_re(mock_get, monkeypatch, capsys):
|
||||
mock_get.return_value = MockResponse([{
|
||||
'id': '111111111111111111',
|
||||
'account': {
|
||||
'display_name': 'Frank Zappa',
|
||||
'username': 'fz'
|
||||
},
|
||||
'created_at': '2017-04-12T15:53:18.174Z',
|
||||
'content': "<p>The computer can't tell you the emotional story. It can give you the exact mathematical design, but what's missing is the eyebrows.</p>",
|
||||
'reblog': None,
|
||||
'in_reply_to_id': '111111111111111110'
|
||||
}])
|
||||
|
||||
console.run_command(app, user, 'timeline', [])
|
||||
|
||||
mock_get.assert_called_once_with(app, user, '/api/v1/timelines/home')
|
||||
|
||||
out, err = capsys.readouterr()
|
||||
assert "The computer can't tell you the emotional story." in out
|
||||
assert "but what's missing is the eyebrows." in out
|
||||
assert "Frank Zappa" in out
|
||||
assert "@fz" in out
|
||||
assert "id: 111111111111111111" in out
|
||||
assert "[RE]" in out
|
||||
|
||||
@mock.patch('toot.http.get')
|
||||
def test_thread(mock_get, monkeypatch, capsys):
|
||||
mock_get.side_effect = [
|
||||
MockResponse({
|
||||
'id': '111111111111111111',
|
||||
'account': {
|
||||
'display_name': 'Frank Zappa',
|
||||
'username': 'fz'
|
||||
},
|
||||
'created_at': '2017-04-12T15:53:18.174Z',
|
||||
'content': "my response in the middle",
|
||||
'reblog': None,
|
||||
'in_reply_to_id': '111111111111111110'
|
||||
}),
|
||||
MockResponse({
|
||||
'ancestors': [{
|
||||
'id': '111111111111111110',
|
||||
'account': {
|
||||
'display_name': 'Frank Zappa',
|
||||
'username': 'fz'
|
||||
},
|
||||
'created_at': '2017-04-12T15:53:18.174Z',
|
||||
'content': "original content",
|
||||
'reblog': None,
|
||||
'in_reply_to_id': None}],
|
||||
'descendants': [{
|
||||
'id': '111111111111111112',
|
||||
'account': {
|
||||
'display_name': 'Frank Zappa',
|
||||
'username': 'fz'
|
||||
},
|
||||
'created_at': '2017-04-12T15:53:18.174Z',
|
||||
'content': "response message",
|
||||
'reblog': None,
|
||||
'in_reply_to_id': '111111111111111111'}],
|
||||
}),
|
||||
]
|
||||
|
||||
console.run_command(app, user, 'thread', ['111111111111111111'])
|
||||
|
||||
calls = [
|
||||
mock.call(app, user, '/api/v1/statuses/111111111111111111'),
|
||||
mock.call(app, user, '/api/v1/statuses/111111111111111111/context'),
|
||||
]
|
||||
mock_get.assert_has_calls(calls, any_order=False)
|
||||
|
||||
out, err = capsys.readouterr()
|
||||
|
||||
# Display order
|
||||
assert out.index('original content') < out.index('my response in the middle')
|
||||
assert out.index('my response in the middle') < out.index('response message')
|
||||
|
||||
assert "original content" in out
|
||||
assert "my response in the middle" in out
|
||||
assert "response message" in out
|
||||
assert "Frank Zappa" in out
|
||||
assert "@fz" in out
|
||||
assert "id: 111111111111111111" in out
|
||||
assert "[RE]" in out
|
||||
|
||||
@mock.patch('toot.http.post')
|
||||
def test_upload(mock_post, capsys):
|
||||
|
|
18
toot/api.py
18
toot/api.py
|
@ -17,10 +17,17 @@ def _account_action(app, user, account, action):
|
|||
return http.post(app, user, url).json()
|
||||
|
||||
|
||||
def _status_action(app, user, status_id, action):
|
||||
url = '/api/v1/statuses/{}/{}'.format(status_id, action)
|
||||
def _status_action(app, user, status_id, action, method='post'):
|
||||
if action is None:
|
||||
url = '/api/v1/statuses/{}'.format(status_id)
|
||||
method = 'get'
|
||||
else:
|
||||
url = '/api/v1/statuses/{}/{}'.format(status_id, action)
|
||||
|
||||
return http.post(app, user, url).json()
|
||||
if method == 'post':
|
||||
return http.post(app, user, url).json()
|
||||
elif method == 'get':
|
||||
return http.get(app, user, url).json()
|
||||
|
||||
|
||||
def create_app(domain, scheme='https'):
|
||||
|
@ -143,6 +150,8 @@ def pin(app, user, status_id):
|
|||
def unpin(app, user, status_id):
|
||||
return _status_action(app, user, status_id, 'unpin')
|
||||
|
||||
def context(app, user, status_id):
|
||||
return _status_action(app, user, status_id, 'context', method='get')
|
||||
|
||||
def timeline_home(app, user):
|
||||
return http.get(app, user, '/api/v1/timelines/home').json()
|
||||
|
@ -245,6 +254,9 @@ def verify_credentials(app, user):
|
|||
return http.get(app, user, '/api/v1/accounts/verify_credentials').json()
|
||||
|
||||
|
||||
def single_status(app, user, status_id):
|
||||
return _status_action(app, user, status_id, None, method='get')
|
||||
|
||||
def get_notifications(app, user):
|
||||
return http.get(app, user, '/api/v1/notifications').json()
|
||||
|
||||
|
|
|
@ -29,6 +29,19 @@ def timeline(app, user, args):
|
|||
|
||||
print_timeline(items)
|
||||
|
||||
def thread(app, user, args):
|
||||
toot = api.single_status(app, user, args.status_id)
|
||||
context = api.context(app, user, args.status_id)
|
||||
thread = []
|
||||
for item in context['ancestors']:
|
||||
thread.append(item)
|
||||
|
||||
thread.append(toot)
|
||||
|
||||
for item in context['descendants']:
|
||||
thread.append(item)
|
||||
|
||||
print_timeline(thread)
|
||||
|
||||
def curses(app, user, args):
|
||||
from toot.ui.app import TimelineApp
|
||||
|
|
|
@ -154,6 +154,16 @@ READ_COMMANDS = [
|
|||
],
|
||||
require_auth=True,
|
||||
),
|
||||
Command(
|
||||
name="thread",
|
||||
description="Show toot thfread items",
|
||||
arguments=[
|
||||
(["status_id"], {
|
||||
"help": "Show thread for toot.",
|
||||
}),
|
||||
],
|
||||
require_auth=True,
|
||||
),
|
||||
Command(
|
||||
name="timeline",
|
||||
description="Show recent items in a timeline (home by default)",
|
||||
|
|
|
@ -137,6 +137,11 @@ def print_timeline(items):
|
|||
if item['reblogged']:
|
||||
left_column.append("Reblogged @{}".format(item['reblogged']))
|
||||
|
||||
if item['reply_to_toot'] is not None:
|
||||
left_column.append('[RE]')
|
||||
|
||||
left_column.append("id: {}".format(item['id']))
|
||||
|
||||
right_column = wrap_text(item['text'], 80)
|
||||
|
||||
return zip_longest(left_column, right_column, fillvalue="")
|
||||
|
@ -153,10 +158,12 @@ def print_timeline(items):
|
|||
time = datetime.strptime(item['created_at'], "%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
|
||||
return {
|
||||
"id": item['id'],
|
||||
"account": item['account'],
|
||||
"text": text,
|
||||
"time": time,
|
||||
"reblogged": reblogged,
|
||||
"reply_to_toot": item['in_reply_to_id']
|
||||
}
|
||||
|
||||
print_out("─" * 31 + "┬" + "─" * 88)
|
||||
|
|
Loading…
Reference in a new issue