mirror of
https://github.com/misskey-dev/misskey.git
synced 2024-11-23 08:46:56 +01:00
Resolve #6170
This commit is contained in:
parent
c6c62f956b
commit
e2c0ee66e4
7 changed files with 318 additions and 10 deletions
|
@ -225,8 +225,13 @@ yearsOld: "{age}歳"
|
|||
registeredDate: "登録日"
|
||||
location: "場所"
|
||||
theme: "テーマ"
|
||||
themeForLightMode: "ライトモードで使うテーマ"
|
||||
themeForDarkMode: "ダークモードで使うテーマ"
|
||||
light: "ライト"
|
||||
dark: "ダーク"
|
||||
lightThemes: "明るいテーマ"
|
||||
darkThemes: "暗いテーマ"
|
||||
syncDeviceDarkMode: "デバイスのダークモードと同期する"
|
||||
drive: "ドライブ"
|
||||
selectFile: "ファイルを選択"
|
||||
selectFiles: "ファイルを選択"
|
||||
|
|
|
@ -164,6 +164,7 @@ import { v4 as uuid } from 'uuid';
|
|||
import i18n from './i18n';
|
||||
import { host, instanceName } from './config';
|
||||
import { search } from './scripts/search';
|
||||
import { isDeviceDarkmode } from './scripts/is-device-darkmode';
|
||||
import MkToast from './components/toast.vue';
|
||||
|
||||
const DESKTOP_THRESHOLD = 1100;
|
||||
|
@ -224,6 +225,10 @@ export default Vue.extend({
|
|||
},
|
||||
|
||||
created() {
|
||||
if (this.$store.state.device.syncDeviceDarkMode) {
|
||||
this.$store.commit('device/set', { key: 'darkMode', value: isDeviceDarkmode() });
|
||||
}
|
||||
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
this.connection = this.$root.stream.useSharedConnection('main');
|
||||
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 Menu from './components/menu.vue';
|
||||
import { router } from './router';
|
||||
import { applyTheme, lightTheme } from './theme';
|
||||
import { applyTheme, lightTheme, builtinThemes } from './theme';
|
||||
|
||||
Vue.use(Vuex);
|
||||
Vue.use(VueHotkey);
|
||||
|
@ -163,6 +163,12 @@ os.init(async () => {
|
|||
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: {
|
||||
api: os.api,
|
||||
signout: os.signout,
|
||||
|
|
|
@ -2,8 +2,30 @@
|
|||
<section class="rfqxtzch _card">
|
||||
<div class="_title"><fa :icon="faPalette"/> {{ $t('theme') }}</div>
|
||||
<div class="_content">
|
||||
<mk-select v-model="theme" :placeholder="$t('theme')">
|
||||
<template #label>{{ $t('theme') }}</template>
|
||||
<div class="darkMode" :class="{ disabled: syncDeviceDarkMode }">
|
||||
<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')">
|
||||
<option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
|
||||
</optgroup>
|
||||
|
@ -11,6 +33,18 @@
|
|||
<option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
|
||||
</optgroup>
|
||||
</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 class="_content">
|
||||
<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 MkButton from '../../components/ui/button.vue';
|
||||
import MkSelect from '../../components/ui/select.vue';
|
||||
import MkSwitch from '../../components/ui/switch.vue';
|
||||
import i18n from '../../i18n';
|
||||
import { Theme, builtinThemes, applyTheme } from '../../theme';
|
||||
import { selectFile } from '../../scripts/select-file';
|
||||
|
@ -36,6 +71,7 @@ export default Vue.extend({
|
|||
MkInput,
|
||||
MkButton,
|
||||
MkSelect,
|
||||
MkSwitch,
|
||||
},
|
||||
|
||||
data() {
|
||||
|
@ -62,15 +98,38 @@ export default Vue.extend({
|
|||
return this.themes.filter(t => t.base == 'light' || t.kind == 'light');
|
||||
},
|
||||
|
||||
theme: {
|
||||
get() { return this.$store.state.device.theme; },
|
||||
set(value) { this.$store.commit('device/set', { key: 'theme', value }); }
|
||||
darkTheme: {
|
||||
get() { return this.$store.state.device.darkTheme; },
|
||||
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: {
|
||||
theme() {
|
||||
applyTheme(this.themes.find(x => x.id === this.theme));
|
||||
darkTheme() {
|
||||
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() {
|
||||
|
@ -92,3 +151,230 @@ export default Vue.extend({
|
|||
}
|
||||
});
|
||||
</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: [],
|
||||
recentEmojis: [],
|
||||
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,
|
||||
animatedMfm: true,
|
||||
imageNewTab: false,
|
||||
|
|
|
@ -44,7 +44,7 @@ export function applyTheme(theme: Theme, persist = true) {
|
|||
const _theme = JSON.parse(JSON.stringify(theme));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue