1# -*- coding: utf-8 -*- 2from __future__ import unicode_literals 3 4import time 5 6from . import docs 7from .content import SubredditContent 8from .page import Page, PageController, logged_in 9from .objects import Navigator, Command 10from .exceptions import TemporaryFileError 11 12 13class SubredditController(PageController): 14 character_map = {} 15 16 17class SubredditPage(Page): 18 BANNER = docs.BANNER_SUBREDDIT 19 FOOTER = docs.FOOTER_SUBREDDIT 20 21 name = 'subreddit' 22 23 def __init__(self, reddit, term, config, oauth, name): 24 """ 25 Params: 26 name (string): Name of subreddit to open 27 """ 28 super(SubredditPage, self).__init__(reddit, term, config, oauth) 29 30 self.controller = SubredditController(self, keymap=config.keymap) 31 self.content = SubredditContent.from_name(reddit, name, term.loader) 32 self.nav = Navigator(self.content.get) 33 self.toggled_subreddit = None 34 35 def handle_selected_page(self): 36 """ 37 Open all selected pages in subwindows except other subreddit pages. 38 """ 39 if not self.selected_page: 40 pass 41 elif self.selected_page.name in ('subscription', 'submission', 'inbox'): 42 # Launch page in a subwindow 43 self.selected_page = self.selected_page.loop() 44 elif self.selected_page.name == 'subreddit': 45 # Replace the current page 46 self.active = False 47 else: 48 raise RuntimeError(self.selected_page.name) 49 50 def refresh_content(self, order=None, name=None): 51 """ 52 Re-download all submissions and reset the page index 53 """ 54 order = order or self.content.order 55 56 # Preserve the query if staying on the current page 57 if name is None: 58 query = self.content.query 59 else: 60 query = None 61 62 name = name or self.content.name 63 64 # Hack to allow an order specified in the name by prompt_subreddit() to 65 # override the current default 66 if order == 'ignore': 67 order = None 68 69 with self.term.loader('Refreshing page'): 70 self.content = SubredditContent.from_name( 71 self.reddit, name, self.term.loader, order=order, query=query) 72 if not self.term.loader.exception: 73 self.nav = Navigator(self.content.get) 74 75 @SubredditController.register(Command('SORT_1')) 76 def sort_content_hot(self): 77 if self.content.query: 78 self.refresh_content(order='relevance') 79 else: 80 self.refresh_content(order='hot') 81 82 @SubredditController.register(Command('SORT_2')) 83 def sort_content_top(self): 84 order = self._prompt_period('top') 85 if order is None: 86 self.term.show_notification('Invalid option') 87 else: 88 self.refresh_content(order=order) 89 90 @SubredditController.register(Command('SORT_3')) 91 def sort_content_rising(self): 92 if self.content.query: 93 order = self._prompt_period('comments') 94 if order is None: 95 self.term.show_notification('Invalid option') 96 else: 97 self.refresh_content(order=order) 98 else: 99 self.refresh_content(order='rising') 100 101 @SubredditController.register(Command('SORT_4')) 102 def sort_content_new(self): 103 self.refresh_content(order='new') 104 105 @SubredditController.register(Command('SORT_5')) 106 def sort_content_controversial(self): 107 if self.content.query: 108 self.term.flash() 109 else: 110 order = self._prompt_period('controversial') 111 if order is None: 112 self.term.show_notification('Invalid option') 113 else: 114 self.refresh_content(order=order) 115 116 @SubredditController.register(Command('SORT_6')) 117 def sort_content_gilded(self): 118 if self.content.query: 119 self.term.flash() 120 else: 121 self.refresh_content(order='gilded') 122 123 @SubredditController.register(Command('SUBREDDIT_SEARCH')) 124 def search_subreddit(self, name=None): 125 """ 126 Open a prompt to search the given subreddit 127 """ 128 name = name or self.content.name 129 130 query = self.term.prompt_input('Search {0}: '.format(name)) 131 if not query: 132 return 133 134 with self.term.loader('Searching'): 135 self.content = SubredditContent.from_name( 136 self.reddit, name, self.term.loader, query=query) 137 if not self.term.loader.exception: 138 self.nav = Navigator(self.content.get) 139 140 @SubredditController.register(Command('SUBREDDIT_FRONTPAGE')) 141 def show_frontpage(self): 142 """ 143 If on a subreddit, remember it and head back to the front page. 144 If this was pressed on the front page, go back to the last subreddit. 145 """ 146 147 if self.content.name != '/r/front': 148 target = '/r/front' 149 self.toggled_subreddit = self.content.name 150 else: 151 target = self.toggled_subreddit 152 153 # target still may be empty string if this command hasn't yet been used 154 if target is not None: 155 self.refresh_content(order='ignore', name=target) 156 157 @SubredditController.register(Command('SUBREDDIT_OPEN')) 158 def open_submission(self, url=None): 159 """ 160 Select the current submission to view posts. 161 """ 162 if url is None: 163 data = self.get_selected_item() 164 url = data['permalink'] 165 if data.get('url_type') == 'selfpost': 166 self.config.history.add(data['url_full']) 167 168 self.selected_page = self.open_submission_page(url) 169 170 @SubredditController.register(Command('SUBREDDIT_OPEN_IN_BROWSER')) 171 def open_link(self): 172 """ 173 Open a link with the webbrowser 174 """ 175 176 data = self.get_selected_item() 177 if data['url_type'] == 'selfpost': 178 self.open_submission() 179 elif data['url_type'] == 'x-post subreddit': 180 self.refresh_content(order='ignore', name=data['xpost_subreddit']) 181 elif data['url_type'] == 'x-post submission': 182 self.open_submission(url=data['url_full']) 183 self.config.history.add(data['url_full']) 184 else: 185 self.term.open_link(data['url_full']) 186 self.config.history.add(data['url_full']) 187 188 @SubredditController.register(Command('SUBREDDIT_POST')) 189 @logged_in 190 def post_submission(self): 191 """ 192 Post a new submission to the given subreddit. 193 """ 194 # Check that the subreddit can be submitted to 195 name = self.content.name 196 if '+' in name or name in ('/r/all', '/r/front', '/r/me', '/u/saved'): 197 self.term.show_notification("Can't post to {0}".format(name)) 198 return 199 200 submission_info = docs.SUBMISSION_FILE.format(name=name) 201 with self.term.open_editor(submission_info) as text: 202 if not text: 203 self.term.show_notification('Canceled') 204 return 205 elif '\n' not in text: 206 self.term.show_notification('Missing body') 207 return 208 209 title, content = text.split('\n', 1) 210 with self.term.loader('Posting', delay=0): 211 submission = self.reddit.submit(name, title, text=content, 212 raise_captcha_exception=True) 213 # Give reddit time to process the submission 214 time.sleep(2.0) 215 if self.term.loader.exception: 216 raise TemporaryFileError() 217 218 if not self.term.loader.exception: 219 # Open the newly created submission 220 self.selected_page = self.open_submission_page(submission=submission) 221 222 @SubredditController.register(Command('SUBREDDIT_HIDE')) 223 @logged_in 224 def hide(self): 225 data = self.get_selected_item() 226 if not hasattr(data["object"], 'hide'): 227 self.term.flash() 228 elif data['hidden']: 229 with self.term.loader('Unhiding'): 230 data['object'].unhide() 231 data['hidden'] = False 232 else: 233 with self.term.loader('Hiding'): 234 data['object'].hide() 235 data['hidden'] = True 236 237 def _draw_item(self, win, data, inverted): 238 239 n_rows, n_cols = win.getmaxyx() 240 n_cols -= 1 # Leave space for the cursor in the first column 241 242 # Handle the case where the window is not large enough to fit the data. 243 valid_rows = range(0, n_rows) 244 offset = 0 if not inverted else -(data['n_rows'] - n_rows) 245 246 n_title = len(data['split_title']) 247 if data['url_full'] in self.config.history: 248 attr = self.term.attr('SubmissionTitleSeen') 249 else: 250 attr = self.term.attr('SubmissionTitle') 251 for row, text in enumerate(data['split_title'], start=offset): 252 if row in valid_rows: 253 self.term.add_line(win, text, row, 1, attr) 254 255 row = n_title + offset 256 if data['url_full'] in self.config.history: 257 attr = self.term.attr('LinkSeen') 258 else: 259 attr = self.term.attr('Link') 260 if row in valid_rows: 261 self.term.add_line(win, '{url}'.format(**data), row, 1, attr) 262 263 row = n_title + offset + 1 264 if row in valid_rows: 265 266 attr = self.term.attr('Score') 267 self.term.add_line(win, '{score}'.format(**data), row, 1, attr) 268 self.term.add_space(win) 269 270 arrow, attr = self.term.get_arrow(data['likes']) 271 self.term.add_line(win, arrow, attr=attr) 272 self.term.add_space(win) 273 274 attr = self.term.attr('Created') 275 self.term.add_line(win, '{created}{edited}'.format(**data), attr=attr) 276 277 if data['comments'] is not None: 278 attr = self.term.attr('Separator') 279 self.term.add_space(win) 280 self.term.add_line(win, '-', attr=attr) 281 282 attr = self.term.attr('CommentCount') 283 self.term.add_space(win) 284 self.term.add_line(win, '{comments}'.format(**data), attr=attr) 285 286 if data['saved']: 287 attr = self.term.attr('Saved') 288 self.term.add_space(win) 289 self.term.add_line(win, '[saved]', attr=attr) 290 291 if data['hidden']: 292 attr = self.term.attr('Hidden') 293 self.term.add_space(win) 294 self.term.add_line(win, '[hidden]', attr=attr) 295 296 if data['stickied']: 297 attr = self.term.attr('Stickied') 298 self.term.add_space(win) 299 self.term.add_line(win, '[stickied]', attr=attr) 300 301 if data['gold']: 302 attr = self.term.attr('Gold') 303 self.term.add_space(win) 304 count = 'x{}'.format(data['gold']) if data['gold'] > 1 else '' 305 text = self.term.gilded + count 306 self.term.add_line(win, text, attr=attr) 307 308 if data['nsfw']: 309 attr = self.term.attr('NSFW') 310 self.term.add_space(win) 311 self.term.add_line(win, 'NSFW', attr=attr) 312 313 row = n_title + offset + 2 314 if row in valid_rows: 315 attr = self.term.attr('SubmissionAuthor') 316 self.term.add_line(win, '{author}'.format(**data), row, 1, attr) 317 self.term.add_space(win) 318 319 attr = self.term.attr('SubmissionSubreddit') 320 self.term.add_line(win, '/r/{subreddit}'.format(**data), attr=attr) 321 322 if data['flair']: 323 attr = self.term.attr('SubmissionFlair') 324 self.term.add_space(win) 325 self.term.add_line(win, '{flair}'.format(**data), attr=attr) 326 327 attr = self.term.attr('CursorBlock') 328 for y in range(n_rows): 329 self.term.addch(win, y, 0, str(' '), attr) 330