feat: instance default theme

This commit is contained in:
syuilo 2022-03-01 23:58:01 +09:00
parent 33d40e6736
commit f71aa3df32
10 changed files with 93 additions and 5 deletions

View File

@ -16,11 +16,12 @@ You should also include the user name that made the change.
このバージョンからNode v16.14.0以降が必要です このバージョンからNode v16.14.0以降が必要です
### Changes ### Changes
- ートの最大文字数を設定できる機能が廃止され、デフォルトで一律3000文字になりました - ートの最大文字数を設定できる機能が廃止され、デフォルトで一律3000文字になりました @syuilo
### Improvements ### Improvements
- プロフィールの追加情報を最大16まで保存できるように - インスタンスデフォルトテーマを設定できるように @syuilo
- 連合チャートにPub&Subを追加 - プロフィールの追加情報を最大16まで保存できるように @syuilo
- 連合チャートにPub&Subを追加 @syuilo
### Bugfixes ### Bugfixes
- Client: リアクションピッカーの高さが低くなったまま戻らないことがあるのを修正 @syuilo - Client: リアクションピッカーの高さが低くなったまま戻らないことがあるのを修正 @syuilo

View File

@ -831,6 +831,9 @@ themeColor: "テーマカラー"
size: "サイズ" size: "サイズ"
numberOfColumn: "列の数" numberOfColumn: "列の数"
searchByGoogle: "ググる" searchByGoogle: "ググる"
instanceDefaultLightTheme: "インスタンスデフォルトのライトテーマ"
instanceDefaultDarkTheme: "インスタンスデフォルトのダークテーマ"
instanceDefaultThemeDescription: "オブジェクト形式のテーマコードを記入します。"
_emailUnavailable: _emailUnavailable:
used: "既に使用されています" used: "既に使用されています"

View File

@ -0,0 +1,13 @@
export class instanceDefaultTheme1646143552768 {
name = 'instanceDefaultTheme1646143552768'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "defaultLightTheme" character varying(8192)`);
await queryRunner.query(`ALTER TABLE "meta" ADD "defaultDarkTheme" character varying(8192)`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "defaultDarkTheme"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "defaultLightTheme"`);
}
}

View File

@ -344,6 +344,20 @@ export class Meta {
}) })
public feedbackUrl: string | null; public feedbackUrl: string | null;
@Column('varchar', {
length: 8192,
default: null,
nullable: true,
})
public defaultLightTheme: string | null;
@Column('varchar', {
length: 8192,
default: null,
nullable: true,
})
public defaultDarkTheme: string | null;
@Column('boolean', { @Column('boolean', {
default: false, default: false,
}) })

View File

