168 lines
		
	
	
		
			No EOL
		
	
	
		
			5.8 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			168 lines
		
	
	
		
			No EOL
		
	
	
		
			5.8 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
<:Head>
 | 
						|
  <title>Add an Instance</title>
 | 
						|
</:Head>
 | 
						|
 | 
						|
<Layout page='settings'>
 | 
						|
  <SettingsLayout page='settings/instances/add' label="Add an Instance">
 | 
						|
    <h1 id="add-an-instance-h1">Add an Instance</h1>
 | 
						|
 | 
						|
    <LoadingMask show="{{loading}}"/>
 | 
						|
 | 
						|
    {{#if $isUserLoggedIn}}
 | 
						|
    <p>Connect to an instance to log in.</p>
 | 
						|
    {{else}}
 | 
						|
    <p>Log in to an instance to start using Pinafore.</p>
 | 
						|
    {{/if}}
 | 
						|
 | 
						|
    <form class="add-new-instance" on:submit='onSubmit(event)' aria-labelledby="add-an-instance-h1">
 | 
						|
      <label for="instanceInput">Instance:</label>
 | 
						|
      <input class="new-instance-input" type="text" id="instanceInput" bind:value='$instanceNameInSearch' placeholder=''>
 | 
						|
      <button class="primary" type="submit" id="submitButton" disabled="{{!$instanceNameInSearch}}">Add instance</button>
 | 
						|
    </form>
 | 
						|
 | 
						|
    {{#if !$isUserLoggedIn}}
 | 
						|
    <p>Don't have an instance? <a rel="noopener" target="_blank" href="https://joinmastodon.org">Join Mastodon!</a></p>
 | 
						|
    {{/if}}
 | 
						|
  </SettingsLayout>
 | 
						|
</Layout>
 | 
						|
<style>
 | 
						|
  input.new-instance-input {
 | 
						|
    min-width: 50%;
 | 
						|
    max-width: 100%;
 | 
						|
  }
 | 
						|
 | 
						|
  form.add-new-instance {
 | 
						|
    background: var(--form-bg);
 | 
						|
    padding: 5px 10px 15px;
 | 
						|
    margin: 20px auto;
 | 
						|
    border: 1px solid var(--form-border);
 | 
						|
    border-radius: 4px;
 | 
						|
  }
 | 
						|
 | 
						|
  form.add-new-instance label, form.add-new-instance input, form.add-new-instance button {
 | 
						|
    display: block;
 | 
						|
    margin: 20px 5px;
 | 
						|
  }
 | 
						|
 | 
						|
  @media (max-width: 767px) {
 | 
						|
    input.new-instance-input {
 | 
						|
      max-width: 80%;
 | 
						|
    }
 | 
						|
  }
 | 
						|
</style>
 | 
						|
<script>
 | 
						|
  import Layout from '../../_components/Layout.html';
 | 
						|
  import SettingsLayout from '../_components/SettingsLayout.html'
 | 
						|
  import { registerApplication, generateAuthLink, getAccessTokenFromAuthCode } from '../../_utils/mastodon/oauth'
 | 
						|
  import { getThisUserAccount } from '../../_utils/mastodon/user'
 | 
						|
  import { store } from '../../_utils/store'
 | 
						|
  import { goto } from 'sapper/runtime.js'
 | 
						|
  import { switchToTheme } from '../../_utils/themeEngine'
 | 
						|
  import { toast } from '../../_utils/toast'
 | 
						|
  import LoadingMask from '../../_components/LoadingMask'
 | 
						|
 | 
						|
  const REDIRECT_URI = (typeof location !== 'undefined' ?
 | 
						|
      location.origin : 'https://pinafore.social') + '/settings/instances/add'
 | 
						|
 | 
						|
  export default {
 | 
						|
    oncreate: function () {
 | 
						|
      if (process.browser) {
 | 
						|
        (async () => {
 | 
						|
          let params = new URLSearchParams(location.search)
 | 
						|
          if (params.has('code')) {
 | 
						|
            this.onReceivedOauthCode(params.get('code'))
 | 
						|
          }
 | 
						|
        })()
 | 
						|
      }
 | 
						|
    },
 | 
						|
    components: {
 | 
						|
      Layout,
 | 
						|
      SettingsLayout,
 | 
						|
      LoadingMask
 | 
						|
    },
 | 
						|
    store: () => store,
 | 
						|
    methods: {
 | 
						|
      onSubmit: async function(event) {
 | 
						|
        event.preventDefault()
 | 
						|
        this.set({loading: true})
 | 
						|
        try {
 | 
						|
          await this.redirectToOauth()
 | 
						|
        } catch (err) {
 | 
						|
          if (process.env.NODE_ENV !== 'production') {
 | 
						|
            console.error(err)
 | 
						|
          }
 | 
						|
          toast.say(`Error: ${err.message || err.name}. ` +
 | 
						|
            (navigator.onLine ?
 | 
						|
              `Is this a valid Mastodon instance?` :
 | 
						|
              `Are you offline?`))
 | 
						|
        } finally {
 | 
						|
          this.set({loading: false})
 | 
						|
        }
 | 
						|
      },
 | 
						|
      redirectToOauth: async function() {
 | 
						|
        let instanceName = this.store.get('instanceNameInSearch')
 | 
						|
        let loggedInInstances = this.store.get('loggedInInstances')
 | 
						|
        instanceName = instanceName.replace(/^https?:\/\//, '').replace('/$', '')
 | 
						|
        if (Object.keys(loggedInInstances).includes(instanceName)) {
 | 
						|
          toast.say(`You've already logged in to ${instanceName}`)
 | 
						|
          return
 | 
						|
        }
 | 
						|
        let instanceData = await registerApplication(instanceName, REDIRECT_URI)
 | 
						|
        this.store.set({
 | 
						|
          currentRegisteredInstanceName: instanceName,
 | 
						|
          currentRegisteredInstance: instanceData
 | 
						|
        })
 | 
						|
        this.store.save()
 | 
						|
        let oauthUrl = generateAuthLink(
 | 
						|
          instanceName,
 | 
						|
          instanceData.client_id,
 | 
						|
          REDIRECT_URI
 | 
						|
        )
 | 
						|
        document.location.href = oauthUrl
 | 
						|
      },
 | 
						|
      onReceivedOauthCode: async function(code) {
 | 
						|
        try {
 | 
						|
          this.set({loading: true})
 | 
						|
          await this.registerNewInstance(code)
 | 
						|
        } catch (err) {
 | 
						|
          toast.say(`Error: ${err.message || err.name}. Failed to connect to instance.`)
 | 
						|
        } finally {
 | 
						|
          this.set({loading: false})
 | 
						|
        }
 | 
						|
      },
 | 
						|
      registerNewInstance: async function (code) {
 | 
						|
        let currentRegisteredInstanceName = this.store.get('currentRegisteredInstanceName')
 | 
						|
        let currentRegisteredInstance = this.store.get('currentRegisteredInstance')
 | 
						|
        let instanceData = await getAccessTokenFromAuthCode(
 | 
						|
          currentRegisteredInstanceName,
 | 
						|
          currentRegisteredInstance.client_id,
 | 
						|
          currentRegisteredInstance.client_secret,
 | 
						|
          code,
 | 
						|
          REDIRECT_URI
 | 
						|
        )
 | 
						|
        let loggedInInstances = this.store.get('loggedInInstances')
 | 
						|
        let loggedInInstancesInOrder = this.store.get('loggedInInstancesInOrder')
 | 
						|
        let instanceThemes = this.store.get('instanceThemes')
 | 
						|
        instanceThemes[currentRegisteredInstanceName] = 'default'
 | 
						|
        loggedInInstances[currentRegisteredInstanceName] = instanceData
 | 
						|
        if (!loggedInInstancesInOrder.includes(currentRegisteredInstanceName)) {
 | 
						|
          loggedInInstancesInOrder.push(currentRegisteredInstanceName)
 | 
						|
        }
 | 
						|
        this.store.set({
 | 
						|
          instanceNameInSearch: '',
 | 
						|
          currentRegisteredInstanceName: null,
 | 
						|
          currentRegisteredInstance: null,
 | 
						|
          loggedInInstances: loggedInInstances,
 | 
						|
          currentInstance: currentRegisteredInstanceName,
 | 
						|
          loggedInInstancesInOrder: loggedInInstancesInOrder,
 | 
						|
          instanceThemes: instanceThemes
 | 
						|
        })
 | 
						|
        this.store.save()
 | 
						|
        switchToTheme('default')
 | 
						|
        // fire off request for account so it's cached in the SW
 | 
						|
        getThisUserAccount(currentRegisteredInstanceName, instanceData.access_token)
 | 
						|
        goto('/')
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
</script> |