2023-07-27 08:31:52 +03:00
|
|
|
<!--
|
|
|
|
SPDX-FileCopyrightText: syuilo and other misskey contributors
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
-->
|
|
|
|
|
2020-08-18 16:44:21 +03:00
|
|
|
<template>
|
2022-06-20 11:38:49 +03:00
|
|
|
<MkStickyContainer>
|
|
|
|
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
2023-05-19 10:20:53 +03:00
|
|
|
<MkSpacer :contentMax="700">
|
2023-04-09 02:22:49 +03:00
|
|
|
<div v-if="channelId == null || channel != null" class="_gaps_m">
|
2023-01-05 14:04:56 +02:00
|
|
|
<MkInput v-model="name">
|
2022-07-20 16:24:26 +03:00
|
|
|
<template #label>{{ i18n.ts.name }}</template>
|
2022-06-20 11:38:49 +03:00
|
|
|
</MkInput>
|
|
|
|
|
2023-12-14 06:11:23 +02:00
|
|
|
<MkTextarea v-model="description" mfmAutocomplete :mfmPreview="true">
|
2022-07-20 16:24:26 +03:00
|
|
|
<template #label>{{ i18n.ts.description }}</template>
|
2022-06-20 11:38:49 +03:00
|
|
|
</MkTextarea>
|
|
|
|
|
2023-05-02 03:36:40 +03:00
|
|
|
<MkColorInput v-model="color">
|
|
|
|
<template #label>{{ i18n.ts.color }}</template>
|
|
|
|
</MkColorInput>
|
|
|
|
|
2023-08-05 07:58:31 +03:00
|
|
|
<MkSwitch v-model="isSensitive">
|
|
|
|
<template #label>{{ i18n.ts.sensitive }}</template>
|
|
|
|
</MkSwitch>
|
|
|
|
|
2023-11-03 10:34:23 +02:00
|
|
|
<MkSwitch v-model="allowRenoteToExternal">
|
|
|
|
<template #label>{{ i18n.ts._channel.allowRenoteToExternal }}</template>
|
|
|
|
</MkSwitch>
|
|
|
|
|
2023-03-31 09:01:56 +03:00
|
|
|
<div>
|
2023-09-30 22:53:52 +03:00
|
|
|
<MkButton v-if="bannerId == null" @click="setBannerImage"><i class="ph-plus ph-bold ph-lg"></i> {{ i18n.ts._channel.setBanner }}</MkButton>
|
2022-06-20 11:38:49 +03:00
|
|
|
<div v-else-if="bannerUrl">
|
|
|
|
<img :src="bannerUrl" style="width: 100%;"/>
|
2023-09-30 22:53:52 +03:00
|
|
|
<MkButton @click="removeBannerImage()"><i class="ph-trash ph-bold ph-lg"></i> {{ i18n.ts._channel.removeBanner }}</MkButton>
|
2022-06-20 11:38:49 +03:00
|
|
|
</div>
|
|
|
|
</div>
|
2023-03-31 09:01:56 +03:00
|
|
|
|
2023-05-19 10:20:53 +03:00
|
|
|
<MkFolder :defaultOpen="true">
|
2023-03-31 09:01:56 +03:00
|
|
|
<template #label>{{ i18n.ts.pinnedNotes }}</template>
|
2023-07-08 01:08:16 +03:00
|
|
|
|
2023-03-31 09:01:56 +03:00
|
|
|
<div class="_gaps">
|
2023-09-30 22:53:52 +03:00
|
|
|
<MkButton primary rounded @click="addPinnedNote()"><i class="ph-plus ph-bold ph-lg"></i></MkButton>
|
2023-03-31 09:01:56 +03:00
|
|
|
|
2023-07-08 01:08:16 +03:00
|
|
|
<Sortable
|
2023-03-31 09:01:56 +03:00
|
|
|
v-model="pinnedNotes"
|
2023-05-19 10:20:53 +03:00
|
|
|
itemKey="id"
|
2023-03-31 09:01:56 +03:00
|
|
|
:handle="'.' + $style.pinnedNoteHandle"
|
|
|
|
:animation="150"
|
|
|
|
>
|
|
|
|
<template #item="{element,index}">
|
|
|
|
<div :class="$style.pinnedNote">
|
2023-10-01 01:46:42 +03:00
|
|
|
<button class="_button" :class="$style.pinnedNoteHandle"><i class="ph-list ph-bold ph-lg"></i></button>
|
2023-03-31 09:01:56 +03:00
|
|
|
{{ element.id }}
|
2023-09-30 22:53:52 +03:00
|
|
|
<button class="_button" :class="$style.pinnedNoteRemove" @click="removePinnedNote(index)"><i class="ph-x ph-bold ph-lg"></i></button>
|
2023-03-31 09:01:56 +03:00
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</Sortable>
|
|
|
|
</div>
|
|
|
|
</MkFolder>
|
|
|
|
|
2023-05-06 02:15:17 +03:00
|
|
|
<div class="_buttons">
|
2023-11-04 00:20:53 +02:00
|
|
|
<MkButton primary @click="save()"><i class="ph-floppy-disk ph-bold ph-lg"></i> {{ channelId ? i18n.ts.save : i18n.ts.create }}</MkButton>
|
2023-09-30 22:53:52 +03:00
|
|
|
<MkButton v-if="channelId" danger @click="archive()"><i class="ph-trash ph-bold ph-lg"></i> {{ i18n.ts.archive }}</MkButton>
|
2020-08-18 16:44:21 +03:00
|
|
|
</div>
|
|
|
|
</div>
|
2022-06-20 11:38:49 +03:00
|
|
|
</MkSpacer>
|
|
|
|
</MkStickyContainer>
|
2020-08-18 16:44:21 +03:00
|
|
|
</template>
|
|
|
|
|
2022-06-20 11:38:49 +03:00
|
|
|
<script lang="ts" setup>
|
2023-03-31 09:01:56 +03:00
|
|
|
import { computed, ref, watch, defineAsyncComponent } from 'vue';
|
2023-12-26 07:19:35 +02:00
|
|
|
import * as Misskey from 'misskey-js';
|
2022-09-06 12:21:49 +03:00
|
|
|
import MkButton from '@/components/MkButton.vue';
|
2023-01-07 08:09:46 +02:00
|
|
|
import MkInput from '@/components/MkInput.vue';
|
2023-05-02 03:36:40 +03:00
|
|
|
import MkColorInput from '@/components/MkColorInput.vue';
|
2023-09-19 10:37:43 +03:00
|
|
|
import { selectFile } from '@/scripts/select-file.js';
|
|
|
|
import * as os from '@/os.js';
|
|
|
|
import { useRouter } from '@/router.js';
|
|
|
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
|
|
|
import { i18n } from '@/i18n.js';
|
2023-03-31 09:01:56 +03:00
|
|
|
import MkFolder from '@/components/MkFolder.vue';
|
2023-11-03 10:34:23 +02:00
|
|
|
import MkSwitch from '@/components/MkSwitch.vue';
|
2023-12-14 06:11:23 +02:00
|
|
|
import MkTextarea from '@/components/MkTextarea.vue';
|
2023-03-31 09:01:56 +03:00
|
|
|
|
|
|
|
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
|
2022-06-20 11:38:49 +03:00
|
|
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
|
|
|
const props = defineProps<{
|
|
|
|
channelId?: string;
|
|
|
|
}>();
|
|
|
|
|
2023-12-26 07:19:35 +02:00
|
|
|
const channel = ref<Misskey.entities.Channel | null>(null);
|
|
|
|
const name = ref<string | null>(null);
|
|
|
|
const description = ref<string | null>(null);
|
2023-12-07 07:42:09 +02:00
|
|
|
const bannerUrl = ref<string | null>(null);
|
|
|
|
const bannerId = ref<string | null>(null);
|
|
|
|
const color = ref('#000');
|
|
|
|
const isSensitive = ref(false);
|
|
|
|
const allowRenoteToExternal = ref(true);
|
2023-12-27 13:35:52 +02:00
|
|
|
const pinnedNotes = ref<{ id: Misskey.entities.Note['id'] }[]>([]);
|
2022-06-20 11:38:49 +03:00
|
|
|
|
2023-12-07 07:42:09 +02:00
|
|
|
watch(() => bannerId.value, async () => {
|
|
|
|
if (bannerId.value == null) {
|
|
|
|
bannerUrl.value = null;
|
2022-06-20 11:38:49 +03:00
|
|
|
} else {
|
2023-12-07 07:42:09 +02:00
|
|
|
bannerUrl.value = (await os.api('drive/files/show', {
|
|
|
|
fileId: bannerId.value,
|
2022-06-20 11:38:49 +03:00
|
|
|
})).url;
|
2020-08-18 16:44:21 +03:00
|
|
|
}
|
|
|
|
});
|
2022-06-20 11:38:49 +03:00
|
|
|
|
|
|
|
async function fetchChannel() {
|
|
|
|
if (props.channelId == null) return;
|
|
|
|
|
2023-12-07 07:42:09 +02:00
|
|
|
channel.value = await os.api('channels/show', {
|
2022-06-20 11:38:49 +03:00
|
|
|
channelId: props.channelId,
|
|
|
|
});
|
|
|
|
|
2023-12-07 07:42:09 +02:00
|
|
|
name.value = channel.value.name;
|
|
|
|
description.value = channel.value.description;
|
|
|
|
bannerId.value = channel.value.bannerId;
|
|
|
|
bannerUrl.value = channel.value.bannerUrl;
|
|
|
|
isSensitive.value = channel.value.isSensitive;
|
|
|
|
pinnedNotes.value = channel.value.pinnedNoteIds.map(id => ({
|
2023-03-31 09:01:56 +03:00
|
|
|
id,
|
|
|
|
}));
|
2023-12-07 07:42:09 +02:00
|
|
|
color.value = channel.value.color;
|
|
|
|
allowRenoteToExternal.value = channel.value.allowRenoteToExternal;
|
2022-06-20 11:38:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fetchChannel();
|
|
|
|
|
2023-03-31 09:01:56 +03:00
|
|
|
async function addPinnedNote() {
|
|
|
|
const { canceled, result: value } = await os.inputText({
|
|
|
|
title: i18n.ts.noteIdOrUrl,
|
|
|
|
});
|
|
|
|
if (canceled) return;
|
|
|
|
const note = await os.apiWithDialog('notes/show', {
|
|
|
|
noteId: value.includes('/') ? value.split('/').pop() : value,
|
|
|
|
});
|
|
|
|
pinnedNotes.value = [{
|
|
|
|
id: note.id,
|
|
|
|
}, ...pinnedNotes.value];
|
|
|
|
}
|
|
|
|
|
|
|
|
function removePinnedNote(index: number) {
|
|
|
|
pinnedNotes.value.splice(index, 1);
|
|
|
|
}
|
|
|
|
|
2022-06-20 11:38:49 +03:00
|
|
|
function save() {
|
|
|
|
const params = {
|
2023-12-07 07:42:09 +02:00
|
|
|
name: name.value,
|
|
|
|
description: description.value,
|
|
|
|
bannerId: bannerId.value,
|
2023-03-31 09:01:56 +03:00
|
|
|
pinnedNoteIds: pinnedNotes.value.map(x => x.id),
|
2023-12-07 07:42:09 +02:00
|
|
|
color: color.value,
|
|
|
|
isSensitive: isSensitive.value,
|
|
|
|
allowRenoteToExternal: allowRenoteToExternal.value,
|
2022-06-20 11:38:49 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
if (props.channelId) {
|
|
|
|
params.channelId = props.channelId;
|
2023-10-29 04:10:01 +02:00
|
|
|
os.apiWithDialog('channels/update', params);
|
2022-06-20 11:38:49 +03:00
|
|
|
} else {
|
2023-10-29 04:10:01 +02:00
|
|
|
os.apiWithDialog('channels/create', params).then(created => {
|
2022-06-20 11:38:49 +03:00
|
|
|
router.push(`/channels/${created.id}`);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-06 02:15:17 +03:00
|
|
|
async function archive() {
|
|
|
|
const { canceled } = await os.confirm({
|
|
|
|
type: 'warning',
|
2023-12-07 07:42:09 +02:00
|
|
|
title: i18n.t('channelArchiveConfirmTitle', { name: name.value }),
|
2023-05-06 02:15:17 +03:00
|
|
|
text: i18n.ts.channelArchiveConfirmDescription,
|
|
|
|
});
|
|
|
|
|
|
|
|
if (canceled) return;
|
2023-07-08 01:08:16 +03:00
|
|
|
|
2023-05-06 02:15:17 +03:00
|
|
|
os.api('channels/update', {
|
|
|
|
channelId: props.channelId,
|
|
|
|
isArchived: true,
|
|
|
|
}).then(() => {
|
|
|
|
os.success();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-06-20 11:38:49 +03:00
|
|
|
function setBannerImage(evt) {
|
|
|
|
selectFile(evt.currentTarget ?? evt.target, null).then(file => {
|
2023-12-07 07:42:09 +02:00
|
|
|
bannerId.value = file.id;
|
2022-06-20 11:38:49 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function removeBannerImage() {
|
2023-12-07 07:42:09 +02:00
|
|
|
bannerId.value = null;
|
2022-06-20 11:38:49 +03:00
|
|
|
}
|
|
|
|
|
2023-12-07 07:42:09 +02:00
|
|
|
const headerActions = computed(() => []);
|
2022-06-20 11:38:49 +03:00
|
|
|
|
2023-12-07 07:42:09 +02:00
|
|
|
const headerTabs = computed(() => []);
|
2022-06-20 11:38:49 +03:00
|
|
|
|
|
|
|
definePageMetadata(computed(() => props.channelId ? {
|
|
|
|
title: i18n.ts._channel.edit,
|
2023-09-30 22:53:52 +03:00
|
|
|
icon: 'ph-television ph-bold ph-lg',
|
2022-06-20 11:38:49 +03:00
|
|
|
} : {
|
|
|
|
title: i18n.ts._channel.create,
|
2023-09-30 22:53:52 +03:00
|
|
|
icon: 'ph-television ph-bold ph-lg',
|
2022-06-20 11:38:49 +03:00
|
|
|
}));
|
2020-08-18 16:44:21 +03:00
|
|
|
</script>
|
|
|
|
|
2023-03-31 09:01:56 +03:00
|
|
|
<style lang="scss" module>
|
|
|
|
.pinnedNote {
|
|
|
|
position: relative;
|
|
|
|
display: block;
|
|
|
|
line-height: 2.85rem;
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
overflow: hidden;
|
|
|
|
white-space: nowrap;
|
|
|
|
color: var(--navFg);
|
|
|
|
}
|
2020-08-18 16:44:21 +03:00
|
|
|
|
2023-03-31 09:01:56 +03:00
|
|
|
.pinnedNoteRemove {
|
|
|
|
position: absolute;
|
|
|
|
z-index: 10000;
|
|
|
|
width: 32px;
|
|
|
|
height: 32px;
|
|
|
|
color: #ff2a2a;
|
|
|
|
right: 8px;
|
|
|
|
opacity: 0.8;
|
|
|
|
}
|
|
|
|
|
|
|
|
.pinnedNoteHandle {
|
|
|
|
cursor: move;
|
|
|
|
width: 32px;
|
|
|
|
height: 32px;
|
|
|
|
margin: 0 8px;
|
|
|
|
opacity: 0.5;
|
|
|
|
}
|
2020-08-18 16:44:21 +03:00
|
|
|
</style>
|