Reddit Default Sort

Automatically sets Reddit's default sorting order for home and subreddits.

  1. // ==UserScript==
  2. // @name Reddit Default Sort
  3. // @version 1.3.0
  4. // @description Automatically sets Reddit's default sorting order for home and subreddits.
  5. // @author yodaluca23
  6. // @license MIT
  7. // @match *://*.reddit.com/*
  8. // @grant GM_getValue
  9. // @grant GM_setValue
  10. // @namespace https://greasyfork.org/users/1315976
  11. // ==/UserScript==
  12.  
  13. (function() {
  14.     'use strict';
  15.  
  16.     // Default values
  17.     const defaultHomePageSort = "new";
  18.     const defaultSubredditSort = "new";
  19. const defaultCommentSort = "new";
  20. const defaultUserSort = "new";
  21.  
  22. // This allows you to use the Reddit filters temporariliy, without it redirecting you to the default
  23. // This is a domain it should never actually be so it sorts to the default the first time, but doesn't fail being passed to URL()
  24. let lastUrl = "https://www.example.com";
  25.  
  26.     // Function to get the stored sort option, or the default if none is stored.
  27.     function getSortOption(key, defaultValue) {
  28.         let value = GM_getValue(key);
  29.         return value === undefined ? defaultValue : value;
  30.     }
  31.  
  32.     // Get the stored sort options
  33.     let homePageSort = getSortOption("homePageSort", defaultHomePageSort);
  34.     let subredditSort = getSortOption("subredditSort", defaultSubredditSort);
  35. let commentSort = getSortOption("commentSort", defaultCommentSort);
  36. let userSort = getSortOption("userSort", defaultUserSort);
  37.  
  38.     // Function to redirect if necessary
  39.     function redirectIfNeeded() {
  40.         const currentUrl = window.location.href;
  41.  
  42. // Comment page sorting
  43. const commentPattern = /^https?:\/\/www\.reddit\.com\/r\/[^/]+\/comments\/[^/]+\/[^/]+\/?/;
  44. if (commentPattern.test(currentUrl) && !currentUrl.includes(`?sort=${commentSort}`)) {
  45. const urlObj = new URL(currentUrl);
  46. const justRoot = urlObj.origin + urlObj.pathname;
  47. const urlObjLast = new URL(lastUrl);
  48. const justRootLast = urlObjLast.origin + urlObjLast.pathname;
  49. if (justRootLast == justRoot) {
  50. return;
  51. } else {
  52. const newUrl = `${justRoot}?sort=${commentSort}`;
  53. window.location.replace(newUrl);
  54. }
  55. }
  56.  
  57.         // Check for home page URLs
  58.         const homeUrls = [
  59.             "https://www.reddit.com/",
  60.             "https://www.reddit.com/?feed=home",
  61.             "https://www.reddit.com/best/?feed=home",
  62.             "https://www.reddit.com/hot/?feed=home",
  63.             "https://www.reddit.com/top/?feed=home",
  64.             "https://www.reddit.com/new/?feed=home",
  65.             "https://www.reddit.com/rising/?feed=home"
  66.         ];
  67.  
  68.         const homeTargetUrl = `https://www.reddit.com/${homePageSort}/?feed=home`;
  69.  
  70.         if (homeUrls.includes(currentUrl) && currentUrl !== homeTargetUrl && !homeUrls.includes(lastUrl)) {
  71. window.location.replace(homeTargetUrl);
  72. return;
  73.         }
  74.  
  75.         // Check for subreddit URLs
  76. const subredditPattern = /^https?:\/\/www\.reddit\.com\/r\/([^/]+)(\/(hot|new|top|best|rising))?(\/)?(\?.*)?$/;
  77.  
  78. if (subredditPattern.test(currentUrl)) {
  79. const match = currentUrl.match(subredditPattern);
  80. const subredditName = match[1];
  81.  
  82. // Check if were still on the same sub indicates the user only changed their sort we should not set it back
  83. if (subredditPattern.test(lastUrl)) {
  84. const matchLast = lastUrl.match(subredditPattern);
  85. if (subredditName == matchLast[1]) {
  86. return;
  87. }
  88. }
  89.  
  90. const targetUrl = `https://www.reddit.com/r/${subredditName}/${subredditSort}`;
  91. const altTargetUrl = `https://www.reddit.com/r/${subredditName}/${subredditSort}/`;
  92.  
  93. if (currentUrl !== targetUrl && currentUrl !== altTargetUrl) {
  94. window.location.replace(targetUrl);
  95. }
  96. }
  97.  
  98. // Check for user URLs
  99. const userPattern = /^https?:\/\/www\.reddit\.com\/user\/([^\/?]+)\/?(?:\?sort=(?:top|hot|new))?$/;
  100.  
  101. if (userPattern.test(currentUrl)) {
  102. const match = currentUrl.match(userPattern);
  103. const userName = match[1];
  104.  
  105. // Check if were still on the same user indicates our user only changed their sort we should not set it back
  106. if (userPattern.test(lastUrl)) {
  107. const matchLast = lastUrl.match(userPattern);
  108. if (userName == matchLast[1]) {
  109. return;
  110. }
  111. }
  112.  
  113. const targetUrl = `https://www.reddit.com/user/${userName}/?sort=${userSort}`;
  114.  
  115. if (currentUrl !== targetUrl) {
  116. window.location.replace(targetUrl);
  117. }
  118.  
  119. }
  120.     }
  121.  
  122.     // Function to create the settings UI
  123. function createSettingsUI() {
  124. // Create the settings container
  125. const settingsContainer = document.createElement('div');
  126. settingsContainer.id = 'default-sort-settings-container';
  127.  
  128. settingsContainer.style.position = 'fixed';
  129. settingsContainer.style.top = '50%';
  130. settingsContainer.style.left = '50%';
  131. settingsContainer.style.transform = 'translate(-50%, -50%)';
  132. settingsContainer.style.padding = '20px';
  133. settingsContainer.style.borderRadius = '12px';
  134. settingsContainer.style.boxShadow = '0px 8px 20px rgba(0, 0, 0, 0.1)';
  135. settingsContainer.style.zIndex = '1000';
  136. settingsContainer.style.minWidth = '280px';
  137. settingsContainer.style.textAlign = 'center';
  138. settingsContainer.style.fontFamily = '"Arial", sans-serif';
  139.  
  140. // Dark/Light Mode detection based on system preference
  141. const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
  142. if (isDarkMode) {
  143. settingsContainer.style.backgroundColor = '#111111'; // AMOLED dark mode
  144. settingsContainer.style.color = '#fff';
  145. settingsContainer.style.border = '1px solid #444'; // Softer border for dark mode
  146. } else {
  147. settingsContainer.style.backgroundColor = '#f9f9f9';
  148. settingsContainer.style.color = '#000';
  149. settingsContainer.style.border = '1px solid #ccc'; // Softer border for light mode
  150. }
  151.  
  152. // Title
  153. const title = document.createElement('h2');
  154. title.textContent = 'Settings';
  155. title.style.marginBottom = '20px';
  156. settingsContainer.appendChild(title);
  157.  
  158. // Home page sort select
  159. const homePageLabel = document.createElement('label');
  160. homePageLabel.textContent = 'Home Page Sort:';
  161. homePageLabel.style.display = 'block';
  162. homePageLabel.style.marginBottom = '10px';
  163.  
  164. const homePageSelect = document.createElement('select');
  165. homePageSelect.id = 'homePageSortSelect';
  166. homePageSelect.style.width = '100%';
  167. homePageSelect.style.padding = '8px';
  168. homePageSelect.style.borderRadius = '5px';
  169. homePageSelect.style.backgroundColor = isDarkMode ? '#333' : '#fff'; // Darker dropdown in dark mode
  170. homePageSelect.style.color = isDarkMode ? '#fff' : '#000'; // Ensure text contrast is high
  171. homePageSelect.style.border = isDarkMode ? '1px solid #444' : '1px solid #ccc'; // Softer borders
  172.  
  173. ['best', 'hot', 'new', 'top', 'rising'].forEach(option => {
  174. const opt = document.createElement('option');
  175. opt.value = option;
  176. opt.textContent = option.charAt(0).toUpperCase() + option.slice(1);
  177. homePageSelect.appendChild(opt);
  178. });
  179. homePageSelect.value = homePageSort;
  180.  
  181. settingsContainer.appendChild(homePageLabel);
  182. settingsContainer.appendChild(homePageSelect);
  183.  
  184. // Subreddit sort select
  185. const subredditLabel = document.createElement('label');
  186. subredditLabel.textContent = 'Subreddit Sort:';
  187. subredditLabel.style.display = 'block';
  188. subredditLabel.style.marginBottom = '10px';
  189.  
  190. const subredditSelect = document.createElement('select');
  191. subredditSelect.id = 'subredditSortSelect';
  192. subredditSelect.style.width = '100%';
  193. subredditSelect.style.padding = '8px';
  194. subredditSelect.style.borderRadius = '5px';
  195. subredditSelect.style.backgroundColor = isDarkMode ? '#333' : '#fff'; // Darker dropdown in dark mode
  196. subredditSelect.style.color = isDarkMode ? '#fff' : '#000'; // Ensure text contrast is high
  197. subredditSelect.style.border = isDarkMode ? '1px solid #444' : '1px solid #ccc'; // Softer borders
  198.  
  199. ['best', 'hot', 'new', 'top', 'rising'].forEach(option => {
  200. const opt = document.createElement('option');
  201. opt.value = option;
  202. opt.textContent = option.charAt(0).toUpperCase() + option.slice(1);
  203. subredditSelect.appendChild(opt);
  204. });
  205. subredditSelect.value = subredditSort;
  206.  
  207. settingsContainer.appendChild(subredditLabel);
  208. settingsContainer.appendChild(subredditSelect);
  209.  
  210. // Comments sort select
  211. const commentsLabel = document.createElement('label');
  212. commentsLabel.textContent = 'Comments Sort:';
  213. commentsLabel.style.display = 'block';
  214. commentsLabel.style.marginBottom = '10px';
  215.  
  216. const commentSelect = document.createElement('select');
  217. commentSelect.id = 'commentSortSelect';
  218. commentSelect.style.width = '100%';
  219. commentSelect.style.padding = '8px';
  220. commentSelect.style.borderRadius = '5px';
  221. commentSelect.style.backgroundColor = isDarkMode ? '#333' : '#fff'; // Darker dropdown in dark mode
  222. commentSelect.style.color = isDarkMode ? '#fff' : '#000'; // Ensure text contrast is high
  223. commentSelect.style.border = isDarkMode ? '1px solid #444' : '1px solid #ccc'; // Softer borders
  224.  
  225. const optionMapping = {
  226. best: 'confidence',
  227. top: 'top',
  228. new: 'new',
  229. controversial: 'controversial',
  230. old: 'old',
  231. 'Q&A': 'qa'
  232. };
  233.  
  234. Object.entries(optionMapping).forEach(([key, value]) => {
  235. const opt = document.createElement('option');
  236. opt.value = value;
  237. opt.textContent = key.charAt(0).toUpperCase() + key.slice(1);
  238. commentSelect.appendChild(opt);
  239. });
  240.  
  241. commentSelect.value = commentSort;
  242.  
  243. settingsContainer.appendChild(commentsLabel);
  244. settingsContainer.appendChild(commentSelect);
  245.  
  246. // User sort select
  247. const userLabel = document.createElement('label');
  248. userLabel.textContent = 'User Sort:';
  249. userLabel.style.display = 'block';
  250. userLabel.style.marginBottom = '10px';
  251.  
  252. const userSelect = document.createElement('select');
  253. userSelect.id = 'userSortSelect';
  254. userSelect.style.width = '100%';
  255. userSelect.style.padding = '8px';
  256. userSelect.style.borderRadius = '5px';
  257. userSelect.style.backgroundColor = isDarkMode ? '#333' : '#fff'; // Darker dropdown in dark mode
  258. userSelect.style.color = isDarkMode ? '#fff' : '#000'; // Ensure text contrast is high
  259. userSelect.style.border = isDarkMode ? '1px solid #444' : '1px solid #ccc'; // Softer borders
  260.  
  261. ['hot', 'new', 'top'].forEach(option => {
  262. const opt = document.createElement('option');
  263. opt.value = option;
  264. opt.textContent = option.charAt(0).toUpperCase() + option.slice(1);
  265. userSelect.appendChild(opt);
  266. });
  267. userSelect.value = userSort;
  268.  
  269. settingsContainer.appendChild(userLabel);
  270. settingsContainer.appendChild(userSelect);
  271.  
  272. // Buttons container
  273. const buttonsContainer = document.createElement('div');
  274. buttonsContainer.style.marginTop = '20px';
  275. buttonsContainer.style.display = 'flex';
  276. buttonsContainer.style.justifyContent = 'space-between';
  277.  
  278. // Save button
  279. const saveButton = document.createElement('button');
  280. saveButton.textContent = 'Save Settings';
  281. saveButton.style.padding = '10px 20px';
  282. saveButton.style.borderRadius = '5px';
  283. saveButton.style.backgroundColor = '#4CAF50';
  284. saveButton.style.color = 'white';
  285. saveButton.style.border = 'none';
  286. saveButton.style.cursor = 'pointer';
  287. saveButton.style.transition = 'background-color 0.3s ease';
  288. saveButton.style.textAlign = 'center'; // Center text
  289. saveButton.style.display = 'flex';
  290. saveButton.style.alignItems = 'center';
  291. saveButton.style.justifyContent = 'center'; // Ensure text is centered
  292. saveButton.addEventListener('click', () => {
  293. homePageSort = document.getElementById('homePageSortSelect').value;
  294. subredditSort = document.getElementById('subredditSortSelect').value;
  295. commentSort = document.getElementById('commentSortSelect').value;
  296. userSort = document.getElementById('userSortSelect').value;
  297.  
  298. GM_setValue("homePageSort", homePageSort);
  299. GM_setValue("subredditSort", subredditSort);
  300. GM_setValue("commentSort", commentSort);
  301. GM_setValue("userSort", userSort);
  302.  
  303. if (confirm('Settings Saved!\nWant to refresh now?')) {
  304. // User clicked "Yes" / "Ok"
  305. location.reload();
  306. }
  307.  
  308. settingsContainer.remove(); // Remove the settings UI after saving
  309. });
  310. saveButton.addEventListener('mouseover', () => {
  311. saveButton.style.backgroundColor = '#45a049'; // Darker green on hover
  312. });
  313. saveButton.addEventListener('mouseout', () => {
  314. saveButton.style.backgroundColor = '#4CAF50'; // Reset to original green
  315. });
  316.  
  317. // Close button
  318. const closeButton = document.createElement('button');
  319. closeButton.textContent = 'Close';
  320. closeButton.style.padding = '10px 20px';
  321. closeButton.style.borderRadius = '5px';
  322. closeButton.style.backgroundColor = '#f44336';
  323. closeButton.style.color = 'white';
  324. closeButton.style.border = 'none';
  325. closeButton.style.cursor = 'pointer';
  326. closeButton.style.transition = 'background-color 0.3s ease';
  327. closeButton.style.textAlign = 'center'; // Center text
  328. closeButton.style.display = 'flex';
  329. closeButton.style.alignItems = 'center';
  330. closeButton.style.justifyContent = 'center'; // Ensure text is centered
  331. closeButton.addEventListener('click', () => {
  332. settingsContainer.remove();
  333. });
  334. closeButton.addEventListener('mouseover', () => {
  335. closeButton.style.backgroundColor = '#e53935'; // Darker red on hover
  336. });
  337. closeButton.addEventListener('mouseout', () => {
  338. closeButton.style.backgroundColor = '#f44336'; // Reset to original red
  339. });
  340.  
  341. // Append buttons
  342. buttonsContainer.appendChild(saveButton);
  343. buttonsContainer.appendChild(closeButton);
  344. settingsContainer.appendChild(buttonsContainer);
  345.  
  346. // Append the settings container to the body
  347. document.body.appendChild(settingsContainer);
  348. }
  349.  
  350.     // Add a button to the page to open the settings
  351. function addSettingsButton() {
  352. const container = document.querySelector('.pl-lg.gap-xs.flex.items-center.justify-end');
  353.  
  354. if (!container) {
  355. console.warn("Sort Settings button: Target container not found.");
  356. return; // Exit if container is missing
  357. }
  358.  
  359. // Create the button element
  360. const settingsButton = document.createElement('button');
  361. settingsButton.className = 'btn btn-primary flex items-center'; // Flex to align icon and text
  362.  
  363. // Add SVG icon as content
  364. const iconSpan = document.createElement('span');
  365. iconSpan.className = 'flex shrink-0 items-center justify-center h-xl w-xl text-20 leading-4';
  366. iconSpan.innerHTML = `
  367. <svg rpl="" fill="currentColor" height="20" icon-name="settings-outline" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg">
  368. <path d="M10 20c-.401 0-.802-.027-1.2-.079a1.145 1.145 0 0 1-.992-1.137v-1.073a.97.97 0 0 0-.627-.878A.98.98 0 0 0 6.1 17l-.755.753a1.149 1.149 0 0 1-1.521.1 10.16 10.16 0 0 1-1.671-1.671 1.149 1.149 0 0 1 .1-1.523L3 13.906a.97.97 0 0 0 .176-1.069.98.98 0 0 0-.887-.649H1.216A1.145 1.145 0 0 1 .079 11.2a9.1 9.1 0 0 1 0-2.393 1.145 1.145 0 0 1 1.137-.992h1.073a.97.97 0 0 0 .878-.627A.979.979 0 0 0 3 6.1l-.754-.754a1.15 1.15 0 0 1-.1-1.522 10.16 10.16 0 0 1 1.673-1.676 1.155 1.155 0 0 1 1.522.1L6.1 3a.966.966 0 0 0 1.068.176.98.98 0 0 0 .649-.887V1.216A1.145 1.145 0 0 1 8.8.079a9.129 9.129 0 0 1 2.393 0 1.144 1.144 0 0 1 .991 1.137v1.073a.972.972 0 0 0 .628.878A.977.977 0 0 0 13.905 3l.754-.754a1.152 1.152 0 0 1 1.522-.1c.62.49 1.18 1.05 1.671 1.671a1.15 1.15 0 0 1-.1 1.522L17 6.1a.967.967 0 0 0-.176 1.068.98.98 0 0 0 .887.649h1.073a1.145 1.145 0 0 1 1.137.991 9.096 9.096 0 0 1 0 2.392 1.145 1.145 0 0 1-1.137.992h-1.073A1.041 1.041 0 0 0 17 13.905l.753.755a1.149 1.149 0 0 1 .1 1.521c-.49.62-1.05 1.18-1.671 1.671a1.149 1.149 0 0 1-1.522-.1L13.906 17a.97.97 0 0 0-1.069-.176.981.981 0 0 0-.65.887v1.073a1.144 1.144 0 0 1-.99 1.137A9.431 9.431 0 0 1 10 20Zm-.938-1.307a7.638 7.638 0 0 0 1.875 0v-.982a2.292 2.292 0 0 1 3.853-1.6l.693.694a8.796 8.796 0 0 0 1.326-1.326l-.694-.694a2.29 2.29 0 0 1 1.6-3.851h.982a7.746 7.746 0 0 0 0-1.876h-.982a2.213 2.213 0 0 1-2.034-1.4 2.223 2.223 0 0 1 .438-2.451l.694-.693a8.76 8.76 0 0 0-1.327-1.326l-.692.694a2.22 2.22 0 0 1-2.434.445 2.221 2.221 0 0 1-1.419-2.041v-.979a7.638 7.638 0 0 0-1.875 0v.982a2.213 2.213 0 0 1-1.4 2.034 2.23 2.23 0 0 1-2.456-.438l-.693-.694a8.757 8.757 0 0 0-1.326 1.327l.694.692a2.216 2.216 0 0 1 .445 2.434 2.22 2.22 0 0 1-2.041 1.418h-.982a7.746 7.746 0 0 0 0 1.876h.982a2.213 2.213 0 0 1 2.034 1.4 2.223 2.223 0 0 1-.438 2.451l-.694.693c.394.488.838.933 1.326 1.326l.694-.694a2.218 2.218 0 0 1 2.433-.445 2.22 2.22 0 0 1 1.418 2.041v.983ZM10 13.229a3.23 3.23 0 1 1 0-6.458 3.23 3.23 0 0 1 0 6.458Zm0-5.208a1.979 1.979 0 1 0 0 3.958 1.979 1.979 0 0 0 0-3.958Z"></path>
  369. </svg>
  370. `;
  371.  
  372. // Add text content to the button
  373. settingsButton.textContent = 'Sort';
  374. settingsButton.style.marginLeft = '2px';
  375. settingsButton.style.paddingLeft = '10px';
  376. settingsButton.style.paddingRight = '4px';
  377. settingsButton.style.backgroundColor = 'transparent';
  378. settingsButton.style.color = 'rgb(221, 228, 232)';
  379. settingsButton.classList.add('rounded-lg', 'transition-colors', 'duration-200'); // Rounded corners and smooth transition
  380.  
  381. // Hover effect for the button
  382. settingsButton.addEventListener('mouseover', () => {
  383. settingsButton.style.backgroundColor = 'rgb(53, 61, 65)';
  384. settingsButton.style.color = '#ffffff'; // Text color changes on hover
  385. });
  386.  
  387. settingsButton.addEventListener('mouseout', () => {
  388. settingsButton.style.backgroundColor = 'transparent';
  389. settingsButton.style.color = 'rgb(221, 228, 232)';
  390. });
  391.  
  392. settingsButton.addEventListener('mousedown', () => {
  393. settingsButton.style.backgroundColor = 'rgb(83, 90, 94)';
  394. settingsButton.style.color = '#ffffff';
  395. });
  396.  
  397. settingsButton.addEventListener('mouseup', () => {
  398. settingsButton.style.backgroundColor = 'rgb(53, 61, 65)';
  399. let settingsContainerUn = document.getElementById('default-sort-settings-container');
  400. if (settingsContainerUn) {
  401. settingsContainerUn.remove()
  402. } else {
  403. createSettingsUI();
  404. }
  405. });
  406.  
  407. settingsButton.addEventListener('mouseleave', () => {
  408. if (settingsButton.style.backgroundColor !== 'rgb(83, 90, 94)') {
  409. settingsButton.style.backgroundColor = 'transparent';
  410. settingsButton.style.color = 'rgb(221, 228, 232)';
  411. }
  412. });
  413.  
  414. // Append icon and button content to the container
  415. settingsButton.append(iconSpan); // Add the icon before the text
  416.  
  417. // Append the button to the container
  418. container.appendChild(settingsButton);
  419. }
  420.  
  421.     // Initialize
  422.     addSettingsButton();
  423.     redirectIfNeeded();
  424.  
  425. // Function to monitor URL changes in single-page app
  426. function observeUrlChanges(callback) {
  427. lastUrl = location.href;
  428.  
  429. new MutationObserver(() => {
  430. if (location.href !== lastUrl) {
  431. lastUrl = location.href;
  432. callback();
  433. }
  434. }).observe(document, { subtree: true, childList: true });
  435.  
  436. // Also intercept history changes
  437. const pushState = history.pushState;
  438. const replaceState = history.replaceState;
  439.  
  440. history.pushState = function() {
  441. pushState.apply(this, arguments);
  442. callback();
  443. };
  444. history.replaceState = function() {
  445. replaceState.apply(this, arguments);
  446. callback();
  447. };
  448. }
  449.  
  450. // Run `redirectIfNeeded` whenever Reddit changes the page
  451. observeUrlChanges(redirectIfNeeded);
  452.  
  453. })();