├── README.md └── open-in-apollo.user.js /README.md: -------------------------------------------------------------------------------- 1 | # Open-In-Apollo 2 | 3 | A userscript to open Reddit links in the Apollo app on iPhone. Works with sideloaded apollo as well. 4 | 5 | Enhanced search engine integration so Reddit links on search result pages (Google, Bing, etc.) open directly in Apollo without redirecting to Reddit first. 6 | 7 | ## Downloads 8 | Safari extension to run user script (free): https://apps.apple.com/us/app/userscripts/id1463298887 9 | 10 | Userscript code: [Open In Apollo Userscript][OpenInApollo] 11 | 12 | ## How to install 13 | 14 | 1. Install the free open source app [Userscripts](https://apps.apple.com/us/app/userscripts/id1463298887) 15 | 2. Launch the app and choose path for scripts 16 | 3. Enable userscripts on your iPhone in Settings > Safari > Extensions and allow access to sites 17 | 4. On iPhone/iPad tap to see the code: [Open In Apollo Userscript][OpenInApollo] 18 | 5. Tap Aa in safari and Tap Userscripts 19 | 6. Install the detected Userscript. 20 | 21 | ## Usage 22 | Open any reddit link in Safari, you should see a pop up asking if you want to "Open this page in Apollo" 23 | ![C19E3A9A-C537-4AF0-AD17-15F98C4CAC83_4_5005_c](https://github.com/AnthonyGress/Open-In-Apollo/assets/70029654/2c719d2d-7f7b-42d7-b35a-60cdb571d92c) 24 | 25 | 26 | Tested on iOS 16.7.2 27 | 28 | [OpenInApollo]: https://raw.githubusercontent.com/AnthonyGress/Open-In-Apollo/refs/heads/main/open-in-apollo.user.js 29 | -------------------------------------------------------------------------------- /open-in-apollo.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name Open Apollo App 3 | // @version 1.1.0 4 | // @author AnthonyGress 5 | // @match *://*.reddit.com/* 6 | // @match *://*.redditmedia.com/* 7 | // @match *://*.yahoo.com/* 8 | // @match *://*.bing.com/* 9 | // @match *://*.duckduckgo.com/* 10 | // @match *://*.ecosia.org/* 11 | // @match *://*.google.com/* 12 | // @match *://*.google.ad/* 13 | // @match *://*.google.ae/* 14 | // @match *://*.google.com.af/* 15 | // @match *://*.google.com.ag/* 16 | // @match *://*.google.al/* 17 | // @match *://*.google.am/* 18 | // @match *://*.google.co.ao/* 19 | // @match *://*.google.com.ar/* 20 | // @match *://*.google.as/* 21 | // @match *://*.google.at/* 22 | // @match *://*.google.com.au/* 23 | // @match *://*.google.az/* 24 | // @match *://*.google.ba/* 25 | // @match *://*.google.com.bd/* 26 | // @match *://*.google.be/* 27 | // @match *://*.google.bf/* 28 | // @match *://*.google.bg/* 29 | // @match *://*.google.com.bh/* 30 | // @match *://*.google.bi/* 31 | // @match *://*.google.bj/* 32 | // @match *://*.google.com.bn/* 33 | // @match *://*.google.com.bo/* 34 | // @match *://*.google.com.br/* 35 | // @match *://*.google.bs/* 36 | // @match *://*.google.bt/* 37 | // @match *://*.google.co.bw/* 38 | // @match *://*.google.by/* 39 | // @match *://*.google.com.bz/* 40 | // @match *://*.google.ca/* 41 | // @match *://*.google.cd/* 42 | // @match *://*.google.cf/* 43 | // @match *://*.google.cg/* 44 | // @match *://*.google.ch/* 45 | // @match *://*.google.ci/* 46 | // @match *://*.google.co.ck/* 47 | // @match *://*.google.cl/* 48 | // @match *://*.google.cm/* 49 | // @match *://*.google.cn/* 50 | // @match *://*.google.com.co/* 51 | // @match *://*.google.co.cr/* 52 | // @match *://*.google.com.cu/* 53 | // @match *://*.google.cv/* 54 | // @match *://*.google.com.cy/* 55 | // @match *://*.google.cz/* 56 | // @match *://*.google.de/* 57 | // @match *://*.google.dj/* 58 | // @match *://*.google.dk/* 59 | // @match *://*.google.dm/* 60 | // @match *://*.google.com.do/* 61 | // @match *://*.google.dz/* 62 | // @match *://*.google.com.ec/* 63 | // @match *://*.google.ee/* 64 | // @match *://*.google.com.eg/* 65 | // @match *://*.google.es/* 66 | // @match *://*.google.com.et/* 67 | // @match *://*.google.fi/* 68 | // @match *://*.google.com.fj/* 69 | // @match *://*.google.fm/* 70 | // @match *://*.google.fr/* 71 | // @match *://*.google.ga/* 72 | // @match *://*.google.ge/* 73 | // @match *://*.google.gg/* 74 | // @match *://*.google.com.gh/* 75 | // @match *://*.google.com.gi/* 76 | // @match *://*.google.gl/* 77 | // @match *://*.google.gm/* 78 | // @match *://*.google.gr/* 79 | // @match *://*.google.com.gt/* 80 | // @match *://*.google.gy/* 81 | // @match *://*.google.com.hk/* 82 | // @match *://*.google.hn/* 83 | // @match *://*.google.hr/* 84 | // @match *://*.google.ht/* 85 | // @match *://*.google.hu/* 86 | // @match *://*.google.co.id/* 87 | // @match *://*.google.ie/* 88 | // @match *://*.google.co.il/* 89 | // @match *://*.google.im/* 90 | // @match *://*.google.co.in/* 91 | // @match *://*.google.iq/* 92 | // @match *://*.google.is/* 93 | // @match *://*.google.it/* 94 | // @match *://*.google.je/* 95 | // @match *://*.google.com.jm/* 96 | // @match *://*.google.jo/* 97 | // @match *://*.google.co.jp/* 98 | // @match *://*.google.co.ke/* 99 | // @match *://*.google.com.kh/* 100 | // @match *://*.google.ki/* 101 | // @match *://*.google.kg/* 102 | // @match *://*.google.co.kr/* 103 | // @match *://*.google.com.kw/* 104 | // @match *://*.google.kz/* 105 | // @match *://*.google.la/* 106 | // @match *://*.google.com.lb/* 107 | // @match *://*.google.li/* 108 | // @match *://*.google.lk/* 109 | // @match *://*.google.co.ls/* 110 | // @match *://*.google.lt/* 111 | // @match *://*.google.lu/* 112 | // @match *://*.google.lv/* 113 | // @match *://*.google.com.ly/* 114 | // @match *://*.google.co.ma/* 115 | // @match *://*.google.md/* 116 | // @match *://*.google.me/* 117 | // @match *://*.google.mg/* 118 | // @match *://*.google.mk/* 119 | // @match *://*.google.ml/* 120 | // @match *://*.google.com.mm/* 121 | // @match *://*.google.mn/* 122 | // @match *://*.google.com.mt/* 123 | // @match *://*.google.mu/* 124 | // @match *://*.google.mv/* 125 | // @match *://*.google.mw/* 126 | // @match *://*.google.com.mx/* 127 | // @match *://*.google.com.my/* 128 | // @match *://*.google.co.mz/* 129 | // @match *://*.google.com.na/* 130 | // @match *://*.google.com.ng/* 131 | // @match *://*.google.com.ni/* 132 | // @match *://*.google.ne/* 133 | // @match *://*.google.nl/* 134 | // @match *://*.google.no/* 135 | // @match *://*.google.com.np/* 136 | // @match *://*.google.nr/* 137 | // @match *://*.google.nu/* 138 | // @match *://*.google.co.nz/* 139 | // @match *://*.google.com.om/* 140 | // @match *://*.google.com.pa/* 141 | // @match *://*.google.com.pe/* 142 | // @match *://*.google.com.pg/* 143 | // @match *://*.google.com.ph/* 144 | // @match *://*.google.com.pk/* 145 | // @match *://*.google.pl/* 146 | // @match *://*.google.pn/* 147 | // @match *://*.google.com.pr/* 148 | // @match *://*.google.ps/* 149 | // @match *://*.google.pt/* 150 | // @match *://*.google.com.py/* 151 | // @match *://*.google.com.qa/* 152 | // @match *://*.google.ro/* 153 | // @match *://*.google.ru/* 154 | // @match *://*.google.rw/* 155 | // @match *://*.google.com.sa/* 156 | // @match *://*.google.com.sb/* 157 | // @match *://*.google.sc/* 158 | // @match *://*.google.se/* 159 | // @match *://*.google.com.sg/* 160 | // @match *://*.google.sh/* 161 | // @match *://*.google.si/* 162 | // @match *://*.google.sk/* 163 | // @match *://*.google.com.sl/* 164 | // @match *://*.google.sn/* 165 | // @match *://*.google.so/* 166 | // @match *://*.google.sm/* 167 | // @match *://*.google.sr/* 168 | // @match *://*.google.st/* 169 | // @match *://*.google.com.sv/* 170 | // @match *://*.google.td/* 171 | // @match *://*.google.tg/* 172 | // @match *://*.google.co.th/* 173 | // @match *://*.google.com.tj/* 174 | // @match *://*.google.tl/* 175 | // @match *://*.google.tm/* 176 | // @match *://*.google.tn/* 177 | // @match *://*.google.to/* 178 | // @match *://*.google.com.tr/* 179 | // @match *://*.google.tt/* 180 | // @match *://*.google.com.tw/* 181 | // @match *://*.google.co.tz/* 182 | // @match *://*.google.com.ua/* 183 | // @match *://*.google.co.ug/* 184 | // @match *://*.google.co.uk/* 185 | // @match *://*.google.com.uy/* 186 | // @match *://*.google.co.uz/* 187 | // @match *://*.google.com.vc/* 188 | // @match *://*.google.co.ve/* 189 | // @match *://*.google.co.vi/* 190 | // @match *://*.google.com.vn/* 191 | // @match *://*.google.vu/* 192 | // @match *://*.google.ws/* 193 | // @match *://*.google.rs/* 194 | // @match *://*.google.co.za/* 195 | // @match *://*.google.co.zm/* 196 | // @match *://*.google.co.zw/* 197 | // @match *://*.google.cat/* 198 | // @downloadURL https://gist.github.com/AnthonyGress/c04327abc6ff3bc45871468743f00341/raw/open-in-apollo.user.js 199 | // @updateURL https://gist.github.com/AnthonyGress/c04327abc6ff3bc45871468743f00341/raw/open-in-apollo.user.js 200 | // @homepage https://github.com/AnthonyGress/Open-In-Apollo/edit/main/open-in-apollo.user.js 201 | // ==/UserScript== 202 | 203 | // Note: Some search engines like DDG and Bing index redditmedia.com links 204 | const regexRedditIDs = /^(?:https?:\/\/)?(?:(?:www|amp|m|i)\.)?(?:(?:reddit\.com|redditmedia\.com))\/r\/(\w+)(?:\/comments\/(\w+)(?:\/\w+\/(\w+)(?:\/?.*?[?&]context=(\d+))?)?)?/i; 205 | 206 | function openInApollo() { 207 | const apolloUrl = convertToApolloUrl(window.location.href); 208 | if (apolloUrl) { 209 | window.location.href = apolloUrl; 210 | } 211 | } 212 | 213 | function convertToApolloUrl(urlString) { 214 | const match = urlString.match(regexRedditIDs); 215 | const url = new URL(urlString); 216 | 217 | if (url.pathname === '/' || url.pathname === '/?feed=home') { 218 | return 'apollo://'; 219 | } 220 | 221 | if (urlString.includes('/search')) { 222 | return null; 223 | } 224 | 225 | if (match) { 226 | // Apollo doesn't support redditmedia.com links, so convert them to reddit.com 227 | let hostname = url.hostname.replace('redditmedia.com', 'reddit.com'); 228 | 229 | // Comments and posts 230 | if (url.pathname.includes('/comments/')) { 231 | return `apollo://${hostname}${url.pathname}`; 232 | } 233 | 234 | // Handle subreddit links with sorting suffixes 235 | const sortingSuffixes = /\/(new|best|hot|top|rising)\/?$/; 236 | const cleanPath = url.pathname.replace(sortingSuffixes, ''); 237 | return `apollo://${hostname}${cleanPath}`; 238 | } 239 | 240 | return null; 241 | } 242 | 243 | function processSearchResults() { 244 | const searchResults = document.querySelectorAll('a:not([data-apollo-listener-added])'); 245 | searchResults.forEach(link => { 246 | const href = link.href; 247 | if (!href) return; 248 | 249 | if (href.match(regexRedditIDs)) { 250 | const apolloUrl = convertToApolloUrl(href); 251 | if (apolloUrl) { 252 | // Prevent duplicate listeners 253 | link.setAttribute('data-apollo-listener-added', 'true'); 254 | 255 | // Hook into 'click' instead of changing link.href so that long press still works. 256 | link.addEventListener('click', (event) => { 257 | event.preventDefault(); 258 | event.stopPropagation(); 259 | window.location.href = apolloUrl; 260 | }, { capture: true }); 261 | } 262 | } 263 | }); 264 | } 265 | 266 | if (window.location.hostname.includes('reddit.com') || window.location.hostname.includes('redditmedia.com')) { 267 | openInApollo(); 268 | } else { 269 | processSearchResults(); 270 | 271 | // Observer for processing new search results added dynamically 272 | const observer = new MutationObserver((mutations) => { 273 | processSearchResults(); 274 | }); 275 | 276 | observer.observe(document.body, { 277 | childList: true, 278 | subtree: true 279 | }); 280 | } 281 | --------------------------------------------------------------------------------