| 
									
										
										
										
											2018-01-07 16:00:42 -08:00
										 |  |  | <:Head> | 
					
						
							| 
									
										
										
										
											2018-01-13 12:12:17 -08:00
										 |  |  |   <title>Add an Instance</title> | 
					
						
							| 
									
										
										
										
											2018-01-07 16:00:42 -08:00
										 |  |  | </:Head> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | <Layout page='settings'> | 
					
						
							| 
									
										
										
										
											2018-01-13 12:12:17 -08:00
										 |  |  |   <SettingsLayout page='settings/instances/add' label="Add an Instance"> | 
					
						
							| 
									
										
										
										
											2018-01-18 23:37:43 -08:00
										 |  |  |     <h1 id="add-an-instance-h1">Add an Instance</h1> | 
					
						
							| 
									
										
										
										
											2018-01-07 16:00:42 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-14 11:22:57 -08:00
										 |  |  |     <LoadingMask show="{{loading}}"/> | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-13 11:11:35 -08:00
										 |  |  |     {{#if $isUserLoggedIn}} | 
					
						
							| 
									
										
										
										
											2018-01-13 12:12:17 -08:00
										 |  |  |     <p>Connect to an instance to log in.</p> | 
					
						
							| 
									
										
										
										
											2018-01-13 11:11:35 -08:00
										 |  |  |     {{else}} | 
					
						
							| 
									
										
										
										
											2018-01-13 12:12:17 -08:00
										 |  |  |     <p>Log in to an instance to start using Pinafore.</p> | 
					
						
							| 
									
										
										
										
											2018-01-13 11:11:35 -08:00
										 |  |  |     {{/if}} | 
					
						
							| 
									
										
										
										
											2018-01-07 16:00:42 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-18 23:37:43 -08:00
										 |  |  |     <form class="add-new-instance" on:submit='onSubmit(event)' aria-labelledby="add-an-instance-h1"> | 
					
						
							| 
									
										
										
										
											2018-01-21 01:19:28 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-14 17:52:16 -08:00
										 |  |  |       <label for="instanceInput">Instance:</label> | 
					
						
							| 
									
										
										
										
											2018-01-21 01:19:28 -08:00
										 |  |  |       <input class="new-instance-input" type="text" id="instanceInput" | 
					
						
							|  |  |  |              bind:value='$instanceNameInSearch' placeholder='' required | 
					
						
							|  |  |  |       > | 
					
						
							| 
									
										
										
										
											2018-01-14 11:22:57 -08:00
										 |  |  |       <button class="primary" type="submit" id="submitButton" disabled="{{!$instanceNameInSearch}}">Add instance</button> | 
					
						
							| 
									
										
										
										
											2018-01-21 01:19:28 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |       {{#if error}} | 
					
						
							|  |  |  |       <div class="form-error" role="alert"> | 
					
						
							|  |  |  |         Error: {{error}} | 
					
						
							|  |  |  |       </div> | 
					
						
							|  |  |  |       {{/if}} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-13 12:12:17 -08:00
										 |  |  |     </form> | 
					
						
							| 
									
										
										
										
											2018-01-07 16:00:42 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-13 11:11:35 -08:00
										 |  |  |     {{#if !$isUserLoggedIn}} | 
					
						
							| 
									
										
										
										
											2018-01-13 14:19:51 -08:00
										 |  |  |     <p>Don't have an instance? <a rel="noopener" target="_blank" href="https://joinmastodon.org">Join Mastodon!</a></p> | 
					
						
							| 
									
										
										
										
											2018-01-13 11:11:35 -08:00
										 |  |  |     {{/if}} | 
					
						
							| 
									
										
										
										
											2018-01-08 17:44:29 -08:00
										 |  |  |   </SettingsLayout> | 
					
						
							| 
									
										
										
										
											2018-01-07 16:00:42 -08:00
										 |  |  | </Layout> | 
					
						
							|  |  |  | <style> | 
					
						
							| 
									
										
										
										
											2018-01-21 01:19:28 -08:00
										 |  |  |   .form-error { | 
					
						
							|  |  |  |     border: 2px solid red; | 
					
						
							|  |  |  |     border-radius: 2px; | 
					
						
							|  |  |  |     padding: 10px; | 
					
						
							|  |  |  |     font-size: 1.3em; | 
					
						
							|  |  |  |     margin: 5px; | 
					
						
							|  |  |  |     background-color: var(--main-bg); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-01-20 12:35:38 -08:00
										 |  |  |   input.new-instance-input { | 
					
						
							| 
									
										
										
										
											2018-01-15 21:58:31 -08:00
										 |  |  |     min-width: 50%; | 
					
						
							|  |  |  |     max-width: 100%; | 
					
						
							| 
									
										
										
										
											2018-01-07 16:00:42 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-13 11:11:35 -08:00
										 |  |  |   form.add-new-instance { | 
					
						
							| 
									
										
										
										
											2018-01-12 09:01:46 -08:00
										 |  |  |     background: var(--form-bg); | 
					
						
							| 
									
										
										
										
											2018-01-07 16:00:42 -08:00
										 |  |  |     padding: 5px 10px 15px; | 
					
						
							| 
									
										
										
										
											2018-01-12 09:01:46 -08:00
										 |  |  |     margin: 20px auto; | 
					
						
							|  |  |  |     border: 1px solid var(--form-border); | 
					
						
							| 
									
										
										
										
											2018-01-14 12:00:22 -08:00
										 |  |  |     border-radius: 4px; | 
					
						
							| 
									
										
										
										
											2018-01-07 16:00:42 -08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-13 11:11:35 -08:00
										 |  |  |   form.add-new-instance label, form.add-new-instance input, form.add-new-instance button { | 
					
						
							| 
									
										
										
										
											2018-01-07 16:00:42 -08:00
										 |  |  |     display: block; | 
					
						
							|  |  |  |     margin: 20px 5px; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-01-20 15:37:40 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @media (max-width: 767px) { | 
					
						
							|  |  |  |     input.new-instance-input { | 
					
						
							|  |  |  |       max-width: 80%; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-01-07 16:00:42 -08:00
										 |  |  | </style> | 
					
						
							|  |  |  | <script> | 
					
						
							| 
									
										
										
										
											2018-01-13 12:12:17 -08:00
										 |  |  |   import Layout from '../../_components/Layout.html'; | 
					
						
							|  |  |  |   import SettingsLayout from '../_components/SettingsLayout.html' | 
					
						
							| 
									
										
										
										
											2018-01-13 14:19:51 -08:00
										 |  |  |   import { registerApplication, generateAuthLink, getAccessTokenFromAuthCode } from '../../_utils/mastodon/oauth' | 
					
						
							| 
									
										
										
										
											2018-01-21 17:18:56 -08:00
										 |  |  |   import { getVerifyCredentials } from '../../_utils/mastodon/user' | 
					
						
							| 
									
										
										
										
											2018-01-25 08:44:10 -08:00
										 |  |  |   import { getInstanceInfo } from '../../_utils/mastodon/instance' | 
					
						
							| 
									
										
										
										
											2018-01-13 12:12:17 -08:00
										 |  |  |   import { store } from '../../_utils/store' | 
					
						
							| 
									
										
										
										
											2018-01-07 22:13:15 -08:00
										 |  |  |   import { goto } from 'sapper/runtime.js' | 
					
						
							| 
									
										
										
										
											2018-01-13 21:03:03 -08:00
										 |  |  |   import { switchToTheme } from '../../_utils/themeEngine' | 
					
						
							| 
									
										
										
										
											2018-01-14 11:22:57 -08:00
										 |  |  |   import LoadingMask from '../../_components/LoadingMask' | 
					
						
							| 
									
										
										
										
											2018-01-21 17:18:56 -08:00
										 |  |  |   import { database } from '../../_utils/database/database' | 
					
						
							| 
									
										
										
										
											2018-01-07 16:00:42 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-13 19:23:05 -08:00
										 |  |  |   const REDIRECT_URI = (typeof location !== 'undefined' ? | 
					
						
							|  |  |  |       location.origin : 'https://pinafore.social') + '/settings/instances/add' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-07 16:00:42 -08:00
										 |  |  |   export default { | 
					
						
							| 
									
										
										
										
											2018-01-21 01:19:28 -08:00
										 |  |  |     async oncreate () { | 
					
						
							|  |  |  |       let params = new URLSearchParams(location.search) | 
					
						
							|  |  |  |       if (params.has('code')) { | 
					
						
							|  |  |  |         this.onReceivedOauthCode(params.get('code')) | 
					
						
							| 
									
										
										
										
											2018-01-07 22:00:16 -08:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-01-21 01:19:28 -08:00
										 |  |  |       this.store.observe('instanceNameInSearch', () => { | 
					
						
							|  |  |  |         this.set({error: false}) | 
					
						
							|  |  |  |       }) | 
					
						
							| 
									
										
										
										
											2018-01-07 22:00:16 -08:00
										 |  |  |     }, | 
					
						
							| 
									
										
										
										
											2018-01-07 16:00:42 -08:00
										 |  |  |     components: { | 
					
						
							| 
									
										
										
										
											2018-01-08 17:44:29 -08:00
										 |  |  |       Layout, | 
					
						
							| 
									
										
										
										
											2018-01-14 11:22:57 -08:00
										 |  |  |       SettingsLayout, | 
					
						
							|  |  |  |       LoadingMask | 
					
						
							| 
									
										
										
										
											2018-01-07 16:00:42 -08:00
										 |  |  |     }, | 
					
						
							| 
									
										
										
										
											2018-01-08 10:13:42 -08:00
										 |  |  |     store: () => store, | 
					
						
							| 
									
										
										
										
											2018-01-07 16:00:42 -08:00
										 |  |  |     methods: { | 
					
						
							| 
									
										
										
										
											2018-01-13 18:59:49 -08:00
										 |  |  |       onSubmit: async function(event) { | 
					
						
							|  |  |  |         event.preventDefault() | 
					
						
							| 
									
										
										
										
											2018-01-14 11:22:57 -08:00
										 |  |  |         this.set({loading: true}) | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |           await this.redirectToOauth() | 
					
						
							|  |  |  |         } catch (err) { | 
					
						
							| 
									
										
										
										
											2018-01-27 08:22:23 -08:00
										 |  |  |           console.error(err) | 
					
						
							| 
									
										
										
										
											2018-01-21 01:19:28 -08:00
										 |  |  |           let error = `${err.message || err.name}. ` + | 
					
						
							| 
									
										
										
										
											2018-01-14 23:36:44 -08:00
										 |  |  |             (navigator.onLine ? | 
					
						
							|  |  |  |               `Is this a valid Mastodon instance?` : | 
					
						
							| 
									
										
										
										
											2018-01-21 01:19:28 -08:00
										 |  |  |               `Are you offline?`) | 
					
						
							|  |  |  |           this.set({error: error}) | 
					
						
							| 
									
										
										
										
											2018-01-14 11:22:57 -08:00
										 |  |  |         } finally { | 
					
						
							|  |  |  |           this.set({loading: false}) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       redirectToOauth: async function() { | 
					
						
							| 
									
										
										
										
											2018-01-13 18:59:49 -08:00
										 |  |  |         let instanceName = this.store.get('instanceNameInSearch') | 
					
						
							| 
									
										
										
										
											2018-01-14 11:22:57 -08:00
										 |  |  |         let loggedInInstances = this.store.get('loggedInInstances') | 
					
						
							| 
									
										
										
										
											2018-01-13 18:59:49 -08:00
										 |  |  |         instanceName = instanceName.replace(/^https?:\/\//, '').replace('/$', '') | 
					
						
							| 
									
										
										
										
											2018-01-14 11:22:57 -08:00
										 |  |  |         if (Object.keys(loggedInInstances).includes(instanceName)) { | 
					
						
							| 
									
										
										
										
											2018-01-21 01:19:28 -08:00
										 |  |  |           this.set({error: `You've already logged in to ${instanceName}`}) | 
					
						
							| 
									
										
										
										
											2018-01-14 11:22:57 -08:00
										 |  |  |           return | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-01-25 08:44:10 -08:00
										 |  |  |         let registrationPromise = registerApplication(instanceName, REDIRECT_URI) | 
					
						
							|  |  |  |         let instanceInfo = await getInstanceInfo(instanceName) | 
					
						
							|  |  |  |         await database.setInstanceInfo(instanceName, instanceInfo) // cache for later | 
					
						
							|  |  |  |         let instanceData = await registrationPromise | 
					
						
							| 
									
										
										
										
											2018-01-13 18:59:49 -08:00
										 |  |  |         this.store.set({ | 
					
						
							|  |  |  |           currentRegisteredInstanceName: instanceName, | 
					
						
							|  |  |  |           currentRegisteredInstance: instanceData | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         this.store.save() | 
					
						
							| 
									
										
										
										
											2018-01-13 19:23:05 -08:00
										 |  |  |         let oauthUrl = generateAuthLink( | 
					
						
							|  |  |  |           instanceName, | 
					
						
							|  |  |  |           instanceData.client_id, | 
					
						
							|  |  |  |           REDIRECT_URI | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2018-01-13 18:59:49 -08:00
										 |  |  |         document.location.href = oauthUrl | 
					
						
							|  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2018-01-08 10:13:42 -08:00
										 |  |  |       onReceivedOauthCode: async function(code) { | 
					
						
							| 
									
										
										
										
											2018-01-14 11:22:57 -08:00
										 |  |  |         try { | 
					
						
							|  |  |  |           this.set({loading: true}) | 
					
						
							|  |  |  |           await this.registerNewInstance(code) | 
					
						
							|  |  |  |         } catch (err) { | 
					
						
							| 
									
										
										
										
											2018-01-21 01:19:28 -08:00
										 |  |  |           this.set({error: `${err.message || err.name}. Failed to connect to instance.`}) | 
					
						
							| 
									
										
										
										
											2018-01-14 11:22:57 -08:00
										 |  |  |         } finally { | 
					
						
							|  |  |  |           this.set({loading: false}) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       registerNewInstance: async function (code) { | 
					
						
							| 
									
										
										
										
											2018-01-12 22:24:54 -08:00
										 |  |  |         let currentRegisteredInstanceName = this.store.get('currentRegisteredInstanceName') | 
					
						
							|  |  |  |         let currentRegisteredInstance = this.store.get('currentRegisteredInstance') | 
					
						
							| 
									
										
										
										
											2018-01-13 14:19:51 -08:00
										 |  |  |         let instanceData = await getAccessTokenFromAuthCode( | 
					
						
							| 
									
										
										
										
											2018-01-12 22:24:54 -08:00
										 |  |  |           currentRegisteredInstanceName, | 
					
						
							|  |  |  |           currentRegisteredInstance.client_id, | 
					
						
							|  |  |  |           currentRegisteredInstance.client_secret, | 
					
						
							| 
									
										
										
										
											2018-01-13 19:23:05 -08:00
										 |  |  |           code, | 
					
						
							|  |  |  |           REDIRECT_URI | 
					
						
							| 
									
										
										
										
											2018-01-13 14:19:51 -08:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2018-01-12 22:24:54 -08:00
										 |  |  |         let loggedInInstances = this.store.get('loggedInInstances') | 
					
						
							|  |  |  |         let loggedInInstancesInOrder = this.store.get('loggedInInstancesInOrder') | 
					
						
							| 
									
										
										
										
											2018-01-13 20:07:11 -08:00
										 |  |  |         let instanceThemes = this.store.get('instanceThemes') | 
					
						
							|  |  |  |         instanceThemes[currentRegisteredInstanceName] = 'default' | 
					
						
							| 
									
										
										
										
											2018-01-12 22:24:54 -08:00
										 |  |  |         loggedInInstances[currentRegisteredInstanceName] = instanceData | 
					
						
							|  |  |  |         if (!loggedInInstancesInOrder.includes(currentRegisteredInstanceName)) { | 
					
						
							|  |  |  |           loggedInInstancesInOrder.push(currentRegisteredInstanceName) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-01-08 10:13:42 -08:00
										 |  |  |         this.store.set({ | 
					
						
							| 
									
										
										
										
											2018-01-12 22:26:20 -08:00
										 |  |  |           instanceNameInSearch: '', | 
					
						
							| 
									
										
										
										
											2018-01-13 18:59:49 -08:00
										 |  |  |           currentRegisteredInstanceName: null, | 
					
						
							|  |  |  |           currentRegisteredInstance: null, | 
					
						
							| 
									
										
										
										
											2018-01-12 22:26:20 -08:00
										 |  |  |           loggedInInstances: loggedInInstances, | 
					
						
							|  |  |  |           currentInstance: currentRegisteredInstanceName, | 
					
						
							| 
									
										
										
										
											2018-01-13 20:07:11 -08:00
										 |  |  |           loggedInInstancesInOrder: loggedInInstancesInOrder, | 
					
						
							|  |  |  |           instanceThemes: instanceThemes | 
					
						
							| 
									
										
										
										
											2018-01-08 10:13:42 -08:00
										 |  |  |         }) | 
					
						
							|  |  |  |         this.store.save() | 
					
						
							| 
									
										
										
										
											2018-01-13 21:03:03 -08:00
										 |  |  |         switchToTheme('default') | 
					
						
							| 
									
										
										
										
											2018-01-25 08:44:10 -08:00
										 |  |  |         // fire off request for account so it's cached | 
					
						
							| 
									
										
										
										
											2018-01-21 17:18:56 -08:00
										 |  |  |         getVerifyCredentials(currentRegisteredInstanceName, instanceData.access_token).then(verifyCredentials => { | 
					
						
							|  |  |  |           database.setInstanceVerifyCredentials(currentRegisteredInstanceName, verifyCredentials) | 
					
						
							|  |  |  |         }) | 
					
						
							| 
									
										
										
										
											2018-01-08 10:13:42 -08:00
										 |  |  |         goto('/') | 
					
						
							| 
									
										
										
										
											2018-01-14 11:22:57 -08:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-01-07 16:00:42 -08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | </script> |