@ -36,6 +36,8 @@ export const paramDef = {
logoImageUrl: { type: 'string', nullable: true }, logoImageUrl: { type: 'string', nullable: true },
name: { type: 'string', nullable: true }, name: { type: 'string', nullable: true },
description: { type: 'string', nullable: true }, description: { type: 'string', nullable: true },
defaultLightTheme: { type: 'string', nullable: true },
defaultDarkTheme: { type: 'string', nullable: true },
localDriveCapacityMb: { type: 'integer' }, localDriveCapacityMb: { type: 'integer' },
remoteDriveCapacityMb: { type: 'integer' }, remoteDriveCapacityMb: { type: 'integer' },
cacheRemoteFiles: { type: 'boolean' }, cacheRemoteFiles: { type: 'boolean' },
@ -162,6 +164,14 @@ export default define(meta, paramDef, async (ps, me) => {
set.description = ps.description; set.description = ps.description;
} }
if (ps.defaultLightTheme !== undefined) {
set.defaultLightTheme = ps.defaultLightTheme;
}
if (ps.defaultDarkTheme !== undefined) {
set.defaultDarkTheme = ps.defaultDarkTheme;
}
if (ps.localDriveCapacityMb !== undefined) { if (ps.localDriveCapacityMb !== undefined) {
set.localDriveCapacityMb = ps.localDriveCapacityMb; set.localDriveCapacityMb = ps.localDriveCapacityMb;
} }

View File

@ -69,6 +69,14 @@ export const meta = {
optional: false, nullable: false, optional: false, nullable: false,
default: false, default: false,
}, },
defaultDarkTheme: {
type: 'string',
optional: false, nullable: true,
},
defaultLightTheme: {
type: 'string',
optional: false, nullable: true,
},
disableRegistration: { disableRegistration: {
type: 'boolean', type: 'boolean',
optional: false, nullable: false, optional: false, nullable: false,
@ -504,6 +512,8 @@ export default define(meta, paramDef, async (ps, me) => {
logoImageUrl: instance.logoImageUrl, logoImageUrl: instance.logoImageUrl,
maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互換性のため maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互換性のため
emojis: await Emojis.packMany(emojis), emojis: await Emojis.packMany(emojis),
defaultLightTheme: instance.defaultLightTheme,
defaultDarkTheme: instance.defaultDarkTheme,
ads: ads.map(ad => ({ ads: ads.map(ad => ({
id: ad.id, id: ad.id,
url: ad.url, url: ad.url,

View File

@ -15,6 +15,7 @@ if (localStorage.getItem('accounts') != null) {
import { computed, createApp, watch, markRaw, version as vueVersion } from 'vue'; import { computed, createApp, watch, markRaw, version as vueVersion } from 'vue';
import compareVersions from 'compare-versions'; import compareVersions from 'compare-versions';
import * as JSON5 from 'json5';
import widgets from '@/widgets'; import widgets from '@/widgets';
import directives from '@/directives'; import directives from '@/directives';
@ -159,7 +160,9 @@ if ($i && $i.token) {
} }
//#endregion //#endregion
fetchInstance().then(() => { const fetchInstanceMetaPromise = fetchInstance();
fetchInstanceMetaPromise.then(() => {
localStorage.setItem('v', instance.version); localStorage.setItem('v', instance.version);
// Init service worker // Init service worker
@ -267,6 +270,14 @@ window.matchMedia('(prefers-color-scheme: dark)').addListener(mql => {
}); });
//#endregion //#endregion
fetchInstanceMetaPromise.then(() => {
if (defaultStore.state.themeInitial) {
if (instance.defaultLightTheme != null) ColdDeviceStorage.set('lightTheme', JSON5.parse(instance.defaultLightTheme));
if (instance.defaultDarkTheme != null) ColdDeviceStorage.set('darkTheme', JSON5.parse(instance.defaultDarkTheme));
defaultStore.set('themeInitial', false);
}
});
// shortcut // shortcut
document.addEventListener('keydown', makeHotkey({ document.addEventListener('keydown', makeHotkey({
'd': () => { 'd': () => {

View File

@ -31,6 +31,16 @@
<template #caption>#RRGGBB</template> <template #caption>#RRGGBB</template>
</FormInput> </FormInput>
<FormTextarea v-model="defaultLightTheme" class="_formBlock">
<template #label>{{ $ts.instanceDefaultLightTheme }}</template>
<template #caption>{{ $ts.instanceDefaultThemeDescription }}</template>
</FormTextarea>
<FormTextarea v-model="defaultDarkTheme" class="_formBlock">
<template #label>{{ $ts.instanceDefaultDarkTheme }}</template>
<template #caption>{{ $ts.instanceDefaultThemeDescription }}</template>
</FormTextarea>
<FormInput v-model="tosUrl" class="_formBlock"> <FormInput v-model="tosUrl" class="_formBlock">
<template #prefix><i class="fas fa-link"></i></template> <template #prefix><i class="fas fa-link"></i></template>
<template #label>{{ $ts.tosUrl }}</template> <template #label>{{ $ts.tosUrl }}</template>
@ -176,6 +186,8 @@ export default defineComponent({
bannerUrl: null, bannerUrl: null,
backgroundImageUrl: null, backgroundImageUrl: null,
themeColor: null, themeColor: null,
defaultLightTheme: null,
defaultDarkTheme: null,
enableLocalTimeline: false, enableLocalTimeline: false,
enableGlobalTimeline: false, enableGlobalTimeline: false,
pinnedUsers: '', pinnedUsers: '',
@ -202,6 +214,8 @@ export default defineComponent({
this.bannerUrl = meta.bannerUrl; this.bannerUrl = meta.bannerUrl;
this.backgroundImageUrl = meta.backgroundImageUrl; this.backgroundImageUrl = meta.backgroundImageUrl;
this.themeColor = meta.themeColor; this.themeColor = meta.themeColor;
this.defaultLightTheme = meta.defaultLightTheme;
this.defaultDarkTheme = meta.defaultDarkTheme;
this.maintainerName = meta.maintainerName; this.maintainerName = meta.maintainerName;
this.maintainerEmail = meta.maintainerEmail; this.maintainerEmail = meta.maintainerEmail;
this.enableLocalTimeline = !meta.disableLocalTimeline; this.enableLocalTimeline = !meta.disableLocalTimeline;
@ -228,6 +242,8 @@ export default defineComponent({
bannerUrl: this.bannerUrl, bannerUrl: this.bannerUrl,
backgroundImageUrl: this.backgroundImageUrl, backgroundImageUrl: this.backgroundImageUrl,
themeColor: this.themeColor === '' ? null : this.themeColor, themeColor: this.themeColor === '' ? null : this.themeColor,
defaultLightTheme: this.defaultLightTheme === '' ? null : this.defaultLightTheme,
defaultDarkTheme: this.defaultDarkTheme === '' ? null : this.defaultDarkTheme,
maintainerName: this.maintainerName, maintainerName: this.maintainerName,
maintainerEmail: this.maintainerEmail, maintainerEmail: this.maintainerEmail,
disableLocalTimeline: !this.enableLocalTimeline, disableLocalTimeline: !this.enableLocalTimeline,

View File

@ -87,6 +87,7 @@
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, onActivated, onMounted, ref, watch } from 'vue'; import { computed, defineComponent, onActivated, onMounted, ref, watch } from 'vue';
import * as JSON5 from 'json5';
import FormSwitch from '@/components/form/switch.vue'; import FormSwitch from '@/components/form/switch.vue';
import FormSelect from '@/components/form/select.vue'; import FormSelect from '@/components/form/select.vue';
import FormGroup from '@/components/form/group.vue'; import FormGroup from '@/components/form/group.vue';
@ -99,6 +100,8 @@ import { isDeviceDarkmode } from '@/scripts/is-device-darkmode';
import { ColdDeviceStorage } from '@/store'; import { ColdDeviceStorage } from '@/store';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { defaultStore } from '@/store'; import { defaultStore } from '@/store';
import { instance } from '@/instance';
import { concat } from '@/scripts/array';
import { fetchThemes, getThemes } from '@/theme-store'; import { fetchThemes, getThemes } from '@/theme-store';
import * as symbols from '@/symbols'; import * as symbols from '@/symbols';
@ -122,7 +125,10 @@ export default defineComponent({
}; };
const installedThemes = ref(getThemes()); const installedThemes = ref(getThemes());
const themes = computed(() => builtinThemes.concat(installedThemes.value)); const instanceThemes = [];
if (instance.defaultLightTheme != null) instanceThemes.push(JSON5.parse(instance.defaultLightTheme));
if (instance.defaultDarkTheme != null) instanceThemes.push(JSON5.parse(instance.defaultDarkTheme));
const themes = computed(() => instanceThemes.concat(builtinThemes.concat(installedThemes.value)));
const darkThemes = computed(() => themes.value.filter(t => t.base === 'dark' || t.kind === 'dark')); const darkThemes = computed(() => themes.value.filter(t => t.base === 'dark' || t.kind === 'dark'));
const lightThemes = computed(() => themes.value.filter(t => t.base === 'light' || t.kind === 'light')); const lightThemes = computed(() => themes.value.filter(t => t.base === 'light' || t.kind === 'light'));
const darkTheme = ColdDeviceStorage.ref('darkTheme'); const darkTheme = ColdDeviceStorage.ref('darkTheme');

View File

@ -230,6 +230,10 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'device', where: 'device',
default: '' default: ''
}, },
themeInitial: {
where: 'device',
default: true,
},
aiChanMode: { aiChanMode: {
where: 'device', where: 'device',
default: false default: false