finish theme engine

This commit is contained in:
Nolan Lawson 2018-01-13 18:59:49 -08:00
parent f69797544d
commit 1f9029f457
10 changed files with 135 additions and 56 deletions

View File

@ -10,6 +10,7 @@ const pify = require('pify')
const writeFile = pify(fs.writeFile.bind(fs))
const readdir = pify(fs.readdir.bind(fs))
const render = pify(sass.render.bind(sass))
const now = require('performance-now')
const globalScss = path.join(__dirname, '../scss/global.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')
function doWatch() {
var start = now()
chokidar.watch(scssDir).on('change', debounce(() => {
compileGlobalSass()
compileThemesSass()
console.log('Recompiling SCSS...')
Promise.all([
compileGlobalSass(),
compileThemesSass()
]).then(() => {
console.log('Recompiled SCSS in ' + (now() - start) + 'ms')
})
}, 500))
chokidar.watch()
}
@ -41,7 +48,8 @@ async function compileThemesSass() {
let files = (await readdir(themesScssDir)).filter(file => !path.basename(file).startsWith('_'))
await Promise.all(files.map(async 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
View File

@ -1803,6 +1803,11 @@
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz",
"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": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
@ -4806,6 +4811,11 @@
"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": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",

View File

@ -20,6 +20,7 @@
"css-loader": "^0.28.7",
"express": "^4.16.2",
"extract-text-webpack-plugin": "^3.0.2",
"fg-loadcss": "^2.0.1",
"font-awesome-svg-png": "^1.2.2",
"glob": "^7.1.2",
"idb": "^2.0.4",
@ -27,6 +28,7 @@
"node-fetch": "^1.7.3",
"node-sass": "^4.7.2",
"npm-run-all": "^4.1.2",
"performance-now": "^2.1.0",
"pify": "^3.0.0",
"sapper": "^0.3.1",
"serve-static": "^1.13.1",

View 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`)
}
}

View File

@ -7,23 +7,30 @@
<h1>{{params.instanceName}}</h1>
{{#if currentUser}}
<h2>Logged in as:</h2>
<div class="current-user">
<img src="{{currentUser.avatar}}" />
<a rel="noopener" target="_blank" href="{{currentUser.url}}">@{{currentUser.acct}}</a>
<span class="acct-name">{{currentUser.display_name}}</span>
</div>
<h2>Theme:</h2>
<form class="theme-chooser">
{{#each themes as theme}}
<div class="theme-group">
<input type="radio" id="choice-theme-{{theme.name}}"
value="{{theme.name}}" checked="$currentTheme === theme.name"
bind:group="selectedTheme" on:change="onThemeChange()">
<label for="choice-theme-{{theme.name}}">{{theme.label}}</label>
</div>
{{/each}}
</form>
<h2>Logged in as:</h2>
<div class="current-user">
<img src="{{currentUser.avatar}}" />
<a rel="noopener" target="_blank" href="{{currentUser.url}}">@{{currentUser.acct}}</a>
<span class="acct-name">{{currentUser.display_name}}</span>
</div>
<h2>Theme:</h2>
<form class="theme-chooser">
{{#each themes as theme}}
<div class="theme-group">
<input type="radio" id="choice-theme-{{theme.name}}"
value="{{theme.name}}" checked="$currentTheme === theme.name"
bind:group="selectedTheme" on:change="onThemeChange()">
<label for="choice-theme-{{theme.name}}">{{theme.label}}</label>
</div>
{{/each}}
</form>
<form class="instance-actions">
<button class="primary" disabled="$currentInstance === params.instanceName">
Switch to this instance
</button>
<button>Log out</button>
</form>
{{/if}}
</SettingsLayout>
</Layout>
@ -54,6 +61,15 @@
.theme-chooser label {
margin: 2px 10px 0;
}
.instance-actions {
width: 100%;
display: flex;
justify-content: right;
}
.instance-actions button {
margin: 0 20px;
flex-basis: 100%;
}
</style>
<script>
import { store } from '../../_utils/store'
@ -61,6 +77,7 @@
import SettingsLayout from '../_components/SettingsLayout.html'
import { getCurrentUser } from '../../_utils/mastodon/user'
import { themes } from '../../_static/themes'
import { switchToTheme } from '../../_utils/themeEngine'
export default {
components: {
@ -87,6 +104,9 @@
instanceThemes[instanceName] = newTheme
this.store.set({instanceThemes: instanceThemes})
this.store.save()
if (this.get('params').instanceName === this.store.get('currentInstance')) {
switchToTheme(newTheme)
}
}
}
}

View File

@ -70,6 +70,21 @@
},
store: () => store,
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) {
let currentRegisteredInstanceName = this.store.get('currentRegisteredInstanceName')
let currentRegisteredInstance = this.store.get('currentRegisteredInstance')
@ -88,6 +103,8 @@
}
this.store.set({
instanceNameInSearch: '',
currentRegisteredInstanceName: null,
currentRegisteredInstance: null,
loggedInInstances: loggedInInstances,
currentInstance: currentRegisteredInstanceName,
loggedInInstancesInOrder: loggedInInstancesInOrder
@ -95,20 +112,6 @@
this.store.save()
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>

View File

@ -62,29 +62,37 @@ button {
border: 1px solid var(--button-border);
cursor: pointer;
color: var(--button-text);
&:hover {
background: var(--button-bg-hover);
}
&:active {
background: var(--button-bg-active);
}
&[disabled] {
opacity: 0.35;
pointer-events: none;
cursor: not-allowed;
}
&.primary {
border: 1px solid var(--button-primary-border);
background: var(--button-primary-bg);
color: var(--button-primary-text);
&:hover {
background: var(--button-primary-bg-hover);
}
&:active {
background: var(--button-primary-bg-active);
}
}
}
button:hover {
background: var(--button-bg-hover);
}
button:active {
background: var(--button-bg-active);
}
button.primary {
border: 1px solid var(--button-primary-border);
background: var(--button-primary-bg);
color: var(--button-primary-text);
}
button.primary:hover {
background: var(--button-primary-bg-hover);
}
button.primary:active {
background: var(--button-primary-bg-active);
}
p, label, input {
font-size: 1.3em;

View File

@ -46,4 +46,4 @@
--settings-list-item-border: $border-color;
--settings-list-item-bg-active: darken($main-bg-color, 10%);
--settings-list-item-bg-hover: darken($main-bg-color, 2%);
}
}

View File

@ -7,6 +7,6 @@ $secondary-text-color: white;
@import "_base.scss";
body.theme-crimson {
body.theme-scarlet {
@include baseTheme()
}

View File

@ -25,6 +25,19 @@
%sapper.head%
</head>
<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;">
<symbol id="pinafore-logo" viewBox="0 0 100 100">