finish theme engine
This commit is contained in:
parent
f69797544d
commit
1f9029f457
|
@ -10,6 +10,7 @@ const pify = require('pify')
|
||||||
const writeFile = pify(fs.writeFile.bind(fs))
|
const writeFile = pify(fs.writeFile.bind(fs))
|
||||||
const readdir = pify(fs.readdir.bind(fs))
|
const readdir = pify(fs.readdir.bind(fs))
|
||||||
const render = pify(sass.render.bind(sass))
|
const render = pify(sass.render.bind(sass))
|
||||||
|
const now = require('performance-now')
|
||||||
|
|
||||||
const globalScss = path.join(__dirname, '../scss/global.scss')
|
const globalScss = path.join(__dirname, '../scss/global.scss')
|
||||||
const defaultThemeScss = path.join(__dirname, '../scss/themes/_default.scss')
|
const defaultThemeScss = path.join(__dirname, '../scss/themes/_default.scss')
|
||||||
|
@ -19,9 +20,15 @@ const themesScssDir = path.join(__dirname, '../scss/themes')
|
||||||
const assetsDir = path.join(__dirname, '../assets')
|
const assetsDir = path.join(__dirname, '../assets')
|
||||||
|
|
||||||
function doWatch() {
|
function doWatch() {
|
||||||
|
var start = now()
|
||||||
chokidar.watch(scssDir).on('change', debounce(() => {
|
chokidar.watch(scssDir).on('change', debounce(() => {
|
||||||
compileGlobalSass()
|
console.log('Recompiling SCSS...')
|
||||||
|
Promise.all([
|
||||||
|
compileGlobalSass(),
|
||||||
compileThemesSass()
|
compileThemesSass()
|
||||||
|
]).then(() => {
|
||||||
|
console.log('Recompiled SCSS in ' + (now() - start) + 'ms')
|
||||||
|
})
|
||||||
}, 500))
|
}, 500))
|
||||||
chokidar.watch()
|
chokidar.watch()
|
||||||
}
|
}
|
||||||
|
@ -41,7 +48,8 @@ async function compileThemesSass() {
|
||||||
let files = (await readdir(themesScssDir)).filter(file => !path.basename(file).startsWith('_'))
|
let files = (await readdir(themesScssDir)).filter(file => !path.basename(file).startsWith('_'))
|
||||||
await Promise.all(files.map(async file => {
|
await Promise.all(files.map(async file => {
|
||||||
let res = await render({file: path.join(themesScssDir, file)})
|
let res = await render({file: path.join(themesScssDir, file)})
|
||||||
await writeFile(path.join(assetsDir, path.basename(file).replace(/\.scss$/, '.css')), res.css, 'utf8')
|
let outputFilename = 'theme-' + path.basename(file).replace(/\.scss$/, '.css')
|
||||||
|
await writeFile(path.join(assetsDir, outputFilename), res.css, 'utf8')
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
package-lock.json
generated
10
package-lock.json
generated
|
@ -1803,6 +1803,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz",
|
||||||
"integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg="
|
"integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg="
|
||||||
},
|
},
|
||||||
|
"fg-loadcss": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fg-loadcss/-/fg-loadcss-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-gFtSJjMMt9it0OhXz4wJQT46/LFUrJ2Db6U/fLtwClBEMEEjmVPSWSYrbGCyFwy47Cd4ULOpR+HSWXVkUKciaQ=="
|
||||||
|
},
|
||||||
"filename-regex": {
|
"filename-regex": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
|
||||||
|
@ -4806,6 +4811,11 @@
|
||||||
"sha.js": "2.4.9"
|
"sha.js": "2.4.9"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"performance-now": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||||
|
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
|
||||||
|
},
|
||||||
"pify": {
|
"pify": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
"css-loader": "^0.28.7",
|
"css-loader": "^0.28.7",
|
||||||
"express": "^4.16.2",
|
"express": "^4.16.2",
|
||||||
"extract-text-webpack-plugin": "^3.0.2",
|
"extract-text-webpack-plugin": "^3.0.2",
|
||||||
|
"fg-loadcss": "^2.0.1",
|
||||||
"font-awesome-svg-png": "^1.2.2",
|
"font-awesome-svg-png": "^1.2.2",
|
||||||
"glob": "^7.1.2",
|
"glob": "^7.1.2",
|
||||||
"idb": "^2.0.4",
|
"idb": "^2.0.4",
|
||||||
|
@ -27,6 +28,7 @@
|
||||||
"node-fetch": "^1.7.3",
|
"node-fetch": "^1.7.3",
|
||||||
"node-sass": "^4.7.2",
|
"node-sass": "^4.7.2",
|
||||||
"npm-run-all": "^4.1.2",
|
"npm-run-all": "^4.1.2",
|
||||||
|
"performance-now": "^2.1.0",
|
||||||
"pify": "^3.0.0",
|
"pify": "^3.0.0",
|
||||||
"sapper": "^0.3.1",
|
"sapper": "^0.3.1",
|
||||||
"serve-static": "^1.13.1",
|
"serve-static": "^1.13.1",
|
||||||
|
|
15
routes/_utils/themeEngine.js
Normal file
15
routes/_utils/themeEngine.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { loadCSS } from 'fg-loadcss';
|
||||||
|
|
||||||
|
export function switchToTheme(themeName) {
|
||||||
|
let CL = document.body.classList
|
||||||
|
for (let i = 0; i < CL.length; i++) {
|
||||||
|
let clazz = CL.item(i)
|
||||||
|
if (clazz.startsWith('theme-')) {
|
||||||
|
CL.remove(clazz)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (themeName !== 'default') {
|
||||||
|
CL.add(`theme-${themeName}`)
|
||||||
|
loadCSS(`/theme-${themeName}.css`)
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,13 @@
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<form class="instance-actions">
|
||||||
|
<button class="primary" disabled="$currentInstance === params.instanceName">
|
||||||
|
Switch to this instance
|
||||||
|
</button>
|
||||||
|
<button>Log out</button>
|
||||||
|
</form>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</SettingsLayout>
|
</SettingsLayout>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
@ -54,6 +61,15 @@
|
||||||
.theme-chooser label {
|
.theme-chooser label {
|
||||||
margin: 2px 10px 0;
|
margin: 2px 10px 0;
|
||||||
}
|
}
|
||||||
|
.instance-actions {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: right;
|
||||||
|
}
|
||||||
|
.instance-actions button {
|
||||||
|
margin: 0 20px;
|
||||||
|
flex-basis: 100%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
import { store } from '../../_utils/store'
|
import { store } from '../../_utils/store'
|
||||||
|
@ -61,6 +77,7 @@
|
||||||
import SettingsLayout from '../_components/SettingsLayout.html'
|
import SettingsLayout from '../_components/SettingsLayout.html'
|
||||||
import { getCurrentUser } from '../../_utils/mastodon/user'
|
import { getCurrentUser } from '../../_utils/mastodon/user'
|
||||||
import { themes } from '../../_static/themes'
|
import { themes } from '../../_static/themes'
|
||||||
|
import { switchToTheme } from '../../_utils/themeEngine'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
@ -87,6 +104,9 @@
|
||||||
instanceThemes[instanceName] = newTheme
|
instanceThemes[instanceName] = newTheme
|
||||||
this.store.set({instanceThemes: instanceThemes})
|
this.store.set({instanceThemes: instanceThemes})
|
||||||
this.store.save()
|
this.store.save()
|
||||||
|
if (this.get('params').instanceName === this.store.get('currentInstance')) {
|
||||||
|
switchToTheme(newTheme)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,21 @@
|
||||||
},
|
},
|
||||||
store: () => store,
|
store: () => store,
|
||||||
methods: {
|
methods: {
|
||||||
|
onSubmit: async function(event) {
|
||||||
|
event.preventDefault()
|
||||||
|
let instanceName = this.store.get('instanceNameInSearch')
|
||||||
|
instanceName = instanceName.replace(/^https?:\/\//, '').replace('/$', '')
|
||||||
|
// TODO: show toast error if you're already logged into this instance
|
||||||
|
let instanceData = await (await registerApplication(instanceName)).json()
|
||||||
|
// TODO: handle error
|
||||||
|
this.store.set({
|
||||||
|
currentRegisteredInstanceName: instanceName,
|
||||||
|
currentRegisteredInstance: instanceData
|
||||||
|
})
|
||||||
|
this.store.save()
|
||||||
|
let oauthUrl = generateAuthLink(instanceName, instanceData.client_id)
|
||||||
|
document.location.href = oauthUrl
|
||||||
|
},
|
||||||
onReceivedOauthCode: async function(code) {
|
onReceivedOauthCode: 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')
|
||||||
|
@ -88,6 +103,8 @@
|
||||||
}
|
}
|
||||||
this.store.set({
|
this.store.set({
|
||||||
instanceNameInSearch: '',
|
instanceNameInSearch: '',
|
||||||
|
currentRegisteredInstanceName: null,
|
||||||
|
currentRegisteredInstance: null,
|
||||||
loggedInInstances: loggedInInstances,
|
loggedInInstances: loggedInInstances,
|
||||||
currentInstance: currentRegisteredInstanceName,
|
currentInstance: currentRegisteredInstanceName,
|
||||||
loggedInInstancesInOrder: loggedInInstancesInOrder
|
loggedInInstancesInOrder: loggedInInstancesInOrder
|
||||||
|
@ -95,20 +112,6 @@
|
||||||
this.store.save()
|
this.store.save()
|
||||||
goto('/')
|
goto('/')
|
||||||
},
|
},
|
||||||
onSubmit: async function(event) {
|
|
||||||
event.preventDefault()
|
|
||||||
let instanceName = this.store.get('instanceNameInSearch')
|
|
||||||
instanceName = instanceName.replace(/^https?:\/\//, '').replace('/$', '')
|
|
||||||
let instanceData = await (await registerApplication(instanceName)).json()
|
|
||||||
// TODO: handle error
|
|
||||||
this.store.set({
|
|
||||||
currentRegisteredInstanceName: instanceName,
|
|
||||||
currentRegisteredInstance: instanceData
|
|
||||||
})
|
|
||||||
this.store.save()
|
|
||||||
let oauthUrl = generateAuthLink(instanceName, instanceData.client_id)
|
|
||||||
document.location.href = oauthUrl
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
|
@ -62,29 +62,37 @@ button {
|
||||||
border: 1px solid var(--button-border);
|
border: 1px solid var(--button-border);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: var(--button-text);
|
color: var(--button-text);
|
||||||
}
|
|
||||||
|
|
||||||
button:hover {
|
&:hover {
|
||||||
background: var(--button-bg-hover);
|
background: var(--button-bg-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
button:active {
|
&:active {
|
||||||
background: var(--button-bg-active);
|
background: var(--button-bg-active);
|
||||||
}
|
}
|
||||||
|
|
||||||
button.primary {
|
&[disabled] {
|
||||||
|
opacity: 0.35;
|
||||||
|
pointer-events: none;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.primary {
|
||||||
border: 1px solid var(--button-primary-border);
|
border: 1px solid var(--button-primary-border);
|
||||||
background: var(--button-primary-bg);
|
background: var(--button-primary-bg);
|
||||||
color: var(--button-primary-text);
|
color: var(--button-primary-text);
|
||||||
}
|
|
||||||
|
|
||||||
button.primary:hover {
|
&:hover {
|
||||||
background: var(--button-primary-bg-hover);
|
background: var(--button-primary-bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: var(--button-primary-bg-active);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
button.primary:active {
|
|
||||||
background: var(--button-primary-bg-active);
|
|
||||||
}
|
|
||||||
|
|
||||||
p, label, input {
|
p, label, input {
|
||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
|
|
|
@ -7,6 +7,6 @@ $secondary-text-color: white;
|
||||||
|
|
||||||
@import "_base.scss";
|
@import "_base.scss";
|
||||||
|
|
||||||
body.theme-crimson {
|
body.theme-scarlet {
|
||||||
@include baseTheme()
|
@include baseTheme()
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,19 @@
|
||||||
%sapper.head%
|
%sapper.head%
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<script>
|
||||||
|
<!-- load theme on startup (handled outside of Sapper/Svelte) -->
|
||||||
|
if (localStorage.store_currentInstance && localStorage.store_instanceThemes) {
|
||||||
|
let theme = JSON.parse(localStorage.store_instanceThemes)[JSON.parse(localStorage.store_currentInstance)]
|
||||||
|
if (theme !== 'default') {
|
||||||
|
document.body.classList.add(`theme-${theme}`)
|
||||||
|
let link = document.createElement('link')
|
||||||
|
link.rel = 'stylesheet'
|
||||||
|
link.href = `/theme-${theme}.css`
|
||||||
|
document.head.appendChild(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" style="display:none;">
|
<svg xmlns="http://www.w3.org/2000/svg" style="display:none;">
|
||||||
|
|
||||||
<symbol id="pinafore-logo" viewBox="0 0 100 100">
|
<symbol id="pinafore-logo" viewBox="0 0 100 100">
|
||||||
|
|
Loading…
Reference in a new issue