summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2023-05-02 09:36:40 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2023-05-02 09:36:40 +0900
commitd535ec21a27529709a9b11751c829527ef1c49b4 (patch)
tree771c93032d6f2b8e5c0bbc6541540f16fdeaced3
parentCreate MkColorInput.vue (diff)
downloadmisskey-d535ec21a27529709a9b11751c829527ef1c49b4.tar.gz
misskey-d535ec21a27529709a9b11751c829527ef1c49b4.tar.bz2
misskey-d535ec21a27529709a9b11751c829527ef1c49b4.zip
feat: チャンネルに色を設定できるように
-rw-r--r--CHANGELOG.md1
-rw-r--r--packages/backend/migration/1682985520254-channelColor.js11
-rw-r--r--packages/backend/src/core/entities/ChannelEntityService.ts1
-rw-r--r--packages/backend/src/core/entities/NoteEntityService.ts1
-rw-r--r--packages/backend/src/models/entities/Channel.ts6
-rw-r--r--packages/backend/src/models/json-schema/channel.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/channels/create.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/channels/update.ts2
-rw-r--r--packages/frontend/src/components/MkColorInput.vue2
-rw-r--r--packages/frontend/src/components/MkNote.vue24
-rw-r--r--packages/frontend/src/pages/channel-editor.vue8
11 files changed, 61 insertions, 1 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 43882432d6..82f848121f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@
(デスクトップ表示ではusernameの右側のボタンからも追加可能)
- アカウントの引っ越し(フォロワー引き継ぎ)に対応
* 一度引っ越したアカウントは利用に制限がかかります
+- チャンネルに色を設定できるようになりました。各ノートに設定した色のインジケーターが表示されます。
- ロールタイムラインをロールごとに表示するかどうかの選択できるようになりました。
* デフォルトがオフになるので、ロールタイムラインを表示する場合はオンにしてください。
- カスタム絵文字のライセンスを複数でセットできるようになりました。
diff --git a/packages/backend/migration/1682985520254-channelColor.js b/packages/backend/migration/1682985520254-channelColor.js
new file mode 100644
index 0000000000..294b7372b2
--- /dev/null
+++ b/packages/backend/migration/1682985520254-channelColor.js
@@ -0,0 +1,11 @@
+export class ChannelColor1682985520254 {
+ name = 'ChannelColor1682985520254'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "channel" ADD "color" character varying(16) NOT NULL DEFAULT '#86b300'`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "channel" DROP COLUMN "color"`);
+ }
+}
diff --git a/packages/backend/src/core/entities/ChannelEntityService.ts b/packages/backend/src/core/entities/ChannelEntityService.ts
index 987002606f..05eb836580 100644
--- a/packages/backend/src/core/entities/ChannelEntityService.ts
+++ b/packages/backend/src/core/entities/ChannelEntityService.ts
@@ -74,6 +74,7 @@ export class ChannelEntityService {
userId: channel.userId,
bannerUrl: banner ? this.driveFileEntityService.getPublicUrl(banner) : null,
pinnedNoteIds: channel.pinnedNoteIds,
+ color: channel.color,
usersCount: channel.usersCount,
notesCount: channel.notesCount,
diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts
index 26debd6adc..32269a4101 100644
--- a/packages/backend/src/core/entities/NoteEntityService.ts
+++ b/packages/backend/src/core/entities/NoteEntityService.ts
@@ -335,6 +335,7 @@ export class NoteEntityService implements OnModuleInit {
channel: channel ? {
id: channel.id,
name: channel.name,
+ color: channel.color,
} : undefined,
mentions: note.mentions.length > 0 ? note.mentions : undefined,
uri: note.uri ?? undefined,
diff --git a/packages/backend/src/models/entities/Channel.ts b/packages/backend/src/models/entities/Channel.ts
index 2d346fdf9d..ebbfc439ad 100644
--- a/packages/backend/src/models/entities/Channel.ts
+++ b/packages/backend/src/models/entities/Channel.ts
@@ -64,6 +64,12 @@ export class Channel {
})
public pinnedNoteIds: string[];
+ @Column('varchar', {
+ length: 16,
+ default: '#86b300',
+ })
+ public color: string;
+
@Index()
@Column('integer', {
default: 0,
diff --git a/packages/backend/src/models/json-schema/channel.ts b/packages/backend/src/models/json-schema/channel.ts
index 745b39a6ba..cb42c782b1 100644
--- a/packages/backend/src/models/json-schema/channel.ts
+++ b/packages/backend/src/models/json-schema/channel.ts
@@ -59,5 +59,9 @@ export const packedChannelSchema = {
format: 'id',
},
},
+ color: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
},
} as const;
diff --git a/packages/backend/src/server/api/endpoints/channels/create.ts b/packages/backend/src/server/api/endpoints/channels/create.ts
index 6294b08fa0..69e2f2504c 100644
--- a/packages/backend/src/server/api/endpoints/channels/create.ts
+++ b/packages/backend/src/server/api/endpoints/channels/create.ts
@@ -43,6 +43,7 @@ export const paramDef = {
name: { type: 'string', minLength: 1, maxLength: 128 },
description: { type: 'string', nullable: true, minLength: 1, maxLength: 2048 },
bannerId: { type: 'string', format: 'misskey:id', nullable: true },
+ color: { type: 'string', minLength: 1, maxLength: 16 },
},
required: ['name'],
} as const;
@@ -80,6 +81,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
name: ps.name,
description: ps.description ?? null,
bannerId: banner ? banner.id : null,
+ ...(ps.color !== undefined ? { color: ps.color } : {}),
} as Channel).then(x => this.channelsRepository.findOneByOrFail(x.identifiers[0]));
return await this.channelEntityService.pack(channel, me);
diff --git a/packages/backend/src/server/api/endpoints/channels/update.ts b/packages/backend/src/server/api/endpoints/channels/update.ts
index 084b3f919e..a4e38d429d 100644
--- a/packages/backend/src/server/api/endpoints/channels/update.ts
+++ b/packages/backend/src/server/api/endpoints/channels/update.ts
@@ -53,6 +53,7 @@ export const paramDef = {
type: 'string', format: 'misskey:id',
},
},
+ color: { type: 'string', minLength: 1, maxLength: 16 },
},
required: ['channelId'],
} as const;
@@ -104,6 +105,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
...(ps.name !== undefined ? { name: ps.name } : {}),
...(ps.description !== undefined ? { description: ps.description } : {}),
...(ps.pinnedNoteIds !== undefined ? { pinnedNoteIds: ps.pinnedNoteIds } : {}),
+ ...(ps.color !== undefined ? { color: ps.color } : {}),
...(banner ? { bannerId: banner.id } : {}),
});
diff --git a/packages/frontend/src/components/MkColorInput.vue b/packages/frontend/src/components/MkColorInput.vue
index a96e1edf7a..2471aa958d 100644
--- a/packages/frontend/src/components/MkColorInput.vue
+++ b/packages/frontend/src/components/MkColorInput.vue
@@ -1,7 +1,7 @@
<template>
<div>
<div :class="$style.label"><slot name="label"></slot></div>
- <div :class="[$style.input, { disabled, focused }]">
+ <div :class="[$style.input, { disabled }]">
<input
ref="inputEl"
v-model="v"
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index eba3c211a6..4273af51e6 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -12,6 +12,7 @@
<!--<div v-if="appearNote._prId_" class="tip"><i class="ti ti-speakerphone"></i> {{ i18n.ts.promotion }}<button class="_textButton hide" @click="readPromo()">{{ i18n.ts.hideThisNote }} <i class="ti ti-x"></i></button></div>-->
<!--<div v-if="appearNote._featuredId_" class="tip"><i class="ti ti-bolt"></i> {{ i18n.ts.featured }}</div>-->
<div v-if="isRenote" :class="$style.renote">
+ <div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div>
<MkAvatar :class="$style.renoteAvatar" :user="note.user" link preview/>
<i class="ti ti-repeat" style="margin-right: 4px;"></i>
<I18n :src="i18n.ts.renotedBy" tag="span" :class="$style.renoteText">
@@ -40,6 +41,7 @@
<Mfm :text="getNoteSummary(appearNote)" :plain="true" :nowrap="true" :author="appearNote.user" :class="$style.collapsedRenoteTargetText" @click="renoteCollapsed = false"/>
</div>
<article v-else :class="$style.article" @contextmenu.stop="onContextmenu">
+ <div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div>
<MkAvatar :class="$style.avatar" :user="appearNote.user" link preview/>
<div :class="$style.main">
<MkNoteHeader :class="$style.header" :note="appearNote" :mini="true"/>
@@ -546,6 +548,7 @@ function showReactions(): void {
}
.renote {
+ position: relative;
display: flex;
align-items: center;
padding: 16px 32px 8px 32px;
@@ -556,6 +559,10 @@ function showReactions(): void {
& + .article {
padding-top: 8px;
}
+
+ > .colorBar {
+ height: calc(100% - 6px);
+ }
}
.renoteAvatar {
@@ -627,6 +634,16 @@ function showReactions(): void {
padding: 28px 32px;
}
+.colorBar {
+ position: absolute;
+ top: 8px;
+ left: 8px;
+ width: 5px;
+ height: calc(100% - 16px);
+ border-radius: 999px;
+ pointer-events: none;
+}
+
.avatar {
flex-shrink: 0;
display: block !important;
@@ -842,6 +859,13 @@ function showReactions(): void {
}
}
}
+
+ .colorBar {
+ top: 6px;
+ left: 6px;
+ width: 4px;
+ height: calc(100% - 12px);
+ }
}
@container (max-width: 300px) {
diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue
index 9cb440d2bb..488738f31c 100644
--- a/packages/frontend/src/pages/channel-editor.vue
+++ b/packages/frontend/src/pages/channel-editor.vue
@@ -11,6 +11,10 @@
<template #label>{{ i18n.ts.description }}</template>
</MkTextarea>
+ <MkColorInput v-model="color">
+ <template #label>{{ i18n.ts.color }}</template>
+ </MkColorInput>
+
<div>
<MkButton v-if="bannerId == null" @click="setBannerImage"><i class="ti ti-plus"></i> {{ i18n.ts._channel.setBanner }}</MkButton>
<div v-else-if="bannerUrl">
@@ -55,6 +59,7 @@ import { computed, ref, watch, defineAsyncComponent } from 'vue';
import MkTextarea from '@/components/MkTextarea.vue';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
+import MkColorInput from '@/components/MkColorInput.vue';
import { selectFile } from '@/scripts/select-file';
import * as os from '@/os';
import { useRouter } from '@/router';
@@ -75,6 +80,7 @@ let name = $ref(null);
let description = $ref(null);
let bannerUrl = $ref<string | null>(null);
let bannerId = $ref<string | null>(null);
+let color = $ref(null);
const pinnedNotes = ref([]);
watch(() => bannerId, async () => {
@@ -101,6 +107,7 @@ async function fetchChannel() {
pinnedNotes.value = channel.pinnedNoteIds.map(id => ({
id,
}));
+ color = channel.color;
}
fetchChannel();
@@ -128,6 +135,7 @@ function save() {
description: description,
bannerId: bannerId,
pinnedNoteIds: pinnedNotes.value.map(x => x.id),
+ color: color,
};
if (props.channelId) {