summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2018-09-29 00:01:11 +0900
committersyuilo <syuilotan@yahoo.co.jp>2018-09-29 00:01:11 +0900
commit6a82e94c5489d4879cbbf86091cd15c7d144f284 (patch)
tree148f1b4bba454a71075d4b7b21d983b58215e8af /src
parentwip (diff)
downloadmisskey-6a82e94c5489d4879cbbf86091cd15c7d144f284.tar.gz
misskey-6a82e94c5489d4879cbbf86091cd15c7d144f284.tar.bz2
misskey-6a82e94c5489d4879cbbf86091cd15c7d144f284.zip
wip
Diffstat (limited to 'src')
-rw-r--r--src/client/app/app.vue12
-rw-r--r--src/client/app/common/views/components/avatar.vue4
-rw-r--r--src/client/app/common/views/components/index.ts2
-rw-r--r--src/client/app/common/views/components/theme.vue179
-rw-r--r--src/client/app/common/views/components/ui/button.vue12
-rw-r--r--src/client/app/desktop/views/components/settings.vue5
-rw-r--r--src/client/app/init.ts35
-rw-r--r--src/client/app/mobile/views/pages/settings.vue7
-rw-r--r--src/client/app/store.ts3
-rw-r--r--src/client/app/theme.ts (renamed from src/client/app/common/scripts/theme.ts)27
-rw-r--r--src/client/theme/dark.json2
-rw-r--r--src/client/theme/halloween.json3
-rw-r--r--src/client/theme/light.json2
13 files changed, 262 insertions, 31 deletions
diff --git a/src/client/app/app.vue b/src/client/app/app.vue
index 9b6af27ece..778e9f29cf 100644
--- a/src/client/app/app.vue
+++ b/src/client/app/app.vue
@@ -14,8 +14,7 @@ export default Vue.extend({
keymap(): any {
return {
'h|slash': this.help,
- 'd': this.dark,
- 'x': this.test
+ 'd': this.dark
};
}
},
@@ -26,11 +25,10 @@ export default Vue.extend({
},
dark() {
- applyTheme(darkTheme);
- },
-
- test() {
- applyTheme(halloweenTheme);
+ this.$store.commit('device/set', {
+ key: 'darkmode',
+ value: !this.$store.state.device.darkmode
+ });
}
}
});
diff --git a/src/client/app/common/views/components/avatar.vue b/src/client/app/common/views/components/avatar.vue
index ca09af87de..ac018abcfc 100644
--- a/src/client/app/common/views/components/avatar.vue
+++ b/src/client/app/common/views/components/avatar.vue
@@ -59,7 +59,9 @@ export default Vue.extend({
}
},
mounted() {
- this.$el.style.color = `rgb(${this.user.avatarColor.slice(0, 3).join(',')})`;
+ if (this.user.avatarColor) {
+ this.$el.style.color = `rgb(${this.user.avatarColor.slice(0, 3).join(',')})`;
+ }
},
methods: {
onClick(e) {
diff --git a/src/client/app/common/views/components/index.ts b/src/client/app/common/views/components/index.ts
index 4c1c0afa80..0dea38a7a1 100644
--- a/src/client/app/common/views/components/index.ts
+++ b/src/client/app/common/views/components/index.ts
@@ -1,5 +1,6 @@
import Vue from 'vue';
+import theme from './theme.vue';
import instance from './instance.vue';
import cwButton from './cw-button.vue';
import tagCloud from './tag-cloud.vue';
@@ -43,6 +44,7 @@ import uiSelect from './ui/select.vue';
import formButton from './ui/form/button.vue';
import formRadio from './ui/form/radio.vue';
+Vue.component('mk-theme', theme);
Vue.component('mk-instance', instance);
Vue.component('mk-cw-button', cwButton);
Vue.component('mk-tag-cloud', tagCloud);
diff --git a/src/client/app/common/views/components/theme.vue b/src/client/app/common/views/components/theme.vue
new file mode 100644
index 0000000000..27888d1e85
--- /dev/null
+++ b/src/client/app/common/views/components/theme.vue
@@ -0,0 +1,179 @@
+<template>
+<div class="nicnklzforebnpfgasiypmpdaaglujqm">
+ <label>
+ <span>%i18n:@light-theme%</span>
+ <ui-select v-model="light" placeholder="%i18n:@light-theme%">
+ <option v-for="x in themes" :value="x.meta.id" :key="x.meta.id">{{ x.meta.name }}</option>
+ </ui-select>
+ </label>
+
+ <label>
+ <span>%i18n:@dark-theme%</span>
+ <ui-select v-model="dark" placeholder="%i18n:@dark-theme%">
+ <option v-for="x in themes" :value="x.meta.id" :key="x.meta.id">{{ x.meta.name }}</option>
+ </ui-select>
+ </label>
+
+ <details class="creator">
+ <summary>%i18n:@create-a-theme%</summary>
+ <div>
+ <span>%i18n:@base-theme%:</span>
+ <ui-radio v-model="myThemeBase" value="light">%i18n:@base-theme-light%</ui-radio>
+ <ui-radio v-model="myThemeBase" value="dark">%i18n:@base-theme-dark%</ui-radio>
+ </div>
+ <div>
+ <ui-input v-model="myThemeName">
+ <span>%i18n:@theme-name%</span>
+ </ui-input>
+ </div>
+ <div>
+ <div style="padding-bottom:8px;">%i18n:@primary-color%:</div>
+ <color-picker v-model="myThemePrimary"/>
+ </div>
+ <div>
+ <div style="padding-bottom:8px;">%i18n:@secondary-color%:</div>
+ <color-picker v-model="myThemeSecondary"/>
+ </div>
+ <div>
+ <div style="padding-bottom:8px;">%i18n:@text-color%:</div>
+ <color-picker v-model="myThemeText"/>
+ </div>
+ <ui-button @click="preview()">%i18n:@preview-created-theme%</ui-button>
+ <ui-button primary @click="gen()">%i18n:@save-created-theme%</ui-button>
+ </details>
+
+ <details>
+ <summary>%i18n:@install-a-theme%</summary>
+ <ui-textarea v-model="installThemeCode">
+ <span>%i18n:@theme-code%</span>
+ </ui-textarea>
+ <ui-button @click="install()">%i18n:@install%</ui-button>
+ </details>
+
+ <details>
+ <summary>%i18n:@installed-themes%</summary>
+ <ui-select v-model="selectedInstalledTheme" placeholder="%i18n:@select-theme%">
+ <option v-for="x in installedThemes" :value="x.meta.id" :key="x.meta.id">{{ x.meta.name }}</option>
+ </ui-select>
+ <ui-textarea readonly :value="selectedInstalledThemeCode">
+ <span>%i18n:@theme-code%</span>
+ </ui-textarea>
+ </details>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import { apiUrl, docsUrl } from '../../../config';
+import { lightTheme, darkTheme, builtinThemes, applyTheme } from '../../../theme';
+import { Chrome } from 'vue-color';
+import * as uuid from 'uuid';
+import * as tinycolor from 'tinycolor2';
+
+export default Vue.extend({
+ components: {
+ ColorPicker: Chrome
+ },
+
+ data() {
+ return {
+ installThemeCode: null,
+ selectedInstalledTheme: null,
+ myThemeBase: 'light',
+ myThemeName: '',
+ myThemePrimary: lightTheme.meta.vars.primary,
+ myThemeSecondary: lightTheme.meta.vars.secondary,
+ myThemeText: lightTheme.meta.vars.text
+ };
+ },
+
+ computed: {
+ themes(): any {
+ return this.$store.state.device.themes.concat(builtinThemes);
+ },
+
+ installedThemes(): any {
+ return this.$store.state.device.themes;
+ },
+
+ light: {
+ get() { return this.$store.state.device.lightTheme; },
+ set(value) { this.$store.commit('device/set', { key: 'lightTheme', value }); }
+ },
+
+ dark: {
+ get() { return this.$store.state.device.darkTheme; },
+ set(value) { this.$store.commit('device/set', { key: 'darkTheme', value }); }
+ },
+
+ selectedInstalledThemeCode() {
+ if (this.selectedInstalledTheme == null) return null;
+ return JSON.stringify(this.installedThemes.find(x => x.meta.id == this.selectedInstalledTheme));
+ },
+
+ myTheme(): any {
+ return {
+ meta: {
+ name: this.myThemeName,
+ author: this.$store.state.i.name,
+ base: this.myThemeBase,
+ vars: {
+ primary: tinycolor(typeof this.myThemePrimary == 'string' ? this.myThemePrimary : this.myThemePrimary.rgba).toRgbString(),
+ secondary: tinycolor(typeof this.myThemeSecondary == 'string' ? this.myThemeSecondary : this.myThemeSecondary.rgba).toRgbString(),
+ text: tinycolor(typeof this.myThemeText == 'string' ? this.myThemeText : this.myThemeText.rgba).toRgbString()
+ }
+ }
+ };
+ }
+ },
+
+ watch: {
+ myThemeBase(v) {
+ const theme = v == 'light' ? lightTheme : darkTheme;
+ this.myThemePrimary = theme.meta.vars.primary;
+ this.myThemeSecondary = theme.meta.vars.secondary;
+ this.myThemeText = theme.meta.vars.text;
+ }
+ },
+
+ methods: {
+ install() {
+ const theme = JSON.parse(this.installThemeCode);
+ if (theme.meta == null || theme.meta.id == null) {
+ alert('%i18n:@invalid-theme%');
+ return;
+ }
+ if (this.$store.state.device.themes.some(t => t.meta.id == theme.meta.id)) {
+ alert('%i18n:@already-installed%');
+ return;
+ }
+ const themes = this.$store.state.device.themes.concat(theme);
+ this.$store.commit('device/set', {
+ key: 'themes', value: themes
+ });
+ },
+
+ preview() {
+ applyTheme(this.myTheme, false);
+ },
+
+ gen() {
+ const theme = this.myTheme;
+ theme.meta.id = uuid();
+ const themes = this.$store.state.device.themes.concat(theme);
+ this.$store.commit('device/set', {
+ key: 'themes', value: themes
+ });
+ alert('%i18n:@saved%');
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+.nicnklzforebnpfgasiypmpdaaglujqm
+ > .creator
+ > div
+ padding 16px 0
+ border-bottom solid 1px var(--faceDivider)
+</style>
diff --git a/src/client/app/common/views/components/ui/button.vue b/src/client/app/common/views/components/ui/button.vue
index a165d100a4..47644b32b5 100644
--- a/src/client/app/common/views/components/ui/button.vue
+++ b/src/client/app/common/views/components/ui/button.vue
@@ -27,14 +27,6 @@ export default Vue.extend({
return {
styl: 'fill'
};
- },
- inject: {
- isCardChild: { default: false }
- },
- created() {
- if (this.isCardChild) {
- this.styl = 'line';
- }
}
});
</script>
@@ -43,6 +35,7 @@ export default Vue.extend({
.dmtdnykelhudezerjlfpbhgovrgnqqgr
display block
width 100%
+ min-height 40px
margin 0
padding 0
font-weight normal
@@ -52,6 +45,9 @@ export default Vue.extend({
outline none
box-shadow none
+ &:not(.inline) + .dmtdnykelhudezerjlfpbhgovrgnqqgr
+ margin-top 16px
+
&.inline
display inline-block
width auto
diff --git a/src/client/app/desktop/views/components/settings.vue b/src/client/app/desktop/views/components/settings.vue
index c7d82590ea..1cb8d4d4c8 100644
--- a/src/client/app/desktop/views/components/settings.vue
+++ b/src/client/app/desktop/views/components/settings.vue
@@ -20,6 +20,11 @@
</section>
<section class="web" v-show="page == 'web'">
+ <h1>%i18n:@theme%</h1>
+ <mk-theme/>
+ </section>
+
+ <section class="web" v-show="page == 'web'">
<h1>%i18n:@behaviour%</h1>
<ui-switch v-model="fetchOnScroll">
%i18n:@fetch-on-scroll%
diff --git a/src/client/app/init.ts b/src/client/app/init.ts
index 8d430ad7ff..802f7b42eb 100644
--- a/src/client/app/init.ts
+++ b/src/client/app/init.ts
@@ -14,11 +14,11 @@ import App from './app.vue';
import checkForUpdate from './common/scripts/check-for-update';
import MiOS, { API } from './mios';
import { version, codename, lang } from './config';
-import applyTheme from './common/scripts/theme';
-const defaultTheme = require('../theme/light.json');
+import { builtinThemes, applyTheme } from './theme';
+const lightTheme = require('../theme/light.json');
if (localStorage.getItem('theme') == null) {
- applyTheme(defaultTheme);
+ applyTheme(lightTheme);
}
Vue.use(Vuex);
@@ -92,6 +92,35 @@ export default (callback: (launch: (router: VueRouter, api?: (os: MiOS) => API)
const launch = (router: VueRouter, api?: (os: MiOS) => API) => {
os.apis = api ? api(os) : null;
+ //#region theme
+ os.store.watch(s => {
+ return s.device.darkmode;
+ }, v => {
+ const themes = os.store.state.device.themes.concat(builtinThemes);
+ const dark = themes.find(t => t.meta.id == os.store.state.device.darkTheme);
+ const light = themes.find(t => t.meta.id == os.store.state.device.lightTheme);
+ applyTheme(v ? dark : light);
+ });
+ os.store.watch(s => {
+ return s.device.lightTheme;
+ }, v => {
+ const themes = os.store.state.device.themes.concat(builtinThemes);
+ const theme = themes.find(t => t.meta.id == v);
+ if (!os.store.state.device.darkmode) {
+ applyTheme(theme);
+ }
+ });
+ os.store.watch(s => {
+ return s.device.darkTheme;
+ }, v => {
+ const themes = os.store.state.device.themes.concat(builtinThemes);
+ const theme = themes.find(t => t.meta.id == v);
+ if (os.store.state.device.darkmode) {
+ applyTheme(theme);
+ }
+ });
+ //#endregion
+
//#region shadow
const shadow = '0 3px 8px rgba(0, 0, 0, 0.2)';
if (os.store.state.settings.useShadow) document.documentElement.style.setProperty('--shadow', shadow);
diff --git a/src/client/app/mobile/views/pages/settings.vue b/src/client/app/mobile/views/pages/settings.vue
index b83eaf6d33..94fa38cec9 100644
--- a/src/client/app/mobile/views/pages/settings.vue
+++ b/src/client/app/mobile/views/pages/settings.vue
@@ -24,6 +24,13 @@
</section>
<section>
+ <header>%i18n:@theme%</header>
+ <div>
+ <mk-theme/>
+ </div>
+ </section>
+
+ <section>
<header>%i18n:@timeline%</header>
<div>
<ui-switch v-model="showReplyTarget">%i18n:@show-reply-target%</ui-switch>
diff --git a/src/client/app/store.ts b/src/client/app/store.ts
index fbcc53d7be..545261225a 100644
--- a/src/client/app/store.ts
+++ b/src/client/app/store.ts
@@ -44,6 +44,9 @@ const defaultDeviceSettings = {
apiViaStream: true,
autoPopout: false,
darkmode: false,
+ darkTheme: 'dark',
+ lightTheme: 'light',
+ themes: [],
enableSounds: true,
soundVolume: 0.5,
lang: null,
diff --git a/src/client/app/common/scripts/theme.ts b/src/client/app/theme.ts
index 7a1c6abb76..1147ff300d 100644
--- a/src/client/app/common/scripts/theme.ts
+++ b/src/client/app/theme.ts
@@ -1,22 +1,21 @@
import * as tinycolor from 'tinycolor2';
-const lightTheme = require('../../../theme/light');
-const darkTheme = require('../../../theme/dark');
type Theme = {
meta: {
id: string;
name: string;
- inherit: string;
+ author: string;
+ base?: string;
vars: any;
};
} & {
[key: string]: string;
};
-export default function(theme: Theme) {
- if (theme.meta.inherit) {
- const inherit = [lightTheme, darkTheme].find(x => x.meta.id == theme.meta.inherit);
- theme = Object.assign({}, inherit, theme);
+export function applyTheme(theme: Theme, persisted = true) {
+ if (theme.meta.base) {
+ const base = [lightTheme, darkTheme].find(x => x.meta.id == theme.meta.base);
+ theme = Object.assign({}, base, theme);
}
const props = compile(theme);
@@ -26,7 +25,9 @@ export default function(theme: Theme) {
document.documentElement.style.setProperty(`--${k}`, v.toString());
});
- localStorage.setItem('theme', JSON.stringify(props));
+ if (persisted) {
+ localStorage.setItem('theme', JSON.stringify(props));
+ }
}
function compile(theme: Theme): { [key: string]: string } {
@@ -87,3 +88,13 @@ function compile(theme: Theme): { [key: string]: string } {
function genValue(c: tinycolor.Instance): string {
return c.toRgbString();
}
+
+export const lightTheme = require('../theme/light.json');
+export const darkTheme = require('../theme/dark.json');
+export const halloweenTheme = require('../theme/halloween.json');
+
+export const builtinThemes = [
+ lightTheme,
+ darkTheme,
+ halloweenTheme
+];
diff --git a/src/client/theme/dark.json b/src/client/theme/dark.json
index 015225ddab..74447b8f2f 100644
--- a/src/client/theme/dark.json
+++ b/src/client/theme/dark.json
@@ -1,6 +1,6 @@
{
"meta": {
- "id": "9978f7f9-5616-44fd-a704-cc5985efdd63",
+ "id": "dark",
"name": "Dark",
"author": "syuilo",
"vars": {
diff --git a/src/client/theme/halloween.json b/src/client/theme/halloween.json
index 6e92db95ff..fb34db57a8 100644
--- a/src/client/theme/halloween.json
+++ b/src/client/theme/halloween.json
@@ -3,10 +3,9 @@
"id": "42e4f09b-67d5-498c-af7d-29faa54745b0",
"name": "Halloween",
"author": "syuilo",
- "inherit": "9978f7f9-5616-44fd-a704-cc5985efdd63",
+ "base": "dark",
"vars": {
"primary": "#d67036",
- "primaryForeground": "#fff",
"secondary": "#1f1d30",
"text": "#b1bee3"
}
diff --git a/src/client/theme/light.json b/src/client/theme/light.json
index 3d131f066a..1b6604e642 100644
--- a/src/client/theme/light.json
+++ b/src/client/theme/light.json
@@ -1,6 +1,6 @@
{
"meta": {
- "id": "406cfea3-a4e7-486c-9057-30ede1353c3f",
+ "id": "light",
"name": "Light",
"author": "syuilo",
"vars": {