summaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2020-03-23 19:06:46 +0900
committersyuilo <syuilotan@yahoo.co.jp>2020-03-23 19:06:46 +0900
commitdac962580b40c5828e70bc4745ce33e049720c6b (patch)
treed71c9a1bb4d9ca9407cde39d25ec95965b215118 /src/client
parentUpdate CHANGELOG.md (diff)
downloadsharkey-dac962580b40c5828e70bc4745ce33e049720c6b.tar.gz
sharkey-dac962580b40c5828e70bc4745ce33e049720c6b.tar.bz2
sharkey-dac962580b40c5828e70bc4745ce33e049720c6b.zip
テーマインポート機能を実装するなど
Diffstat (limited to 'src/client')
-rw-r--r--src/client/components/error.vue2
-rw-r--r--src/client/components/notes.vue2
-rw-r--r--src/client/init.ts1
-rw-r--r--src/client/pages/follow-requests.vue2
-rw-r--r--src/client/pages/messaging/index.vue2
-rw-r--r--src/client/pages/not-found.vue2
-rw-r--r--src/client/pages/preferences/theme.vue95
-rw-r--r--src/client/theme.ts7
8 files changed, 104 insertions, 9 deletions
diff --git a/src/client/components/error.vue b/src/client/components/error.vue
index 7446a7cb5d..dd9de43c16 100644
--- a/src/client/components/error.vue
+++ b/src/client/components/error.vue
@@ -1,6 +1,6 @@
<template>
<div class="mjndxjcg _panel">
- <img src="https://xn--931a.moe/assets/error.png" class="_ghost"/>
+ <img src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/>
<p><fa :icon="faExclamationTriangle"/> {{ $t('error') }}</p>
<mk-button @click="() => $emit('retry')" class="button">{{ $t('retry') }}</mk-button>
</div>
diff --git a/src/client/components/notes.vue b/src/client/components/notes.vue
index bc2ae8472c..65dda17575 100644
--- a/src/client/components/notes.vue
+++ b/src/client/components/notes.vue
@@ -1,7 +1,7 @@
<template>
<div class="mk-notes" v-size="[{ max: 500 }]">
<div class="empty" v-if="empty">
- <img src="https://xn--931a.moe/assets/info.png" class="_ghost"/>
+ <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
<div>{{ $t('noNotes') }}</div>
</div>
diff --git a/src/client/init.ts b/src/client/init.ts
index c7587afb8c..b9c6aedae4 100644
--- a/src/client/init.ts
+++ b/src/client/init.ts
@@ -178,6 +178,7 @@ os.init(async () => {
},
watch: {
'$store.state.device.darkMode'() {
+ // TODO: このファイルでbuiltinThemesを参照するとcode splittingが効かず、初回読み込み時に全てのテーマコードを読み込むことになってしまい無駄なので何とかする
const themes = builtinThemes.concat(this.$store.state.device.themes);
applyTheme(themes.find(x => x.id === (this.$store.state.device.darkMode ? this.$store.state.device.darkTheme : this.$store.state.device.lightTheme)));
}
diff --git a/src/client/pages/follow-requests.vue b/src/client/pages/follow-requests.vue
index 14d60a12ec..a900bf735c 100644
--- a/src/client/pages/follow-requests.vue
+++ b/src/client/pages/follow-requests.vue
@@ -6,7 +6,7 @@
<mk-pagination :pagination="pagination" class="mk-follow-requests" ref="list">
<template #empty>
<div class="tkdrhpxr">
- <img src="https://xn--931a.moe/assets/info.png" class="_ghost"/>
+ <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
<div>{{ $t('noFollowRequests') }}</div>
</div>
</template>
diff --git a/src/client/pages/messaging/index.vue b/src/client/pages/messaging/index.vue
index 702979a098..ed24f8ef54 100644
--- a/src/client/pages/messaging/index.vue
+++ b/src/client/pages/messaging/index.vue
@@ -32,7 +32,7 @@
</router-link>
</div>
<div class="no-history" v-if="!fetching && messages.length == 0">
- <img src="https://xn--931a.moe/assets/info.png" class="_ghost"/>
+ <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
<div>{{ $t('noHistory') }}</div>
</div>
<mk-loading v-if="fetching"/>
diff --git a/src/client/pages/not-found.vue b/src/client/pages/not-found.vue
index 6ddbd1932b..9608e07786 100644
--- a/src/client/pages/not-found.vue
+++ b/src/client/pages/not-found.vue
@@ -5,7 +5,7 @@
<section class="_card">
<div class="_content">
- <img src="https://xn--931a.moe/assets/not-found.png" class="_ghost"/>
+ <img src="https://xn--931a.moe/assets/not-found.jpg" class="_ghost"/>
<div>{{ $t('notFoundDescription') }}</div>
</div>
</section>
diff --git a/src/client/pages/preferences/theme.vue b/src/client/pages/preferences/theme.vue
index 488935a0cd..fcea457396 100644
--- a/src/client/pages/preferences/theme.vue
+++ b/src/client/pages/preferences/theme.vue
@@ -42,6 +42,7 @@
<option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup>
</mk-select>
+ <a href="https://assets.msky.cafe/theme/list" rel="noopener" target="_blank" class="_link">{{ $t('_theme.explore') }}</a>
</div>
<div class="_content">
<mk-switch v-model="syncDeviceDarkMode">{{ $t('syncDeviceDarkMode') }}</mk-switch>
@@ -50,18 +51,43 @@
<mk-button primary v-if="wallpaper == null" @click="setWallpaper">{{ $t('setWallpaper') }}</mk-button>
<mk-button primary v-else @click="wallpaper = null">{{ $t('removeWallpaper') }}</mk-button>
</div>
+ <div class="_content">
+ <details>
+ <summary><fa :icon="faDownload"/> {{ $t('_theme.install') }}</summary>
+ <mk-textarea v-model="installThemeCode">
+ <span>{{ $t('_theme.code') }}</span>
+ </mk-textarea>
+ <mk-button @click="() => install(this.installThemeCode)" :disabled="installThemeCode == null"><fa :icon="faCheck"/> {{ $t('install') }}</mk-button>
+ </details>
+ </div>
+ <div class="_content">
+ <details>
+ <summary><fa :icon="faFolderOpen"/> {{ $t('_theme.manage') }}</summary>
+ <mk-select v-model="selectedThemeId">
+ <option v-for="x in installedThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
+ </mk-select>
+ <template v-if="selectedTheme">
+ <mk-textarea readonly tall :value="selectedThemeCode">
+ <span>{{ $t('_theme.code') }}</span>
+ </mk-textarea>
+ <mk-button @click="uninstall()" v-if="!builtinThemes.some(t => t.id == selectedTheme.id)"><fa :icon="faTrashAlt"/> {{ $t('uninstall') }}</mk-button>
+ </template>
+ </details>
+ </div>
</section>
</template>
<script lang="ts">
import Vue from 'vue';
-import { faPalette } from '@fortawesome/free-solid-svg-icons';
+import { faPalette, faDownload, faFolderOpen, faCheck, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
+import * as JSON5 from 'json5';
import MkInput from '../../components/ui/input.vue';
import MkButton from '../../components/ui/button.vue';
import MkSelect from '../../components/ui/select.vue';
import MkSwitch from '../../components/ui/switch.vue';
+import MkTextarea from '../../components/ui/textarea.vue';
import i18n from '../../i18n';
-import { Theme, builtinThemes, applyTheme } from '../../theme';
+import { Theme, builtinThemes, applyTheme, validateTheme } from '../../theme';
import { selectFile } from '../../scripts/select-file';
import { isDeviceDarkmode } from '../../scripts/is-device-darkmode';
@@ -73,12 +99,16 @@ export default Vue.extend({
MkButton,
MkSelect,
MkSwitch,
+ MkTextarea,
},
data() {
return {
+ builtinThemes,
+ installThemeCode: null,
+ selectedThemeId: null,
wallpaper: localStorage.getItem('wallpaper'),
- faPalette
+ faPalette, faDownload, faFolderOpen, faCheck, faTrashAlt
}
},
@@ -118,6 +148,16 @@ export default Vue.extend({
get() { return this.$store.state.device.syncDeviceDarkMode; },
set(value) { this.$store.commit('device/set', { key: 'syncDeviceDarkMode', value }); }
},
+
+ selectedTheme() {
+ if (this.selectedThemeId == null) return null;
+ return this.themes.find(x => x.id === this.selectedThemeId);
+ },
+
+ selectedThemeCode() {
+ if (this.selectedTheme == null) return null;
+ return JSON5.stringify(this.selectedTheme, null, '\t');
+ },
},
watch: {
@@ -155,6 +195,53 @@ export default Vue.extend({
this.wallpaper = file.url;
});
},
+
+ install(code) {
+ let theme;
+ try {
+ theme = JSON5.parse(code);
+ } catch (e) {
+ this.$root.dialog({
+ type: 'error',
+ text: this.$t('_theme.invalid')
+ });
+ return;
+ }
+ if (!validateTheme(theme)) {
+ this.$root.dialog({
+ type: 'error',
+ text: this.$t('_theme.invalid')
+ });
+ return;
+ }
+ if (this.$store.state.device.themes.some(t => t.id === theme.id)) {
+ this.$root.dialog({
+ type: 'info',
+ text: this.$t('_theme.alreadyInstalled')
+ });
+ return;
+ }
+ const themes = this.$store.state.device.themes.concat(theme);
+ this.$store.commit('device/set', {
+ key: 'themes', value: themes
+ });
+ this.$root.dialog({
+ type: 'success',
+ text: this.$t('_theme.installed', { name: theme.name })
+ });
+ },
+
+ uninstall() {
+ const theme = this.selectedTheme;
+ const themes = this.$store.state.device.themes.filter(t => t.id != theme.id);
+ this.$store.commit('device/set', {
+ key: 'themes', value: themes
+ });
+ this.$root.dialog({
+ type: 'info',
+ iconOnly: true, autoClose: true
+ });
+ },
}
});
</script>
@@ -179,7 +266,7 @@ export default Vue.extend({
top: 50%;
left: 50%;
overflow: hidden;
- padding: 0 200px;
+ padding: 0 100px;
transform: translate3d(-50%, -50%, 0);
input {
diff --git a/src/client/theme.ts b/src/client/theme.ts
index 2a6adbffcc..e90c1f3a3b 100644
--- a/src/client/theme.ts
+++ b/src/client/theme.ts
@@ -102,3 +102,10 @@ function compile(theme: Theme): { [key: string]: string } {
function genValue(c: tinycolor.Instance): string {
return c.toRgbString();
}
+
+export function validateTheme(theme: Record<string, any>): boolean {
+ if (theme.id == null) return false;
+ if (theme.name == null) return false;
+ if (theme.base == null || !['light', 'dark'].includes(theme.base)) return false;
+ return true;
+}