add error handling, toasts, and loading spinner
This commit is contained in:
parent
9d9e6716d5
commit
ee1251467a
41
package-lock.json
generated
41
package-lock.json
generated
|
@ -1321,6 +1321,11 @@
|
||||||
"stream-shift": "1.0.0"
|
"stream-shift": "1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"eases-jsnext": {
|
||||||
|
"version": "1.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/eases-jsnext/-/eases-jsnext-1.0.10.tgz",
|
||||||
|
"integrity": "sha512-1bO1+FIuqtOZpcyoIJuTnw8PU9X+RHHA248mZ1m+CPiiKFGCiNLWecITlhO4DXe7whZmBoJyfKwUoMW0KK5mNw=="
|
||||||
|
},
|
||||||
"ecc-jsbn": {
|
"ecc-jsbn": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
|
||||||
|
@ -6779,6 +6784,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/svelte/-/svelte-1.51.0.tgz",
|
"resolved": "https://registry.npmjs.org/svelte/-/svelte-1.51.0.tgz",
|
||||||
"integrity": "sha512-lqa9eAZ4ZQLMWsoyynAogUtib7HhHnrJJaS93uRgZU5cfXquBVR+FkKVK41LdlwffmOfOjbUin6pT8e/LZUwjA=="
|
"integrity": "sha512-lqa9eAZ4ZQLMWsoyynAogUtib7HhHnrJJaS93uRgZU5cfXquBVR+FkKVK41LdlwffmOfOjbUin6pT8e/LZUwjA=="
|
||||||
},
|
},
|
||||||
|
"svelte-extras": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/svelte-extras/-/svelte-extras-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-0yzXHJdnaX3+KiLrDu9Hl6V7+idfKrUkYqhpbdnxCEJos2FSxtpos6cjAt+A2vVrdcNjFqtXYs6xS+rFWeg1yA=="
|
||||||
|
},
|
||||||
"svelte-loader": {
|
"svelte-loader": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/svelte-loader/-/svelte-loader-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/svelte-loader/-/svelte-loader-2.3.3.tgz",
|
||||||
|
@ -6788,6 +6798,37 @@
|
||||||
"tmp": "0.0.31"
|
"tmp": "0.0.31"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"svelte-transitions": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/svelte-transitions/-/svelte-transitions-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-AaLpVPOnTXH8dtOn3Sn90VVRCBA=",
|
||||||
|
"requires": {
|
||||||
|
"svelte-transitions-fade": "1.0.0",
|
||||||
|
"svelte-transitions-fly": "1.0.2",
|
||||||
|
"svelte-transitions-slide": "1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"svelte-transitions-fade": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/svelte-transitions-fade/-/svelte-transitions-fade-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-2+FSDfH1tTcL1hr+/Gfy0v/2MwM="
|
||||||
|
},
|
||||||
|
"svelte-transitions-fly": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/svelte-transitions-fly/-/svelte-transitions-fly-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-CP02aUG0uSmpL5Y1uQJDpYbCuNc=",
|
||||||
|
"requires": {
|
||||||
|
"eases-jsnext": "1.0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"svelte-transitions-slide": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/svelte-transitions-slide/-/svelte-transitions-slide-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-FQ3Zy455+p4vJQ4ZjH1plgvyGZQ=",
|
||||||
|
"requires": {
|
||||||
|
"eases-jsnext": "1.0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"svgo": {
|
"svgo": {
|
||||||
"version": "0.7.2",
|
"version": "0.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz",
|
||||||
|
|
|
@ -34,7 +34,9 @@
|
||||||
"serve-static": "^1.13.1",
|
"serve-static": "^1.13.1",
|
||||||
"style-loader": "^0.19.1",
|
"style-loader": "^0.19.1",
|
||||||
"svelte": "^1.50.0",
|
"svelte": "^1.50.0",
|
||||||
|
"svelte-extras": "^1.6.0",
|
||||||
"svelte-loader": "^2.3.3",
|
"svelte-loader": "^2.3.3",
|
||||||
|
"svelte-transitions": "^1.1.1",
|
||||||
"uglifyjs-webpack-plugin": "^1.1.5",
|
"uglifyjs-webpack-plugin": "^1.1.5",
|
||||||
"url-search-params": "^0.10.0",
|
"url-search-params": "^0.10.0",
|
||||||
"webpack": "^3.10.0",
|
"webpack": "^3.10.0",
|
||||||
|
|
61
routes/_components/LoadingMask.html
Normal file
61
routes/_components/LoadingMask.html
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
<div class="loading-container">
|
||||||
|
{{#if show}}
|
||||||
|
<div transition:fade class="loading-mask">
|
||||||
|
<svg>
|
||||||
|
<use xlink:href="#fa-spinner" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
.loading-container {
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
position: fixed;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
.loading-mask {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: var(--mask-bg);
|
||||||
|
opacity: 0.6;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
svg {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
fill: var(--mask-svg-fill);
|
||||||
|
animation: spin 2s infinite linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
25% {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
75% {
|
||||||
|
transform: rotate(270deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
import { fade } from 'svelte-transitions'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
transitions: { fade }
|
||||||
|
}
|
||||||
|
</script>
|
96
routes/_components/Toast.html
Normal file
96
routes/_components/Toast.html
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
<div class="toast-modal {{shown ? 'shown' : ''}}">
|
||||||
|
<div class="toast-container">
|
||||||
|
{{text}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<style>
|
||||||
|
.toast-modal {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 40px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 333ms linear;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-container {
|
||||||
|
max-width: 600px;
|
||||||
|
max-height: 20vh;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
border: 2px solid var(--toast-border);
|
||||||
|
background: var(--toast-bg);
|
||||||
|
border-radius: 5px;
|
||||||
|
margin: 0 40px;
|
||||||
|
padding: 20px;
|
||||||
|
font-size: 1.3em;
|
||||||
|
color: var(--toast-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-modal.shown {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.toast-container {
|
||||||
|
max-width: 80vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
import { splice, push } from 'svelte-extras'
|
||||||
|
|
||||||
|
const TIME_TO_SHOW_TOAST = 5000
|
||||||
|
const DELAY_BETWEEN_TOASTS = 1000
|
||||||
|
|
||||||
|
export default {
|
||||||
|
oncreate () {
|
||||||
|
this._queue = Promise.resolve()
|
||||||
|
this.observe('messages', (messages) => {
|
||||||
|
console.log('messages', messages)
|
||||||
|
if (messages.length) {
|
||||||
|
this.onNewToast(messages[0])
|
||||||
|
this.splice('messages', 0, 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
ondestroy () {
|
||||||
|
},
|
||||||
|
data: () => ({
|
||||||
|
text: '',
|
||||||
|
shown: false,
|
||||||
|
messages: []
|
||||||
|
}),
|
||||||
|
methods: {
|
||||||
|
push,
|
||||||
|
splice,
|
||||||
|
say(text) {
|
||||||
|
this.push('messages', text)
|
||||||
|
},
|
||||||
|
onNewToast(text) {
|
||||||
|
this._queue = this._queue.then(() => {
|
||||||
|
this.set({
|
||||||
|
'text': text,
|
||||||
|
shown: true
|
||||||
|
})
|
||||||
|
return new Promise(resolve => {
|
||||||
|
setTimeout(resolve, TIME_TO_SHOW_TOAST)
|
||||||
|
})
|
||||||
|
}).then(() => {
|
||||||
|
this.set({
|
||||||
|
shown: false
|
||||||
|
})
|
||||||
|
return new Promise(resolve => {
|
||||||
|
setTimeout(resolve, DELAY_BETWEEN_TOASTS)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
18
routes/_utils/toast.js
Normal file
18
routes/_utils/toast.js
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import Toast from '../_components/Toast.html'
|
||||||
|
|
||||||
|
let toast
|
||||||
|
|
||||||
|
if (process.browser) {
|
||||||
|
toast = new Toast({
|
||||||
|
target: document.querySelector('#toast')
|
||||||
|
})
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
window.toast = toast // for debugging
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toast = {
|
||||||
|
say: () => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { toast }
|
|
@ -4,8 +4,15 @@
|
||||||
<style>
|
<style>
|
||||||
ul {
|
ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
width: 80%;
|
width: 100%;
|
||||||
border: 1px solid var(--settings-list-item-border);
|
border: 1px solid var(--settings-list-item-border);
|
||||||
margin: 20px auto;
|
margin: 20px auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
ul {
|
||||||
|
max-width: 80%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
|
@ -13,16 +13,15 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
ul {
|
ul {
|
||||||
margin: 0;
|
margin: 5px 10px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
margin: 10px 0;
|
margin: 5px 0;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
li::after {
|
li::after {
|
||||||
|
|
|
@ -68,7 +68,7 @@
|
||||||
justify-content: right;
|
justify-content: right;
|
||||||
}
|
}
|
||||||
.instance-actions button {
|
.instance-actions button {
|
||||||
margin: 0 20px;
|
margin: 0 5px;
|
||||||
flex-basis: 100%;
|
flex-basis: 100%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
<SettingsLayout page='settings/instances/add' label="Add an Instance">
|
<SettingsLayout page='settings/instances/add' label="Add an Instance">
|
||||||
<h1>Add an Instance</h1>
|
<h1>Add an Instance</h1>
|
||||||
|
|
||||||
|
<LoadingMask show="{{loading}}"/>
|
||||||
|
|
||||||
{{#if $isUserLoggedIn}}
|
{{#if $isUserLoggedIn}}
|
||||||
<p>Connect to an instance to log in.</p>
|
<p>Connect to an instance to log in.</p>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
@ -15,7 +17,7 @@
|
||||||
<form class="add-new-instance" on:submit='onSubmit(event)'>
|
<form class="add-new-instance" on:submit='onSubmit(event)'>
|
||||||
<label for="instanceInput">Instance name:</label>
|
<label for="instanceInput">Instance name:</label>
|
||||||
<input type="text" id="instanceInput" bind:value='$instanceNameInSearch' placeholder=''>
|
<input type="text" id="instanceInput" bind:value='$instanceNameInSearch' placeholder=''>
|
||||||
<button class="primary" type="submit" id="submitButton">Add instance</button>
|
<button class="primary" type="submit" id="submitButton" disabled="{{!$instanceNameInSearch}}">Add instance</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{{#if !$isUserLoggedIn}}
|
{{#if !$isUserLoggedIn}}
|
||||||
|
@ -53,6 +55,9 @@
|
||||||
import { store } from '../../_utils/store'
|
import { store } from '../../_utils/store'
|
||||||
import { goto } from 'sapper/runtime.js'
|
import { goto } from 'sapper/runtime.js'
|
||||||
import { switchToTheme } from '../../_utils/themeEngine'
|
import { switchToTheme } from '../../_utils/themeEngine'
|
||||||
|
import { toast } from '../../_utils/toast'
|
||||||
|
import LoadingMask from '../../_components/LoadingMask'
|
||||||
|
import { fade } from 'svelte-transitions'
|
||||||
|
|
||||||
const REDIRECT_URI = (typeof location !== 'undefined' ?
|
const REDIRECT_URI = (typeof location !== 'undefined' ?
|
||||||
location.origin : 'https://pinafore.social') + '/settings/instances/add'
|
location.origin : 'https://pinafore.social') + '/settings/instances/add'
|
||||||
|
@ -70,17 +75,37 @@
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
Layout,
|
Layout,
|
||||||
SettingsLayout
|
SettingsLayout,
|
||||||
|
LoadingMask
|
||||||
},
|
},
|
||||||
store: () => store,
|
store: () => store,
|
||||||
|
transitions: {
|
||||||
|
fade
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onSubmit: async function(event) {
|
onSubmit: async function(event) {
|
||||||
event.preventDefault()
|
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}. Is this a valid Mastodon instance?`)
|
||||||
|
} finally {
|
||||||
|
this.set({loading: false})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
redirectToOauth: async function() {
|
||||||
let instanceName = this.store.get('instanceNameInSearch')
|
let instanceName = this.store.get('instanceNameInSearch')
|
||||||
|
let loggedInInstances = this.store.get('loggedInInstances')
|
||||||
instanceName = instanceName.replace(/^https?:\/\//, '').replace('/$', '')
|
instanceName = instanceName.replace(/^https?:\/\//, '').replace('/$', '')
|
||||||
// TODO: show toast error if you're already logged into this instance
|
if (Object.keys(loggedInInstances).includes(instanceName)) {
|
||||||
|
toast.say(`You've already logged in to ${instanceName}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
let instanceData = await registerApplication(instanceName, REDIRECT_URI)
|
let instanceData = await registerApplication(instanceName, REDIRECT_URI)
|
||||||
// TODO: handle error
|
|
||||||
this.store.set({
|
this.store.set({
|
||||||
currentRegisteredInstanceName: instanceName,
|
currentRegisteredInstanceName: instanceName,
|
||||||
currentRegisteredInstance: instanceData
|
currentRegisteredInstance: instanceData
|
||||||
|
@ -94,6 +119,16 @@
|
||||||
document.location.href = oauthUrl
|
document.location.href = oauthUrl
|
||||||
},
|
},
|
||||||
onReceivedOauthCode: async function(code) {
|
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 currentRegisteredInstanceName = this.store.get('currentRegisteredInstanceName')
|
||||||
let currentRegisteredInstance = this.store.get('currentRegisteredInstance')
|
let currentRegisteredInstance = this.store.get('currentRegisteredInstance')
|
||||||
let instanceData = await getAccessTokenFromAuthCode(
|
let instanceData = await getAccessTokenFromAuthCode(
|
||||||
|
@ -103,7 +138,6 @@
|
||||||
code,
|
code,
|
||||||
REDIRECT_URI
|
REDIRECT_URI
|
||||||
)
|
)
|
||||||
// TODO: handle error
|
|
||||||
let loggedInInstances = this.store.get('loggedInInstances')
|
let loggedInInstances = this.store.get('loggedInInstances')
|
||||||
let loggedInInstancesInOrder = this.store.get('loggedInInstancesInOrder')
|
let loggedInInstancesInOrder = this.store.get('loggedInInstancesInOrder')
|
||||||
let instanceThemes = this.store.get('instanceThemes')
|
let instanceThemes = this.store.get('instanceThemes')
|
||||||
|
@ -124,7 +158,7 @@
|
||||||
this.store.save()
|
this.store.save()
|
||||||
switchToTheme('default')
|
switchToTheme('default')
|
||||||
goto('/')
|
goto('/')
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
|
@ -24,7 +24,6 @@ main {
|
||||||
background: var(--main-bg);
|
background: var(--main-bg);
|
||||||
border: 1px solid var(--main-border);
|
border: 1px solid var(--main-border);
|
||||||
border-radius: 1px;
|
border-radius: 1px;
|
||||||
min-height: 50vh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
|
|
@ -47,4 +47,11 @@
|
||||||
--settings-list-item-border: $border-color;
|
--settings-list-item-border: $border-color;
|
||||||
--settings-list-item-bg-active: darken($main-bg-color, 10%);
|
--settings-list-item-bg-active: darken($main-bg-color, 10%);
|
||||||
--settings-list-item-bg-hover: darken($main-bg-color, 2%);
|
--settings-list-item-bg-hover: darken($main-bg-color, 2%);
|
||||||
|
|
||||||
|
--toast-bg: $toast-bg;
|
||||||
|
--toast-border: $toast-border;
|
||||||
|
--toast-text: $secondary-text-color;
|
||||||
|
|
||||||
|
--mask-bg: $toast-bg;
|
||||||
|
--mask-svg-fill: $secondary-text-color;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ $main-text-color: #333;
|
||||||
$border-color: #dadada;
|
$border-color: #dadada;
|
||||||
$main-bg-color: white;
|
$main-bg-color: white;
|
||||||
$secondary-text-color: white;
|
$secondary-text-color: white;
|
||||||
|
$toast-border: #fafafa;
|
||||||
|
$toast-bg: #333;
|
||||||
|
|
||||||
@import "_base.scss";
|
@import "_base.scss";
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ $main-text-color: #333;
|
||||||
$border-color: #dadada;
|
$border-color: #dadada;
|
||||||
$main-bg-color: white;
|
$main-bg-color: white;
|
||||||
$secondary-text-color: white;
|
$secondary-text-color: white;
|
||||||
|
$toast-border: #fafafa;
|
||||||
|
$toast-bg: #333;
|
||||||
|
|
||||||
@import "_base.scss";
|
@import "_base.scss";
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ $main-text-color: #333;
|
||||||
$border-color: #dadada;
|
$border-color: #dadada;
|
||||||
$main-bg-color: white;
|
$main-bg-color: white;
|
||||||
$secondary-text-color: white;
|
$secondary-text-color: white;
|
||||||
|
$toast-border: #fafafa;
|
||||||
|
$toast-bg: #333;
|
||||||
|
|
||||||
@import "_base.scss";
|
@import "_base.scss";
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ $main-text-color: #333;
|
||||||
$border-color: #dadada;
|
$border-color: #dadada;
|
||||||
$main-bg-color: white;
|
$main-bg-color: white;
|
||||||
$secondary-text-color: white;
|
$secondary-text-color: white;
|
||||||
|
$toast-border: #fafafa;
|
||||||
|
$toast-bg: #333;
|
||||||
|
|
||||||
@import "_base.scss";
|
@import "_base.scss";
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ $main-text-color: #333;
|
||||||
$border-color: #dadada;
|
$border-color: #dadada;
|
||||||
$main-bg-color: white;
|
$main-bg-color: white;
|
||||||
$secondary-text-color: white;
|
$secondary-text-color: white;
|
||||||
|
$toast-border: #fafafa;
|
||||||
|
$toast-bg: #333;
|
||||||
|
|
||||||
@import "_base.scss";
|
@import "_base.scss";
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ $main-text-color: #333;
|
||||||
$border-color: #dadada;
|
$border-color: #dadada;
|
||||||
$main-bg-color: white;
|
$main-bg-color: white;
|
||||||
$secondary-text-color: white;
|
$secondary-text-color: white;
|
||||||
|
$toast-border: #fafafa;
|
||||||
|
$toast-bg: #333;
|
||||||
|
|
||||||
@import "_base.scss";
|
@import "_base.scss";
|
||||||
|
|
||||||
|
|
|
@ -94,11 +94,20 @@
|
||||||
<path d="M576 736v192q0 40-28 68t-68 28H288q-40 0-68-28t-28-68V736q0-40 28-68t68-28h192q40 0 68 28t28 68zm512 0v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68V736q0-40 28-68t68-28h192q40 0 68 28t28 68zm512 0v192q0 40-28 68t-68 28h-192q-40 0-68-28t-28-68V736q0-40 28-68t68-28h192q40 0 68 28t28 68z"/>
|
<path d="M576 736v192q0 40-28 68t-68 28H288q-40 0-68-28t-28-68V736q0-40 28-68t68-28h192q40 0 68 28t28 68zm512 0v192q0 40-28 68t-68 28H800q-40 0-68-28t-28-68V736q0-40 28-68t68-28h192q40 0 68 28t28 68zm512 0v192q0 40-28 68t-68 28h-192q-40 0-68-28t-28-68V736q0-40 28-68t68-28h192q40 0 68 28t28 68z"/>
|
||||||
</symbol>
|
</symbol>
|
||||||
|
|
||||||
|
<symbol id="fa-spinner" viewBox="0 0 1792 1792">
|
||||||
|
<title>Spinner</title>
|
||||||
|
<path d="M526 1394q0 53-37.5 90.5T398 1522q-52 0-90-38t-38-90q0-53 37.5-90.5T398 1266t90.5 37.5T526 1394zm498 206q0 53-37.5 90.5T896 1728t-90.5-37.5T768 1600t37.5-90.5T896 1472t90.5 37.5 37.5 90.5zM320 896q0 53-37.5 90.5T192 1024t-90.5-37.5T64 896t37.5-90.5T192 768t90.5 37.5T320 896zm1202 498q0 52-38 90t-90 38q-53 0-90.5-37.5T1266 1394t37.5-90.5 90.5-37.5 90.5 37.5 37.5 90.5zM558 398q0 66-47 113t-113 47-113-47-47-113 47-113 113-47 113 47 47 113zm1170 498q0 53-37.5 90.5T1600 1024t-90.5-37.5T1472 896t37.5-90.5T1600 768t90.5 37.5T1728 896zm-640-704q0 80-56 136t-136 56-136-56-56-136 56-136T896 0t136 56 56 136zm530 206q0 93-66 158.5T1394 622q-93 0-158.5-65.5T1170 398q0-92 65.5-158t158.5-66q92 0 158 66t66 158z"/>
|
||||||
|
</symbol>
|
||||||
|
|
||||||
|
|
||||||
</svg>
|
</svg>
|
||||||
<!-- The application will be rendered inside this element,
|
<!-- The application will be rendered inside this element,
|
||||||
because `templates/main.js` references it -->
|
because `templates/main.js` references it -->
|
||||||
<div id='sapper'>%sapper.html%</div>
|
<div id='sapper'>%sapper.html%</div>
|
||||||
|
|
||||||
|
<!-- Toast.html gets rendered here -->
|
||||||
|
<div id="toast"></div>
|
||||||
|
|
||||||
<!-- Sapper creates a <script> tag containing `templates/main.js`
|
<!-- Sapper creates a <script> tag containing `templates/main.js`
|
||||||
and anything else it needs to hydrate the app and
|
and anything else it needs to hydrate the app and
|
||||||
initialise the router -->
|
initialise the router -->
|
||||||
|
|
Loading…
Reference in a new issue