diff --git a/locales/index.d.ts b/locales/index.d.ts
index f7f952175f..3537d76840 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -8173,9 +8173,9 @@ export interface Locale extends ILocale {
* 指定したユーザーのみに公開
*/
"specifiedDescription": string;
- /**
- * 連合なし
- */
+ "channel": string;
+ "channelDescription": string;
+ "channelSelected": string;
"disableFederation": string;
/**
* 他サーバーへの配信を行いません
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 6c8a453023..c3e50c7146 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -2153,6 +2153,9 @@ _visibility:
followersDescription: "自分のフォロワーのみに公開"
specified: "ダイレクト"
specifiedDescription: "指定したユーザーのみに公開"
+ channel: "チャンネル"
+ channelDescription: "選択したチャンネルに公開"
+ channelSelected: "選択中:{name}"
disableFederation: "連合なし"
disableFederationDescription: "他サーバーへの配信を行いません"
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 1e073a7de9..3df986c815 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -19,21 +19,21 @@ SPDX-License-Identifier: AGPL-3.0-only
-
-
-
-
-
+ {{ postChannelName }}
+
+
+
+
+
+
+ {{ i18n.ts._visibility[actualVisibility] }}
+
+
+
+
@@ -54,7 +54,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.quoteAttached }}
-
+
{{ i18n.ts.recipient }}
@@ -67,7 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.notSpecifiedMentionWarning }} - {{ i18n.ts.add }}
-
+
{{ maxTextLength - textLength }}
@@ -137,7 +137,7 @@ const modal = inject('modal');
const props = withDefaults(defineProps<{
reply?: Misskey.entities.Note;
renote?: Misskey.entities.Note;
- channel?: Misskey.entities.Channel; // TODO
+ channel?: Misskey.entities.Channel;
mention?: Misskey.entities.User;
specified?: Misskey.entities.User;
initialText?: string;
@@ -206,8 +206,22 @@ const imeText = ref('');
const showingOptions = ref(false);
const textAreaReadOnly = ref(false);
+const postChannel = ref(props.channel ?? null);
+const postChannelName = computed(() => postChannel.value?.name ?? '');
+
+/**
+ * {@link localOnly}が持つ値にチャンネル選択有無を加味した値を計算する(チャンネル選択時は強制的にfalse)
+ * チャンネル選択有無を考慮する必要がある場面では{@link localOnly}ではなくこの値を使用する。
+ */
+const actualLocalOnly = computed(() => postChannel.value ? true : localOnly.value);
+/**
+ * {@link visibility}が持つ値にチャンネル選択有無を加味した値を計算する(チャンネル選択時は強制的にpublic)。
+ * チャンネル選択有無を考慮する必要がある場面では{@link actualVisibility}ではなくこの値を使用する。
+ */
+const actualVisibility = computed(() => postChannel.value ? 'public' : visibility.value);
+
const draftKey = computed((): string => {
- let key = props.channel ? `channel:${props.channel.id}` : '';
+ let key = postChannel.value ? `channel:${postChannel.value.id}` : '';
if (props.renote) {
key += `renote:${props.renote.id}`;
@@ -225,7 +239,7 @@ const placeholder = computed((): string => {
return i18n.ts._postForm.quotePlaceholder;
} else if (props.reply) {
return i18n.ts._postForm.replyPlaceholder;
- } else if (props.channel) {
+ } else if (postChannel.value) {
return i18n.ts._postForm.channelPlaceholder;
} else {
const xs = [
@@ -270,7 +284,7 @@ watch(text, () => {
checkMissingMention();
}, { immediate: true });
-watch(visibility, () => {
+watch(actualVisibility, () => {
checkMissingMention();
}, { immediate: true });
@@ -314,11 +328,6 @@ if ($i.isSilenced && visibility.value === 'public') {
visibility.value = 'home';
}
-if (props.channel) {
- visibility.value = 'public';
- localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す
-}
-
// 公開以外へのリプライ時は元の公開範囲を引き継ぐ
if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visibility)) {
if (props.reply.visibility === 'home' && visibility.value === 'followers') {
@@ -368,7 +377,7 @@ function watchForDraft() {
}
function checkMissingMention() {
- if (visibility.value === 'specified') {
+ if (actualVisibility.value === 'specified') {
const ast = mfm.parse(text.value);
for (const x of extractMentions(ast)) {
@@ -455,34 +464,29 @@ function upload(file: File, name?: string): void {
}
function setVisibility() {
- if (props.channel) {
- visibility.value = 'public';
- localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す
- return;
- }
-
os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), {
- currentVisibility: visibility.value,
+ currentVisibility: actualVisibility.value,
isSilenced: $i.isSilenced,
- localOnly: localOnly.value,
+ // チャンネル→ダイレクトの選択変更ができなくなるので、チャンネル選択中の場合は意図的にfalseを渡すようにする
+ localOnly: postChannel.value ? false : actualLocalOnly.value,
src: visibilityButton.value,
+ currentChannel: postChannel.value,
}, {
changeVisibility: v => {
visibility.value = v;
+ postChannel.value = null;
if (defaultStore.state.rememberNoteVisibility) {
defaultStore.set('visibility', visibility.value);
}
},
+ changeChannel: channel => {
+ // computedで読み替えをするので、localOnlyとvisibilityの変更はしない
+ postChannel.value = channel;
+ },
}, 'closed');
}
async function toggleLocalOnly() {
- if (props.channel) {
- visibility.value = 'public';
- localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す
- return;
- }
-
const neverShowInfo = miLocalStorage.getItem('neverShowLocalOnlyInfo');
if (!localOnly.value && neverShowInfo !== 'true') {
@@ -715,7 +719,8 @@ async function post(ev?: MouseEvent) {
text.value.includes('$[scale') ||
text.value.includes('$[position');
- if (annoying && visibility.value === 'public') {
+ // チャンネル投稿時はサーバサイド側でpublicに変更されているため警告を出す意味がない
+ if (annoying && actualVisibility.value === 'public' && !postChannel.value) {
const { canceled, result } = await os.actions({
type: 'warning',
text: i18n.ts.thisPostMayBeAnnoying,
@@ -744,12 +749,12 @@ async function post(ev?: MouseEvent) {
fileIds: files.value.length > 0 ? files.value.map(f => f.id) : undefined,
replyId: props.reply ? props.reply.id : undefined,
renoteId: props.renote ? props.renote.id : quoteId.value ? quoteId.value : undefined,
- channelId: props.channel ? props.channel.id : undefined,
+ channelId: postChannel.value ? postChannel.value.id : undefined,
poll: poll.value,
cw: useCw.value ? cw.value ?? '' : null,
- localOnly: localOnly.value,
- visibility: visibility.value,
- visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(u => u.id) : undefined,
+ localOnly: actualLocalOnly.value,
+ visibility: actualVisibility.value,
+ visibleUserIds: actualVisibility.value === 'specified' ? visibleUsers.value.map(u => u.id) : undefined,
reactionAcceptance: reactionAcceptance.value,
};
diff --git a/packages/frontend/src/components/MkVisibilityPicker.vue b/packages/frontend/src/components/MkVisibilityPicker.vue
index 1324ed12e1..515bcd1530 100644
--- a/packages/frontend/src/components/MkVisibilityPicker.vue
+++ b/packages/frontend/src/components/MkVisibilityPicker.vue
@@ -9,61 +9,117 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.visibility }}
-
+
{{ i18n.ts._visibility.public }}
{{ i18n.ts._visibility.publicDescription }}
-
+
{{ i18n.ts._visibility.home }}
{{ i18n.ts._visibility.homeDescription }}
-
+
{{ i18n.ts._visibility.followers }}
{{ i18n.ts._visibility.followersDescription }}
-
+
{{ i18n.ts._visibility.specified }}
{{ i18n.ts._visibility.specifiedDescription }}
+
+
+
+
+
+
+ {{ i18n.ts._visibility.channel }}
+
+ {{ i18n.t('_visibility.channelSelected', { name: currentChannelName }) }}
+ {{ i18n.ts._visibility.channelDescription }}
+
+
+
+