Resolve #6170
This commit is contained in:
parent
c6c62f956b
commit
e2c0ee66e4
@ -225,8 +225,13 @@ yearsOld: "{age}歳"
|
|||||||
registeredDate: "登録日"
|
registeredDate: "登録日"
|
||||||
location: "場所"
|
location: "場所"
|
||||||
theme: "テーマ"
|
theme: "テーマ"
|
||||||
|
themeForLightMode: "ライトモードで使うテーマ"
|
||||||
|
themeForDarkMode: "ダークモードで使うテーマ"
|
||||||
|
light: "ライト"
|
||||||
|
dark: "ダーク"
|
||||||
lightThemes: "明るいテーマ"
|
lightThemes: "明るいテーマ"
|
||||||
darkThemes: "暗いテーマ"
|
darkThemes: "暗いテーマ"
|
||||||
|
syncDeviceDarkMode: "デバイスのダークモードと同期する"
|
||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
selectFile: "ファイルを選択"
|
selectFile: "ファイルを選択"
|
||||||
selectFiles: "ファイルを選択"
|
selectFiles: "ファイルを選択"
|
||||||
|
@ -164,6 +164,7 @@ import { v4 as uuid } from 'uuid';
|
|||||||
import i18n from './i18n';
|
import i18n from './i18n';
|
||||||
import { host, instanceName } from './config';
|
import { host, instanceName } from './config';
|
||||||
import { search } from './scripts/search';
|
import { search } from './scripts/search';
|
||||||
|
import { isDeviceDarkmode } from './scripts/is-device-darkmode';
|
||||||
import MkToast from './components/toast.vue';
|
import MkToast from './components/toast.vue';
|
||||||
|
|
||||||
const DESKTOP_THRESHOLD = 1100;
|
const DESKTOP_THRESHOLD = 1100;
|
||||||
@ -224,6 +225,10 @@ export default Vue.extend({
|
|||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
|
if (this.$store.state.device.syncDeviceDarkMode) {
|
||||||
|
this.$store.commit('device/set', { key: 'darkMode', value: isDeviceDarkmode() });
|
||||||
|
}
|
||||||
|
|
||||||
if (this.$store.getters.isSignedIn) {
|
if (this.$store.getters.isSignedIn) {
|
||||||
this.connection = this.$root.stream.useSharedConnection('main');
|
this.connection = this.$root.stream.useSharedConnection('main');
|
||||||
this.connection.on('notification', this.onNotification);
|
this.connection.on('notification', this.onNotification);
|
||||||
|
@ -18,7 +18,7 @@ import PostFormDialog from './components/post-form-dialog.vue';
|
|||||||
import Dialog from './components/dialog.vue';
|
import Dialog from './components/dialog.vue';
|
||||||
import Menu from './components/menu.vue';
|
import Menu from './components/menu.vue';
|
||||||
import { router } from './router';
|
import { router } from './router';
|
||||||
import { applyTheme, lightTheme } from './theme';
|
import { applyTheme, lightTheme, builtinThemes } from './theme';
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
Vue.use(VueHotkey);
|
Vue.use(VueHotkey);
|
||||||
@ -163,6 +163,12 @@ os.init(async () => {
|
|||||||
isMobile: isMobile
|
isMobile: isMobile
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
'$store.state.device.darkMode'() {
|
||||||
|
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)));
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
api: os.api,
|
api: os.api,
|
||||||
signout: os.signout,
|
signout: os.signout,
|
||||||
|
@ -2,8 +2,30 @@
|
|||||||
<section class="rfqxtzch _card">
|
<section class="rfqxtzch _card">
|
||||||
<div class="_title"><fa :icon="faPalette"/> {{ $t('theme') }}</div>
|
<div class="_title"><fa :icon="faPalette"/> {{ $t('theme') }}</div>
|
||||||
<div class="_content">
|
<div class="_content">
|
||||||
<mk-select v-model="theme" :placeholder="$t('theme')">
|
<div class="darkMode" :class="{ disabled: syncDeviceDarkMode }">
|
||||||
<template #label>{{ $t('theme') }}</template>
|
<div class="toggleWrapper">
|
||||||
|
<input type="checkbox" class="dn" id="dn" v-model="darkMode" :disabled="syncDeviceDarkMode"/>
|
||||||
|
<label for="dn" class="toggle">
|
||||||
|
<span class="before">{{ $t('light') }}</span>
|
||||||
|
<span class="after">{{ $t('dark') }}</span>
|
||||||
|
<span class="toggle__handler">
|
||||||
|
<span class="crater crater--1"></span>
|
||||||
|
<span class="crater crater--2"></span>
|
||||||
|
<span class="crater crater--3"></span>
|
||||||
|
</span>
|
||||||
|
<span class="star star--1"></span>
|
||||||
|
<span class="star star--2"></span>
|
||||||
|
<span class="star star--3"></span>
|
||||||
|
<span class="star star--4"></span>
|
||||||
|
<span class="star star--5"></span>
|
||||||
|
<span class="star star--6"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="_content">
|
||||||
|
<mk-select v-model="lightTheme">
|
||||||
|
<template #label>{{ $t('themeForLightMode') }}</template>
|
||||||
<optgroup :label="$t('lightThemes')">
|
<optgroup :label="$t('lightThemes')">
|
||||||
<option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
|
<option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
@ -11,6 +33,18 @@
|
|||||||
<option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
|
<option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
</mk-select>
|
</mk-select>
|
||||||
|
<mk-select v-model="darkTheme">
|
||||||
|
<template #label>{{ $t('themeForDarkMode') }}</template>
|
||||||
|
<optgroup :label="$t('darkThemes')">
|
||||||
|
<option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup :label="$t('lightThemes')">
|
||||||
|
<option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
|
||||||
|
</optgroup>
|
||||||
|
</mk-select>
|
||||||
|
</div>
|
||||||
|
<div class="_content">
|
||||||
|
<mk-switch v-model="syncDeviceDarkMode">{{ $t('syncDeviceDarkMode') }}</mk-switch>
|
||||||
</div>
|
</div>
|
||||||
<div class="_content">
|
<div class="_content">
|
||||||
<mk-button primary v-if="wallpaper == null" @click="setWallpaper">{{ $t('setWallpaper') }}</mk-button>
|
<mk-button primary v-if="wallpaper == null" @click="setWallpaper">{{ $t('setWallpaper') }}</mk-button>
|
||||||
@ -25,6 +59,7 @@ import { faPalette } from '@fortawesome/free-solid-svg-icons';
|
|||||||
import MkInput from '../../components/ui/input.vue';
|
import MkInput from '../../components/ui/input.vue';
|
||||||
import MkButton from '../../components/ui/button.vue';
|
import MkButton from '../../components/ui/button.vue';
|
||||||
import MkSelect from '../../components/ui/select.vue';
|
import MkSelect from '../../components/ui/select.vue';
|
||||||
|
import MkSwitch from '../../components/ui/switch.vue';
|
||||||
import i18n from '../../i18n';
|
import i18n from '../../i18n';
|
||||||
import { Theme, builtinThemes, applyTheme } from '../../theme';
|
import { Theme, builtinThemes, applyTheme } from '../../theme';
|
||||||
import { selectFile } from '../../scripts/select-file';
|
import { selectFile } from '../../scripts/select-file';
|
||||||
@ -36,6 +71,7 @@ export default Vue.extend({
|
|||||||
MkInput,
|
MkInput,
|
||||||
MkButton,
|
MkButton,
|
||||||
MkSelect,
|
MkSelect,
|
||||||
|
MkSwitch,
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
@ -62,15 +98,38 @@ export default Vue.extend({
|
|||||||
return this.themes.filter(t => t.base == 'light' || t.kind == 'light');
|
return this.themes.filter(t => t.base == 'light' || t.kind == 'light');
|
||||||
},
|
},
|
||||||
|
|
||||||
theme: {
|
darkTheme: {
|
||||||
get() { return this.$store.state.device.theme; },
|
get() { return this.$store.state.device.darkTheme; },
|
||||||
set(value) { this.$store.commit('device/set', { key: 'theme', value }); }
|
set(value) { this.$store.commit('device/set', { key: 'darkTheme', value }); }
|
||||||
|
},
|
||||||
|
|
||||||
|
lightTheme: {
|
||||||
|
get() { return this.$store.state.device.lightTheme; },
|
||||||
|
set(value) { this.$store.commit('device/set', { key: 'lightTheme', value }); }
|
||||||
|
},
|
||||||
|
|
||||||
|
darkMode: {
|
||||||
|
get() { return this.$store.state.device.darkMode; },
|
||||||
|
set(value) { this.$store.commit('device/set', { key: 'darkMode', value }); }
|
||||||
|
},
|
||||||
|
|
||||||
|
syncDeviceDarkMode: {
|
||||||
|
get() { return this.$store.state.device.syncDeviceDarkMode; },
|
||||||
|
set(value) { this.$store.commit('device/set', { key: 'syncDeviceDarkMode', value }); }
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
theme() {
|
darkTheme() {
|
||||||
applyTheme(this.themes.find(x => x.id === this.theme));
|
if (this.$store.state.device.darkMode) {
|
||||||
|
applyTheme(this.themes.find(x => x.id === this.darkTheme));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
lightTheme() {
|
||||||
|
if (!this.$store.state.device.darkMode) {
|
||||||
|
applyTheme(this.themes.find(x => x.id === this.lightTheme));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
wallpaper() {
|
wallpaper() {
|
||||||
@ -92,3 +151,230 @@ export default Vue.extend({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.rfqxtzch {
|
||||||
|
> ._content {
|
||||||
|
> .darkMode {
|
||||||
|
position: relative;
|
||||||
|
padding: 32px 0;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
opacity: 0.7;
|
||||||
|
|
||||||
|
&, * {
|
||||||
|
cursor: not-allowed !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggleWrapper {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0 200px;
|
||||||
|
transform: translate3d(-50%, -50%, 0);
|
||||||
|
|
||||||
|
input {
|
||||||
|
position: absolute;
|
||||||
|
left: -99em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
width: 90px;
|
||||||
|
height: 50px;
|
||||||
|
background-color: #83D8FF;
|
||||||
|
border-radius: 90px - 6;
|
||||||
|
transition: background-color 200ms cubic-bezier(0.445, 0.05, 0.55, 0.95) !important;
|
||||||
|
|
||||||
|
> .before, > .after {
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
font-size: 18px;
|
||||||
|
transition: color 1s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .before {
|
||||||
|
left: -60px;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
> .after {
|
||||||
|
right: -58px;
|
||||||
|
color: var(--fg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle__handler {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
top: 3px;
|
||||||
|
left: 3px;
|
||||||
|
width: 50px - 6;
|
||||||
|
height: 50px - 6;
|
||||||
|
background-color: #FFCF96;
|
||||||
|
border-radius: 50px;
|
||||||
|
box-shadow: 0 2px 6px rgba(0,0,0,.3);
|
||||||
|
transition: all 400ms cubic-bezier(0.68, -0.55, 0.265, 1.55) !important;
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
|
||||||
|
.crater {
|
||||||
|
position: absolute;
|
||||||
|
background-color: #E8CDA5;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 200ms ease-in-out !important;
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crater--1 {
|
||||||
|
top: 18px;
|
||||||
|
left: 10px;
|
||||||
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crater--2 {
|
||||||
|
top: 28px;
|
||||||
|
left: 22px;
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.crater--3 {
|
||||||
|
top: 10px;
|
||||||
|
left: 25px;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.star {
|
||||||
|
position: absolute;
|
||||||
|
background-color: #ffffff;
|
||||||
|
transition: all 300ms cubic-bezier(0.445, 0.05, 0.55, 0.95) !important;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.star--1 {
|
||||||
|
top: 10px;
|
||||||
|
left: 35px;
|
||||||
|
z-index: 0;
|
||||||
|
width: 30px;
|
||||||
|
height: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.star--2 {
|
||||||
|
top: 18px;
|
||||||
|
left: 28px;
|
||||||
|
z-index: 1;
|
||||||
|
width: 30px;
|
||||||
|
height: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.star--3 {
|
||||||
|
top: 27px;
|
||||||
|
left: 40px;
|
||||||
|
z-index: 0;
|
||||||
|
width: 30px;
|
||||||
|
height: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.star--4,
|
||||||
|
.star--5,
|
||||||
|
.star--6 {
|
||||||
|
opacity: 0;
|
||||||
|
transition: all 300ms 0 cubic-bezier(0.445, 0.05, 0.55, 0.95) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.star--4 {
|
||||||
|
top: 16px;
|
||||||
|
left: 11px;
|
||||||
|
z-index: 0;
|
||||||
|
width: 2px;
|
||||||
|
height: 2px;
|
||||||
|
transform: translate3d(3px,0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.star--5 {
|
||||||
|
top: 32px;
|
||||||
|
left: 17px;
|
||||||
|
z-index: 0;
|
||||||
|
width: 3px;
|
||||||
|
height: 3px;
|
||||||
|
transform: translate3d(3px,0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.star--6 {
|
||||||
|
top: 36px;
|
||||||
|
left: 28px;
|
||||||
|
z-index: 0;
|
||||||
|
width: 2px;
|
||||||
|
height: 2px;
|
||||||
|
transform: translate3d(3px,0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked {
|
||||||
|
+ .toggle {
|
||||||
|
background-color: #749DD6;
|
||||||
|
|
||||||
|
> .before {
|
||||||
|
color: var(--fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
> .after {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle__handler {
|
||||||
|
background-color: #FFE5B5;
|
||||||
|
transform: translate3d(40px, 0, 0) rotate(0);
|
||||||
|
|
||||||
|
.crater { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.star--1 {
|
||||||
|
width: 2px;
|
||||||
|
height: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.star--2 {
|
||||||
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
|
transform: translate3d(-5px, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.star--3 {
|
||||||
|
width: 2px;
|
||||||
|
height: 2px;
|
||||||
|
transform: translate3d(-7px, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.star--4,
|
||||||
|
.star--5,
|
||||||
|
.star--6 {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate3d(0,0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.star--4 {
|
||||||
|
transition: all 300ms 200ms cubic-bezier(0.445, 0.05, 0.55, 0.95) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.star--5 {
|
||||||
|
transition: all 300ms 300ms cubic-bezier(0.445, 0.05, 0.55, 0.95) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.star--6 {
|
||||||
|
transition: all 300ms 400ms cubic-bezier(0.445, 0.05, 0.55, 0.95) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
3
src/client/scripts/is-device-darkmode.ts
Normal file
3
src/client/scripts/is-device-darkmode.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export function isDeviceDarkmode() {
|
||||||
|
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
}
|
@ -35,7 +35,10 @@ const defaultDeviceSettings = {
|
|||||||
accounts: [],
|
accounts: [],
|
||||||
recentEmojis: [],
|
recentEmojis: [],
|
||||||
themes: [],
|
themes: [],
|
||||||
theme: '4eea646f-7afa-4645-83e9-83af0333cd37',
|
darkTheme: '8c539dc1-0fab-4d47-9194-39c508e9bfe1',
|
||||||
|
lightTheme: '4eea646f-7afa-4645-83e9-83af0333cd37',
|
||||||
|
darkMode: false,
|
||||||
|
syncDeviceDarkMode: true,
|
||||||
animation: true,
|
animation: true,
|
||||||
animatedMfm: true,
|
animatedMfm: true,
|
||||||
imageNewTab: false,
|
imageNewTab: false,
|
||||||
|
@ -44,7 +44,7 @@ export function applyTheme(theme: Theme, persist = true) {
|
|||||||
const _theme = JSON.parse(JSON.stringify(theme));
|
const _theme = JSON.parse(JSON.stringify(theme));
|
||||||
|
|
||||||
if (_theme.base) {
|
if (_theme.base) {
|
||||||
const base = [lightTheme, darkTheme].find(x => x.id == _theme.base);
|
const base = [lightTheme, darkTheme].find(x => x.id === _theme.base);
|
||||||
_theme.props = Object.assign({}, base.props, _theme.props);
|
_theme.props = Object.assign({}, base.props, _theme.props);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user