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