Merge remote-tracking branch 'upstream/master'
Refactoring
This commit is contained in:
216
rtv/content.py
216
rtv/content.py
@@ -213,9 +213,15 @@ class Content(object):
|
||||
|
||||
data = {}
|
||||
data['object'] = subscription
|
||||
data['type'] = 'Subscription'
|
||||
data['name'] = "/r/" + subscription.display_name
|
||||
data['title'] = subscription.title
|
||||
if isinstance(subscription, praw.objects.Multireddit):
|
||||
data['type'] = 'Multireddit'
|
||||
data['name'] = subscription.path
|
||||
data['title'] = subscription.description_md
|
||||
else:
|
||||
data['type'] = 'Subscription'
|
||||
data['name'] = "/r/" + subscription.display_name
|
||||
data['title'] = subscription.title
|
||||
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
@@ -399,75 +405,149 @@ class SubredditContent(Content):
|
||||
|
||||
@classmethod
|
||||
def from_name(cls, reddit, name, loader, order=None, query=None):
|
||||
"""
|
||||
Params:
|
||||
reddit (praw.Reddit): Instance of the reddit api.
|
||||
name (text): The name of the desired subreddit, user, multireddit,
|
||||
etc. In most cases this translates directly from the URL that
|
||||
reddit itself uses. This is what users will type in the command
|
||||
prompt when they navigate to a new location.
|
||||
loader (terminal.loader): Handler for the load screen that will be
|
||||
displayed when making http requests.
|
||||
order (text): If specified, the order that posts will be sorted in.
|
||||
For `top` and `controversial`, you can specify the time frame
|
||||
by including a dash, e.g. "top-year". If an order is not
|
||||
specified, it will be extracted from the name.
|
||||
query (text): Content to search for on the given subreddit or
|
||||
user's page.
|
||||
"""
|
||||
# TODO: refactor this into smaller methods
|
||||
|
||||
# Strip leading and trailing backslashes
|
||||
name = name.strip(' /')
|
||||
if name.startswith('r/'):
|
||||
name = name[2:]
|
||||
# Strip leading, trailing, and redundant backslashes
|
||||
parts = [seg for seg in name.strip(' /').split('/') if seg]
|
||||
|
||||
# If the order is not given explicitly, it will be searched for and
|
||||
# stripped out of the subreddit name e.g. python/new.
|
||||
if '/' in name:
|
||||
name, name_order = name.split('/')
|
||||
order = order or name_order
|
||||
display_name = '/r/{0}'.format(name)
|
||||
# Check for the resource type, assume /r/ as the default
|
||||
if len(parts) >= 3 and parts[2] == 'm':
|
||||
# E.g. /u/multi-mod/m/android
|
||||
resource_root, parts = '/'.join(parts[:3]), parts[3:]
|
||||
elif len(parts) > 1 and parts[0] in ['r', 'u', 'user', 'domain']:
|
||||
resource_root = parts.pop(0)
|
||||
else:
|
||||
resource_root = 'r'
|
||||
|
||||
# There should at most two parts left, the resource and the order
|
||||
if len(parts) == 1:
|
||||
resource, resource_order = parts[0], None
|
||||
elif len(parts) == 2:
|
||||
resource, resource_order = parts
|
||||
else:
|
||||
raise InvalidSubreddit()
|
||||
|
||||
if not resource:
|
||||
# Praw does not correctly handle empty strings
|
||||
# https://github.com/praw-dev/praw/issues/615
|
||||
raise InvalidSubreddit()
|
||||
|
||||
# If the order was explicitly passed in, it will take priority over
|
||||
# the order that was extracted from the name
|
||||
order = order or resource_order
|
||||
|
||||
display_order = order
|
||||
display_name = '/'.join(['', resource_root, resource])
|
||||
|
||||
# Split the order from the period E.g. controversial-all, top-hour
|
||||
if order and '-' in order:
|
||||
order, period = order.split('-', 1)
|
||||
else:
|
||||
period = None
|
||||
|
||||
if order not in ['hot', 'top', 'rising', 'new', 'controversial', None]:
|
||||
raise exceptions.SubredditError('Unrecognized order "%s"' % order)
|
||||
raise InvalidSubreddit('Invalid order "%s"' % order)
|
||||
if period not in ['all', 'day', 'hour', 'month', 'week', 'year', None]:
|
||||
raise InvalidSubreddit('Invalid period "%s"' % period)
|
||||
if period and order not in ['top', 'controversial']:
|
||||
raise InvalidSubreddit('"%s" order does not allow sorting by'
|
||||
'period' % order)
|
||||
|
||||
if name == 'me':
|
||||
# On some objects, praw doesn't allow you to pass arguments for the
|
||||
# order and period. Instead you need to call special helper functions
|
||||
# such as Multireddit.get_controversial_from_year(). Build the method
|
||||
# name here for convenience.
|
||||
if period:
|
||||
method_alias = 'get_{0}_from_{1}'.format(order, period)
|
||||
elif order:
|
||||
method_alias = 'get_{0}'.format(order)
|
||||
else:
|
||||
method_alias = 'get_hot'
|
||||
|
||||
# Here's where we start to build the submission generators
|
||||
if query:
|
||||
if resource_root in ('u', 'user'):
|
||||
search = '/r/{subreddit}/search'
|
||||
author = reddit.user.name if resource == 'me' else resource
|
||||
query = 'author:{0} {1}'.format(author, query)
|
||||
subreddit = None
|
||||
else:
|
||||
search = resource_root + '/{subreddit}/search'
|
||||
subreddit = None if resource == 'front' else resource
|
||||
|
||||
reddit.config.API_PATHS['search'] = search
|
||||
submissions = reddit.search(query, subreddit=subreddit,
|
||||
sort=order, period=period)
|
||||
|
||||
elif resource_root == 'domain':
|
||||
order = order or 'hot'
|
||||
submissions = reddit.get_domain_listing(
|
||||
resource, sort=order, period=period, limit=None)
|
||||
|
||||
elif resource_root.endswith('/m'):
|
||||
redditor = resource_root.split('/')[1]
|
||||
multireddit = reddit.get_multireddit(redditor, resource)
|
||||
submissions = getattr(multireddit, method_alias)(limit=None)
|
||||
|
||||
elif resource_root in ('u', 'user') and resource == 'me':
|
||||
if not reddit.is_oauth_session():
|
||||
raise exceptions.AccountError('Not logged in')
|
||||
elif order:
|
||||
submissions = reddit.user.get_submitted(sort=order)
|
||||
else:
|
||||
submissions = reddit.user.get_submitted()
|
||||
order = order or 'new'
|
||||
submissions = reddit.user.get_submitted(sort=order, limit=None)
|
||||
|
||||
elif name == 'saved':
|
||||
elif resource_root in ('u', 'user') and resource == 'saved':
|
||||
if not reddit.is_oauth_session():
|
||||
raise exceptions.AccountError('Not logged in')
|
||||
elif order:
|
||||
submissions = reddit.user.get_saved(sort=order)
|
||||
else:
|
||||
submissions = reddit.user.get_saved()
|
||||
order = order or 'new'
|
||||
submissions = reddit.user.get_saved(sort=order, limit=None)
|
||||
|
||||
elif query:
|
||||
if name == 'front':
|
||||
submissions = reddit.search(query, subreddit=None, sort=order)
|
||||
elif resource_root in ('u', 'user'):
|
||||
order = order or 'new'
|
||||
period = period or 'all'
|
||||
redditor = reddit.get_redditor(resource)
|
||||
submissions = redditor.get_submitted(
|
||||
sort=order, time=period, limit=None)
|
||||
|
||||
elif resource == 'front':
|
||||
if order in (None, 'hot'):
|
||||
submissions = reddit.get_front_page(limit=None)
|
||||
elif period:
|
||||
# For the front page, praw makes you send the period as `t`
|
||||
# instead of calling reddit.get_hot_from_week()
|
||||
method_alias = 'get_{0}'.format(order)
|
||||
method = getattr(reddit, method_alias)
|
||||
submissions = method(limit=None, t=period)
|
||||
else:
|
||||
submissions = reddit.search(query, subreddit=name, sort=order)
|
||||
submissions = getattr(reddit, method_alias)(limit=None)
|
||||
|
||||
else:
|
||||
if name == '':
|
||||
# Praw does not correctly handle empty strings
|
||||
# https://github.com/praw-dev/praw/issues/615
|
||||
raise InvalidSubreddit()
|
||||
subreddit = reddit.get_subreddit(resource)
|
||||
submissions = getattr(subreddit, method_alias)(limit=None)
|
||||
|
||||
if name == 'front':
|
||||
dispatch = {
|
||||
None: reddit.get_front_page,
|
||||
'hot': reddit.get_front_page,
|
||||
'top': reddit.get_top,
|
||||
'rising': reddit.get_rising,
|
||||
'new': reddit.get_new,
|
||||
'controversial': reddit.get_controversial,
|
||||
}
|
||||
else:
|
||||
subreddit = reddit.get_subreddit(name)
|
||||
# For special subreddits like /r/random we want to replace the
|
||||
# display name with the one returned by the request.
|
||||
display_name = '/r/{0}'.format(subreddit.display_name)
|
||||
dispatch = {
|
||||
None: subreddit.get_hot,
|
||||
'hot': subreddit.get_hot,
|
||||
'top': subreddit.get_top,
|
||||
'rising': subreddit.get_rising,
|
||||
'new': subreddit.get_new,
|
||||
'controversial': subreddit.get_controversial,
|
||||
}
|
||||
submissions = dispatch[order](limit=None)
|
||||
# For special subreddits like /r/random we want to replace the
|
||||
# display name with the one returned by the request.
|
||||
display_name = '/r/{0}'.format(subreddit.display_name)
|
||||
|
||||
return cls(display_name, submissions, loader, order=order)
|
||||
# We made it!
|
||||
return cls(display_name, submissions, loader, order=display_order)
|
||||
|
||||
def get(self, index, n_cols=70):
|
||||
"""
|
||||
@@ -510,9 +590,9 @@ class SubredditContent(Content):
|
||||
|
||||
class SubscriptionContent(Content):
|
||||
|
||||
def __init__(self, subscriptions, loader):
|
||||
def __init__(self, name, subscriptions, loader):
|
||||
|
||||
self.name = "Subscriptions"
|
||||
self.name = name
|
||||
self.order = None
|
||||
self._loader = loader
|
||||
self._subscriptions = subscriptions
|
||||
@@ -521,16 +601,28 @@ class SubscriptionContent(Content):
|
||||
try:
|
||||
self.get(0)
|
||||
except IndexError:
|
||||
raise exceptions.SubscriptionError('No subscriptions')
|
||||
raise exceptions.SubscriptionError('No content')
|
||||
|
||||
@classmethod
|
||||
def from_user(cls, reddit, loader):
|
||||
subscriptions = reddit.get_my_subreddits(limit=None)
|
||||
return cls(subscriptions, loader)
|
||||
def from_user(cls, reddit, loader, content_type='subreddit'):
|
||||
if content_type == 'subreddit':
|
||||
name = 'My Subreddits'
|
||||
items = reddit.get_my_subreddits(limit=None)
|
||||
elif content_type == 'multireddit':
|
||||
name = 'My Multireddits'
|
||||
# Multireddits are returned as a list
|
||||
items = iter(reddit.get_my_multireddits())
|
||||
elif content_type == 'popular':
|
||||
name = 'Popular Subreddits'
|
||||
items = reddit.get_popular_subreddits(limit=None)
|
||||
else:
|
||||
raise exceptions.SubscriptionError('Invalid type %s', content_type)
|
||||
|
||||
return cls(name, items, loader)
|
||||
|
||||
def get(self, index, n_cols=70):
|
||||
"""
|
||||
Grab the `i`th subscription, with the title field formatted to fit
|
||||
Grab the `i`th object, with the title field formatted to fit
|
||||
inside of a window of width `n_cols`
|
||||
"""
|
||||
|
||||
@@ -539,7 +631,7 @@ class SubscriptionContent(Content):
|
||||
|
||||
while index >= len(self._subscription_data):
|
||||
try:
|
||||
with self._loader('Loading subscriptions'):
|
||||
with self._loader('Loading content'):
|
||||
subscription = next(self._subscriptions)
|
||||
if self._loader.exception:
|
||||
raise IndexError
|
||||
|
||||
@@ -34,12 +34,14 @@ HELP = """
|
||||
`e` : Edit an existing post or comment
|
||||
`d` : Delete an existing post or comment
|
||||
`i` : Display new messages prompt
|
||||
`s` : Open/close subscribed subreddits list
|
||||
`s` : Open subscribed subreddits
|
||||
`S` : Open subscribed multireddits
|
||||
|
||||
[Subreddit Mode]
|
||||
`l` or `RIGHT` : Enter the selected submission
|
||||
`/` : Open a prompt to switch subreddits
|
||||
`f` : Open a prompt to search the current subreddit
|
||||
'p' : Toggle between the front page and last visited subreddit
|
||||
|
||||
[Submission Mode]
|
||||
`h` or `LEFT` : Return to subreddit mode
|
||||
|
||||
@@ -27,7 +27,7 @@ class SubredditError(RTVError):
|
||||
|
||||
|
||||
class SubscriptionError(RTVError):
|
||||
"Subscriptions could not be fetched"
|
||||
"Content could not be fetched"
|
||||
|
||||
|
||||
class ProgramError(RTVError):
|
||||
@@ -39,4 +39,4 @@ class BrowserError(RTVError):
|
||||
|
||||
|
||||
class TemporaryFileError(RTVError):
|
||||
"Indicates that an error has occurred and the file should not be deleted"
|
||||
"Indicates that an error has occurred and the file should not be deleted"
|
||||
|
||||
@@ -299,7 +299,8 @@ class Page(object):
|
||||
|
||||
sub_name = self.content.name
|
||||
sub_name = sub_name.replace('/r/front', 'Front Page')
|
||||
sub_name = sub_name.replace('/r/me', 'My Submissions')
|
||||
sub_name = sub_name.replace('/u/me', 'My Submissions')
|
||||
sub_name = sub_name.replace('/u/saved', 'My Saved Submissions')
|
||||
self.term.add_line(window, sub_name, 0, 0)
|
||||
|
||||
# Set the terminal title
|
||||
@@ -340,7 +341,8 @@ class Page(object):
|
||||
text = spacing.join(items)
|
||||
self.term.add_line(window, text, 0, 0)
|
||||
if self.content.order is not None:
|
||||
col = text.find(self.content.order) - 3
|
||||
order = self.content.order.split('-')[0]
|
||||
col = text.find(order) - 3
|
||||
window.chgat(0, col, 3, attr | curses.A_REVERSE)
|
||||
|
||||
self._row += 1
|
||||
|
||||
@@ -122,7 +122,9 @@ SUBREDDIT_POST = c
|
||||
SUBREDDIT_OPEN = l, <KEY_RIGHT>
|
||||
SUBREDDIT_OPEN_IN_BROWSER = o, <LF>, <KEY_ENTER>, <KEY_ENTER>
|
||||
SUBREDDIT_OPEN_SUBSCRIPTIONS = s
|
||||
SUBREDDIT_OPEN_MULTIREDDITS = S
|
||||
SUBREDDIT_FRONTPAGE = p
|
||||
|
||||
; Subscription page
|
||||
SUBSCRIPTION_SELECT = l, <LF>, <KEY_ENTER>, <KEY_RIGHT>
|
||||
SUBSCRIPTION_EXIT = h, s, <ESC>, <KEY_LEFT>
|
||||
SUBSCRIPTION_EXIT = h, s, S, <ESC>, <KEY_LEFT>
|
||||
|
||||
@@ -150,7 +150,7 @@ class SubmissionPage(Page):
|
||||
@SubmissionController.register(Command('SUBMISSION_OPEN_IN_URLVIEWER'))
|
||||
def comment_urlview(self):
|
||||
data = self.content.get(self.nav.absolute_index)
|
||||
comment = data.get('body', '')
|
||||
comment = data.get('body') or data.get('text') or data.get('url_full')
|
||||
if comment:
|
||||
self.term.open_urlview(comment)
|
||||
else:
|
||||
|
||||
@@ -30,6 +30,7 @@ class SubredditPage(Page):
|
||||
self.controller = SubredditController(self, keymap=config.keymap)
|
||||
self.content = SubredditContent.from_name(reddit, name, term.loader)
|
||||
self.nav = Navigator(self.content.get)
|
||||
self._toggled_subreddit = None
|
||||
|
||||
@SubredditController.register(Command('REFRESH'))
|
||||
def refresh_content(self, order=None, name=None):
|
||||
@@ -55,7 +56,7 @@ class SubredditPage(Page):
|
||||
|
||||
name = name or self.content.name
|
||||
|
||||
query = self.term.prompt_input('Search {0}:'.format(name))
|
||||
query = self.term.prompt_input('Search {0}: '.format(name))
|
||||
if not query:
|
||||
return
|
||||
|
||||
@@ -69,10 +70,27 @@ class SubredditPage(Page):
|
||||
def prompt_subreddit(self):
|
||||
"Open a prompt to navigate to a different subreddit"
|
||||
|
||||
name = self.term.prompt_input('Enter Subreddit: /r/')
|
||||
name = self.term.prompt_input('Enter page: /')
|
||||
if name is not None:
|
||||
self.refresh_content(order='ignore', name=name)
|
||||
|
||||
@SubredditController.register(Command('SUBREDDIT_FRONTPAGE'))
|
||||
def show_frontpage(self):
|
||||
"""
|
||||
If on a subreddit, remember it and head back to the front page.
|
||||
If this was pressed on the front page, go back to the last subreddit.
|
||||
"""
|
||||
|
||||
if not self.content.name == '/r/front':
|
||||
target = '/r/front'
|
||||
self._toggled_subreddit = self.content.name
|
||||
else:
|
||||
target = self._toggled_subreddit
|
||||
|
||||
# target still may be empty string if this command hasn't yet been used
|
||||
if target is not None:
|
||||
self.refresh_content(order='ignore', name=target)
|
||||
|
||||
@SubredditController.register(Command('SUBREDDIT_OPEN'))
|
||||
def open_submission(self, url=None):
|
||||
"Select the current submission to view posts"
|
||||
@@ -157,8 +175,27 @@ class SubredditPage(Page):
|
||||
"Open user subscriptions page"
|
||||
|
||||
with self.term.loader('Loading subscriptions'):
|
||||
page = SubscriptionPage(
|
||||
self.reddit, self.term, self.config, self.oauth)
|
||||
page = SubscriptionPage(self.reddit, self.term, self.config,
|
||||
self.oauth, content_type='subreddit')
|
||||
if self.term.loader.exception:
|
||||
return
|
||||
|
||||
page.loop()
|
||||
|
||||
# When the user has chosen a subreddit in the subscriptions list,
|
||||
# refresh content with the selected subreddit
|
||||
if page.subreddit_data is not None:
|
||||
self.refresh_content(order='ignore',
|
||||
name=page.subreddit_data['name'])
|
||||
|
||||
@SubredditController.register(Command('SUBREDDIT_OPEN_MULTIREDDITS'))
|
||||
@logged_in
|
||||
def open_multireddit_subscriptions(self):
|
||||
"Open user multireddit subscriptions page"
|
||||
|
||||
with self.term.loader('Loading multireddits'):
|
||||
page = SubscriptionPage(self.reddit, self.term, self.config,
|
||||
self.oauth, content_type='multireddit')
|
||||
if self.term.loader.exception:
|
||||
return
|
||||
|
||||
|
||||
@@ -14,12 +14,14 @@ class SubscriptionController(PageController):
|
||||
|
||||
class SubscriptionPage(Page):
|
||||
|
||||
def __init__(self, reddit, term, config, oauth):
|
||||
def __init__(self, reddit, term, config, oauth, content_type='subreddit'):
|
||||
super(SubscriptionPage, self).__init__(reddit, term, config, oauth)
|
||||
|
||||
self.controller = SubscriptionController(self, keymap=config.keymap)
|
||||
self.content = SubscriptionContent.from_user(reddit, term.loader)
|
||||
self.content = SubscriptionContent.from_user(
|
||||
reddit, term.loader, content_type)
|
||||
self.nav = Navigator(self.content.get)
|
||||
self.content_type = content_type
|
||||
self.subreddit_data = None
|
||||
|
||||
@SubscriptionController.register(Command('REFRESH'))
|
||||
@@ -32,8 +34,8 @@ class SubscriptionPage(Page):
|
||||
return
|
||||
|
||||
with self.term.loader():
|
||||
self.content = SubscriptionContent.from_user(self.reddit,
|
||||
self.term.loader)
|
||||
self.content = SubscriptionContent.from_user(
|
||||
self.reddit, self.term.loader, self.content_type)
|
||||
if not self.term.loader.exception:
|
||||
self.nav = Navigator(self.content.get)
|
||||
|
||||
@@ -70,4 +72,4 @@ class SubscriptionPage(Page):
|
||||
row = offset + 1
|
||||
for row, text in enumerate(data['split_title'], start=row):
|
||||
if row in valid_rows:
|
||||
self.term.add_line(win, text, row, 1)
|
||||
self.term.add_line(win, text, row, 1)
|
||||
|
||||
@@ -387,7 +387,7 @@ class Terminal(object):
|
||||
try:
|
||||
with self.suspend():
|
||||
p = subprocess.Popen([pager], stdin=subprocess.PIPE)
|
||||
p.stdin.write(self.clean(data))
|
||||
p.stdin.write(data.encode('utf-8'))
|
||||
p.stdin.close()
|
||||
try:
|
||||
p.wait()
|
||||
@@ -456,15 +456,14 @@ class Terminal(object):
|
||||
urlview = os.getenv('RTV_URLVIEWER') or 'urlview'
|
||||
try:
|
||||
with self.suspend():
|
||||
p = subprocess.Popen([urlview],
|
||||
stdin=subprocess.PIPE)
|
||||
p = subprocess.Popen([urlview], stdin=subprocess.PIPE)
|
||||
try:
|
||||
p.communicate(input=six.b(data))
|
||||
p.communicate(input=data.encode('utf-8'))
|
||||
except KeyboardInterrupt:
|
||||
p.terminate()
|
||||
except OSError:
|
||||
self.show_notification(
|
||||
'Could not open urls with {}'.format(urlview))
|
||||
'Failed to open {0}'.format(urlview))
|
||||
|
||||
def text_input(self, window, allow_resize=False):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user