Merge branch 'develop' into feature/2024.10
This commit is contained in:
commit
6d4ae93592
54 changed files with 1630 additions and 20 deletions
|
|
@ -195,6 +195,12 @@ onMounted(() => {
|
|||
|
||||
textBox.textContent = pswp.currSlide?.data.comment;
|
||||
});
|
||||
|
||||
// `passive: true` is for Safari compatibility, apparently
|
||||
const stopEvent = name => textBox.addEventListener(name, event => event.stopPropagation(), { passive: true });
|
||||
stopEvent('wheel');
|
||||
stopEvent('pointerdown');
|
||||
stopEvent('pointercancel');
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</MkA>:
|
||||
<Mfm :text="getNoteSummary(appearNote.reply)" :plain="true" :nowrap="true" :author="appearNote.reply.user" :nyaize="'respect'" :class="$style.collapsedInReplyToText" @click="inReplyToCollapsed = false"/>
|
||||
</div>
|
||||
<MkNoteSub v-if="appearNote.reply && !renoteCollapsed && !inReplyToCollapsed" :note="appearNote.reply" :class="$style.replyTo"/>
|
||||
<MkNoteSub v-if="appearNote.reply" v-show="!renoteCollapsed && !inReplyToCollapsed" :note="appearNote.reply" :class="$style.replyTo"/>
|
||||
<div v-if="pinned" :class="$style.tip"><i class="ti ti-pin"></i> {{ i18n.ts.pinnedNote }}</div>
|
||||
<!--<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>-->
|
||||
|
|
|
|||
|
|
@ -46,7 +46,10 @@ import { popupMenu } from '@/os.js';
|
|||
import { defaultStore } from '@/store.js';
|
||||
|
||||
const props = defineProps<{
|
||||
note: Misskey.entities.Note;
|
||||
note: Misskey.entities.Note & {
|
||||
isSchedule?: boolean
|
||||
};
|
||||
scheduled?: boolean;
|
||||
}>();
|
||||
|
||||
const menuVersionsButton = shallowRef<HTMLElement>();
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
-->
|
||||
|
||||
<template>
|
||||
<div :class="$style.root">
|
||||
<div v-if="!isDeleted" :class="$style.root">
|
||||
<MkAvatar :class="$style.avatar" :user="note.user" link preview/>
|
||||
<div :class="$style.main">
|
||||
<MkNoteHeader :class="$style.header" :note="note" :mini="true"/>
|
||||
|
|
@ -15,6 +15,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</p>
|
||||
<div v-show="note.cw == null || showContent">
|
||||
<MkSubNoteContent :hideFiles="hideFiles" :class="$style.text" :note="note" :expandAllCws="props.expandAllCws"/>
|
||||
<div v-if="note.isSchedule" style="margin-top: 10px;">
|
||||
<MkButton :class="$style.button" inline @click.stop.prevent="editScheduleNote()"><i class="ti ti-eraser"></i> {{ i18n.ts.deleteAndEdit }}</MkButton>
|
||||
<MkButton :class="$style.button" inline danger @click.stop.prevent="deleteScheduleNote()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -24,18 +28,58 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import * as os from '@/os.js';
|
||||
import MkNoteHeader from '@/components/MkNoteHeader.vue';
|
||||
import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
|
||||
import MkCwButton from '@/components/MkCwButton.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import { defaultStore } from '@/store.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
|
||||
const props = defineProps<{
|
||||
note: Misskey.entities.Note;
|
||||
note: Misskey.entities.Note & {
|
||||
isSchedule? : boolean,
|
||||
scheduledNoteId?: string
|
||||
};
|
||||
expandAllCws?: boolean;
|
||||
hideFiles?: boolean;
|
||||
}>();
|
||||
|
||||
let showContent = ref(defaultStore.state.uncollapseCW);
|
||||
const isDeleted = ref(false);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'editScheduleNote'): void;
|
||||
}>();
|
||||
|
||||
async function deleteScheduleNote() {
|
||||
const { canceled } = await os.confirm({
|
||||
type: 'warning',
|
||||
text: i18n.ts.deleteConfirm,
|
||||
okText: i18n.ts.delete,
|
||||
cancelText: i18n.ts.cancel,
|
||||
});
|
||||
if (canceled) return;
|
||||
await os.apiWithDialog('notes/schedule/delete', { noteId: props.note.id })
|
||||
.then(() => {
|
||||
isDeleted.value = true;
|
||||
});
|
||||
}
|
||||
|
||||
async function editScheduleNote() {
|
||||
await os.apiWithDialog('notes/schedule/delete', { noteId: props.note.id })
|
||||
.then(() => {
|
||||
isDeleted.value = true;
|
||||
});
|
||||
|
||||
await os.post({
|
||||
initialNote: props.note,
|
||||
renote: props.note.renote,
|
||||
reply: props.note.reply,
|
||||
channel: props.note.channel,
|
||||
});
|
||||
emit('editScheduleNote');
|
||||
}
|
||||
|
||||
watch(() => props.expandAllCws, (expandAllCws) => {
|
||||
if (expandAllCws !== showContent.value) showContent.value = expandAllCws;
|
||||
|
|
@ -50,6 +94,11 @@ watch(() => props.expandAllCws, (expandAllCws) => {
|
|||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
.button{
|
||||
margin-right: var(--margin);
|
||||
margin-bottom: var(--margin);
|
||||
}
|
||||
|
||||
.avatar {
|
||||
flex-shrink: 0;
|
||||
display: block;
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<template>
|
||||
<div :class="$style.root">
|
||||
<div :class="$style.head">
|
||||
<MkAvatar v-if="['pollEnded', 'note', 'edited'].includes(notification.type) && 'note' in notification" :class="$style.icon" :user="notification.note.user" link preview/>
|
||||
<MkAvatar v-else-if="['roleAssigned', 'achievementEarned', 'exportCompleted', 'login'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/>
|
||||
<MkAvatar v-if="['pollEnded', 'note', 'edited', 'scheduledNotePosted'].includes(notification.type) && 'note' in notification" :class="$style.icon" :user="notification.note.user" link preview/>
|
||||
<MkAvatar v-else-if="['roleAssigned', 'achievementEarned', 'exportCompleted', 'login', 'scheduledNoteFailed'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/>
|
||||
<div v-else-if="notification.type === 'reaction:grouped' && notification.note.reactionAcceptance === 'likeOnly'" :class="[$style.icon, $style.icon_reactionGroupHeart]"><i class="ph-smiley ph-bold ph-lg" style="line-height: 1;"></i></div>
|
||||
<div v-else-if="notification.type === 'reaction:grouped'" :class="[$style.icon, $style.icon_reactionGroup]"><i class="ph-smiley ph-bold ph-lg" style="line-height: 1;"></i></div>
|
||||
<div v-else-if="notification.type === 'renote:grouped'" :class="[$style.icon, $style.icon_renoteGroup]"><i class="ti ti-repeat" style="line-height: 1;"></i></div>
|
||||
|
|
@ -29,6 +29,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
[$style.t_login]: notification.type === 'login',
|
||||
[$style.t_roleAssigned]: notification.type === 'roleAssigned' && notification.role.iconUrl == null,
|
||||
[$style.t_pollEnded]: notification.type === 'edited',
|
||||
[$style.t_roleAssigned]: notification.type === 'scheduledNoteFailed',
|
||||
[$style.t_pollEnded]: notification.type === 'scheduledNotePosted',
|
||||
}]"
|
||||
> <!-- we re-use t_pollEnded for "edited" instead of making an identical style -->
|
||||
<i v-if="notification.type === 'follow'" class="ti ti-plus"></i>
|
||||
|
|
@ -47,6 +49,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<i v-else class="ti ti-badges"></i>
|
||||
</template>
|
||||
<i v-else-if="notification.type === 'edited'" class="ph-pencil ph-bold ph-lg"></i>
|
||||
<i v-else-if="notification.type === 'scheduledNoteFailed'" class="ti ti-calendar-event"></i>
|
||||
<i v-else-if="notification.type === 'scheduledNotePosted'" class="ti ti-calendar-event"></i>
|
||||
<!-- notification.reaction が null になることはまずないが、ここでoptional chaining使うと一部ブラウザで刺さるので念の為 -->
|
||||
<MkReactionIcon
|
||||
v-else-if="notification.type === 'reaction'"
|
||||
|
|
@ -72,6 +76,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<span v-else-if="notification.type === 'renote:grouped'">{{ i18n.tsx._notification.renotedBySomeUsers({ n: notification.users.length }) }}</span>
|
||||
<span v-else-if="notification.type === 'app'">{{ notification.header }}</span>
|
||||
<span v-else-if="notification.type === 'edited'">{{ i18n.ts._notification.edited }}</span>
|
||||
<span v-else-if="notification.type === 'scheduledNoteFailed'">{{ i18n.ts._notification.scheduledNoteFailed }}</span>
|
||||
<span v-else-if="notification.type === 'scheduledNotePosted'">{{ i18n.ts._notification.scheduledNotePosted }}</span>
|
||||
<MkTime v-if="withTime" :time="notification.createdAt" :class="$style.headerTime"/>
|
||||
</header>
|
||||
<div>
|
||||
|
|
@ -111,6 +117,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkA v-else-if="notification.type === 'exportCompleted'" :class="$style.text" :to="`/my/drive/file/${notification.fileId}`">
|
||||
{{ i18n.ts.showFile }}
|
||||
</MkA>
|
||||
<div v-else-if="notification.type === 'scheduledNoteFailed'" :class="$style.text">
|
||||
{{ notification.reason }}
|
||||
</div>
|
||||
<template v-else-if="notification.type === 'follow'">
|
||||
<span :class="$style.text" style="opacity: 0.6;">{{ i18n.ts.youGotNewFollower }}</span>
|
||||
</template>
|
||||
|
|
@ -158,6 +167,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<Mfm :text="getNoteSummary(notification.note)" :isBlock="true" :plain="true" :nowrap="true" :author="notification.note.user"/>
|
||||
<i class="ph-quotes ph-bold ph-lg" :class="$style.quote"></i>
|
||||
</MkA>
|
||||
|
||||
<MkA v-else-if="notification.type === 'scheduledNotePosted'" :class="$style.text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
|
||||
<i class="ph-quotes ph-bold ph-lg" :class="$style.quote"></i>
|
||||
<Mfm :text="getNoteSummary(notification.note)" :isBlock="true" :plain="true" :nowrap="true" :author="notification.note.user"/>
|
||||
<i class="ph-quotes ph-bold ph-lg" :class="$style.quote"></i>
|
||||
</MkA>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" :class="$style.hashtags" :placeholder="i18n.ts.hashtags" list="hashtags">
|
||||
<XPostFormAttaches v-model="files" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName" @replaceFile="replaceFile"/>
|
||||
<MkPollEditor v-if="poll" v-model="poll" @destroyed="poll = null"/>
|
||||
<MkScheduleEditor v-if="scheduleNote" v-model="scheduleNote" @destroyed="scheduleNote = null"/>
|
||||
<MkNotePreview v-if="showPreview" :class="$style.preview" :text="text" :files="files" :poll="poll ?? undefined" :useCw="useCw" :cw="cw" :user="postAccount ?? $i"/>
|
||||
<div v-if="showingOptions" style="padding: 8px 16px;">
|
||||
</div>
|
||||
|
|
@ -90,6 +91,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<button v-if="postFormActions.length > 0" v-tooltip="i18n.ts.plugins" class="_button" :class="$style.footerButton" @click="showActions"><i class="ti ti-plug"></i></button>
|
||||
<button v-tooltip="i18n.ts.emoji" :class="['_button', $style.footerButton]" @click="insertEmoji"><i class="ti ti-mood-happy"></i></button>
|
||||
<button v-if="showAddMfmFunction" v-tooltip="i18n.ts.addMfmFunction" :class="['_button', $style.footerButton]" @click="insertMfmFunction"><i class="ti ti-palette"></i></button>
|
||||
<button v-tooltip="i18n.ts.otherSettings" :class="['_button', $style.footerButton]" @click="showOtherMenu"><i class="ti ti-dots"></i></button>
|
||||
</div>
|
||||
<div :class="$style.footerRight">
|
||||
<button v-tooltip="i18n.ts.previewNoteText" class="_button" :class="[$style.footerButton, { [$style.previewButtonActive]: showPreview }]" @click="showPreview = !showPreview"><i class="ti ti-eye"></i></button>
|
||||
|
|
@ -110,6 +112,7 @@ import * as Misskey from 'misskey-js';
|
|||
import insertTextAtCursor from 'insert-text-at-cursor';
|
||||
import { toASCII } from 'punycode/';
|
||||
import { host, url } from '@@/js/config.js';
|
||||
import type { MenuItem } from '@/types/menu.js';
|
||||
import MkNoteSimple from '@/components/MkNoteSimple.vue';
|
||||
import MkNotePreview from '@/components/MkNotePreview.vue';
|
||||
import XPostFormAttaches from '@/components/MkPostFormAttaches.vue';
|
||||
|
|
@ -134,6 +137,7 @@ import { claimAchievement } from '@/scripts/achievements.js';
|
|||
import { emojiPicker } from '@/scripts/emoji-picker.js';
|
||||
import { mfmFunctionPicker } from '@/scripts/mfm-function-picker.js';
|
||||
import type { PostFormProps } from '@/types/post-form.js';
|
||||
import MkScheduleEditor from '@/components/MkScheduleEditor.vue';
|
||||
|
||||
const $i = signinRequired();
|
||||
|
||||
|
|
@ -195,6 +199,9 @@ const imeText = ref('');
|
|||
const showingOptions = ref(false);
|
||||
const textAreaReadOnly = ref(false);
|
||||
const justEndedComposition = ref(false);
|
||||
const scheduleNote = ref<{
|
||||
scheduledAt: number | null;
|
||||
} | null>(null);
|
||||
|
||||
const draftKey = computed((): string => {
|
||||
let key = props.channel ? `channel:${props.channel.id}` : '';
|
||||
|
|
@ -367,6 +374,7 @@ function watchForDraft() {
|
|||
watch(localOnly, () => saveDraft());
|
||||
watch(quoteId, () => saveDraft());
|
||||
watch(reactionAcceptance, () => saveDraft());
|
||||
watch(scheduleNote, () => saveDraft());
|
||||
}
|
||||
|
||||
function MFMWindow() {
|
||||
|
|
@ -575,6 +583,7 @@ function clear() {
|
|||
files.value = [];
|
||||
poll.value = null;
|
||||
quoteId.value = null;
|
||||
scheduleNote.value = null;
|
||||
}
|
||||
|
||||
function onKeydown(ev: KeyboardEvent) {
|
||||
|
|
@ -732,6 +741,7 @@ function saveDraft() {
|
|||
visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(x => x.id) : undefined,
|
||||
quoteId: quoteId.value,
|
||||
reactionAcceptance: reactionAcceptance.value,
|
||||
scheduleNote: scheduleNote.value,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -839,6 +849,7 @@ async function post(ev?: MouseEvent) {
|
|||
visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(u => u.id) : undefined,
|
||||
reactionAcceptance: reactionAcceptance.value,
|
||||
editId: props.editId ? props.editId : undefined,
|
||||
scheduleNote: scheduleNote.value ?? undefined,
|
||||
};
|
||||
|
||||
if (withHashtags.value && hashtags.value && hashtags.value.trim() !== '') {
|
||||
|
|
@ -875,7 +886,7 @@ async function post(ev?: MouseEvent) {
|
|||
}
|
||||
|
||||
posting.value = true;
|
||||
misskeyApi(postData.editId ? 'notes/edit' : 'notes/create', postData, token).then(() => {
|
||||
misskeyApi(postData.editId ? 'notes/edit' : (postData.scheduleNote ? 'notes/schedule/create' : 'notes/create'), postData, token).then(() => {
|
||||
if (props.freezeAfterPosted) {
|
||||
posted.value = true;
|
||||
} else {
|
||||
|
|
@ -1026,6 +1037,42 @@ function openAccountMenu(ev: MouseEvent) {
|
|||
}, ev);
|
||||
}
|
||||
|
||||
function toggleScheduleNote() {
|
||||
if (scheduleNote.value) {
|
||||
scheduleNote.value = null;
|
||||
} else {
|
||||
scheduleNote.value = {
|
||||
scheduledAt: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function showOtherMenu(ev: MouseEvent) {
|
||||
const menuItems: MenuItem[] = [];
|
||||
|
||||
if ($i.policies.scheduleNoteMax > 0) {
|
||||
menuItems.push({
|
||||
type: 'button',
|
||||
text: i18n.ts.schedulePost,
|
||||
icon: 'ti ti-calendar-time',
|
||||
action: toggleScheduleNote,
|
||||
}, {
|
||||
type: 'button',
|
||||
text: i18n.ts.schedulePostList,
|
||||
icon: 'ti ti-calendar-event',
|
||||
action: () => {
|
||||
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkSchedulePostListDialog.vue')), {}, {
|
||||
closed: () => {
|
||||
dispose();
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
os.popupMenu(menuItems, ev.currentTarget ?? ev.target);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (props.autofocus) {
|
||||
focus();
|
||||
|
|
@ -1095,6 +1142,11 @@ onMounted(() => {
|
|||
}
|
||||
quoteId.value = init.renote ? init.renote.id : null;
|
||||
reactionAcceptance.value = init.reactionAcceptance;
|
||||
if (init.isSchedule) {
|
||||
scheduleNote.value = {
|
||||
scheduledAt: new Date(init.createdAt).getTime(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
nextTick(() => watchForDraft());
|
||||
|
|
|
|||
65
packages/frontend/src/components/MkScheduleEditor.vue
Normal file
65
packages/frontend/src/components/MkScheduleEditor.vue
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div style="padding: 8px 16px;">
|
||||
<section>
|
||||
<MkInput v-model="atDate" small type="date" class="input">
|
||||
<template #label>{{ i18n.ts._poll.deadlineDate }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-model="atTime" small type="time" class="input">
|
||||
<template #label>{{ i18n.ts._poll.deadlineTime }}</template>
|
||||
</MkInput>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import { formatDateTimeString } from '@/scripts/format-time-string.js';
|
||||
import { addTime } from '@/scripts/time.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: {
|
||||
scheduledAt: number | null;
|
||||
};
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'update:modelValue', v: {
|
||||
scheduledAt: number | null;
|
||||
}): void;
|
||||
}>();
|
||||
|
||||
const atDate = ref(formatDateTimeString(addTime(new Date(), 1, 'day'), 'yyyy-MM-dd'));
|
||||
const atTime = ref('00:00');
|
||||
|
||||
if (props.modelValue.scheduledAt) {
|
||||
const date = new Date(props.modelValue.scheduledAt);
|
||||
atDate.value = formatDateTimeString(date, 'yyyy-MM-dd');
|
||||
atTime.value = formatDateTimeString(date, 'HH:mm');
|
||||
}
|
||||
|
||||
function get() {
|
||||
const calcAt = () => {
|
||||
return new Date(`${ atDate.value } ${ atTime.value }`).getTime();
|
||||
};
|
||||
|
||||
return { scheduledAt: calcAt() };
|
||||
}
|
||||
|
||||
watch([
|
||||
atDate,
|
||||
atTime,
|
||||
], () => emit('update:modelValue', get()), {
|
||||
deep: true,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
emit('update:modelValue', get());
|
||||
});
|
||||
</script>
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<MkModalWindow
|
||||
ref="dialogEl"
|
||||
:withOkButton="false"
|
||||
@click="cancel()"
|
||||
@close="cancel()"
|
||||
>
|
||||
<template #header>{{ i18n.ts.schedulePostList }}</template>
|
||||
<MkSpacer :marginMin="14" :marginMax="16">
|
||||
<MkPagination ref="paginationEl" :pagination="pagination">
|
||||
<template #empty>
|
||||
<div class="_fullinfo">
|
||||
<img :src="infoImageUrl" class="_ghost"/>
|
||||
<div>{{ i18n.ts.nothing }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #default="{ items }">
|
||||
<div class="_gaps">
|
||||
<MkNoteSimple v-for="item in items" :key="item.id" :scheduled="true" :note="item.note" @editScheduleNote="listUpdate"/>
|
||||
</div>
|
||||
</template>
|
||||
</MkPagination>
|
||||
</MkSpacer>
|
||||
</MkModalWindow>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import type { Paging } from '@/components/MkPagination.vue';
|
||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||
import MkPagination from '@/components/MkPagination.vue';
|
||||
import MkNoteSimple from '@/components/MkNoteSimple.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { infoImageUrl } from '@/instance.js';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'cancel'): void;
|
||||
}>();
|
||||
|
||||
const dialogEl = ref();
|
||||
const cancel = () => {
|
||||
emit('cancel');
|
||||
dialogEl.value.close();
|
||||
};
|
||||
|
||||
const paginationEl = ref();
|
||||
const pagination: Paging = {
|
||||
endpoint: 'notes/schedule/list',
|
||||
limit: 10,
|
||||
offsetMode: true,
|
||||
};
|
||||
|
||||
function listUpdate() {
|
||||
paginationEl.value.reload();
|
||||
}
|
||||
</script>
|
||||
|
|
@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
:class="[$style.root, { [$style.showActionsOnlyHover]: defaultStore.state.showNoteActionsOnlyHover, [$style.skipRender]: defaultStore.state.skipNoteRender }]"
|
||||
:tabindex="isDeleted ? '-1' : '0'"
|
||||
>
|
||||
<SkNoteSub v-if="appearNote.reply && !renoteCollapsed && !inReplyToCollapsed" :note="appearNote.reply" :class="$style.replyTo"/>
|
||||
<SkNoteSub v-if="appearNote.reply" v-show="!renoteCollapsed && !inReplyToCollapsed" :note="appearNote.reply" :class="$style.replyTo"/>
|
||||
<div v-if="appearNote.reply && inReplyToCollapsed && !renoteCollapsed" :class="$style.collapsedInReplyTo">
|
||||
<div :class="$style.collapsedInReplyToLine"></div>
|
||||
<MkAvatar :class="$style.collapsedInReplyToAvatar" :user="appearNote.reply.user" link preview/>
|
||||
|
|
|
|||
|
|
@ -25,9 +25,11 @@
|
|||
connect-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000 https://newassets.hcaptcha.com https://api.listenbrainz.org https://api.friendlycaptcha.com https://raw.esm.sh;
|
||||
frame-src *;"
|
||||
/>
|
||||
<meta property="og:site_name" content="[DEV BUILD] Misskey" />
|
||||
<meta property="og:site_name" content="[DEV BUILD] Sharkey" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color-orig" content="#86b300">
|
||||
<link rel='stylesheet' href='/assets/phosphor-icons/bold/style.css'>
|
||||
<link rel='stylesheet' href='/static-assets/fonts/sharkey-icons/style.css'>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
|
|||
|
|
@ -740,3 +740,4 @@ export function checkExistence(fileData: ArrayBuffer): Promise<any> {
|
|||
});
|
||||
});
|
||||
}*/
|
||||
|
||||
|
|
|
|||
|
|
@ -200,6 +200,25 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.scheduleNoteMax, 'scheduleNoteMax'])">
|
||||
<template #label>{{ i18n.ts._role._options.scheduleNoteMax }}</template>
|
||||
<template #suffix>
|
||||
<span v-if="role.policies.scheduleNoteMax.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
|
||||
<span v-else>{{ role.policies.scheduleNoteMax.value }}</span>
|
||||
<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.scheduleNoteMax)"></i></span>
|
||||
</template>
|
||||
<div class="_gaps">
|
||||
<MkSwitch v-model="role.policies.scheduleNoteMax.useDefault" :readonly="readonly">
|
||||
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
|
||||
</MkSwitch>
|
||||
<MkInput v-model="role.policies.scheduleNoteMax.value" :disabled="role.policies.scheduleNoteMax.useDefault" type="number" :readonly="readonly">
|
||||
</MkInput>
|
||||
<MkRange v-model="role.policies.scheduleNoteMax.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||
</MkRange>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])">
|
||||
<template #label>{{ i18n.ts._role._options.mentionMax }}</template>
|
||||
<template #suffix>
|
||||
|
|
|
|||
|
|
@ -70,6 +70,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</MkSwitch>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.scheduleNoteMax, 'scheduleNoteMax'])">
|
||||
<template #label>{{ i18n.ts._role._options.scheduleNoteMax }}</template>
|
||||
<template #suffix>{{ policies.scheduleNoteMax }}</template>
|
||||
<MkInput v-model="policies.scheduleNoteMax" type="number">
|
||||
</MkInput>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])">
|
||||
<template #label>{{ i18n.ts._role._options.mentionMax }}</template>
|
||||
<template #suffix>{{ policies.mentionLimit }}</template>
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ const memoDraft = ref(props.user.memo);
|
|||
const isEditingMemo = ref(false);
|
||||
const moderationNote = ref(props.user.moderationNote);
|
||||
const editModerationNote = ref(false);
|
||||
const noteview = ref<string | null>('pinned');
|
||||
const noteview = ref<string | null>(props.user.pinnedNotes.length ? 'pinned' : null);
|
||||
|
||||
const listenbrainzdata = ref(false);
|
||||
if (props.user.listenbrainz) {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ export interface PostFormProps {
|
|||
initialFiles?: Misskey.entities.DriveFile[];
|
||||
initialLocalOnly?: boolean;
|
||||
initialVisibleUsers?: Misskey.entities.UserDetailed[];
|
||||
initialNote?: Misskey.entities.Note;
|
||||
initialNote?: Misskey.entities.Note & {
|
||||
isSchedule?: boolean,
|
||||
};
|
||||
instant?: boolean;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue