Merge remote-tracking branch 'misskey-original/develop' into develop
# Conflicts: # package.json # packages/frontend/src/components/MkDrive.file.vue # packages/frontend/src/components/MkNote.vue
This commit is contained in:
commit
d691a130e2
31 changed files with 688 additions and 254 deletions
|
|
@ -86,7 +86,7 @@
|
|||
"bcryptjs": "2.4.3",
|
||||
"blurhash": "2.0.5",
|
||||
"body-parser": "1.20.2",
|
||||
"bullmq": "4.12.2",
|
||||
"bullmq": "4.12.3",
|
||||
"cacheable-lookup": "7.0.0",
|
||||
"cbor": "9.0.1",
|
||||
"chalk": "5.3.0",
|
||||
|
|
@ -189,13 +189,13 @@
|
|||
"@types/jsrsasign": "10.5.9",
|
||||
"@types/mime-types": "2.1.2",
|
||||
"@types/ms": "0.7.32",
|
||||
"@types/node": "20.8.3",
|
||||
"@types/node": "20.8.4",
|
||||
"@types/node-fetch": "3.0.3",
|
||||
"@types/nodemailer": "6.4.11",
|
||||
"@types/oauth": "0.9.2",
|
||||
"@types/oauth2orize": "1.11.1",
|
||||
"@types/oauth2orize-pkce": "0.1.0",
|
||||
"@types/pg": "8.10.3",
|
||||
"@types/pg": "8.10.4",
|
||||
"@types/pug": "2.0.7",
|
||||
"@types/punycode": "2.1.0",
|
||||
"@types/qrcode": "1.5.2",
|
||||
|
|
@ -212,8 +212,8 @@
|
|||
"@types/vary": "1.1.1",
|
||||
"@types/web-push": "3.6.1",
|
||||
"@types/ws": "8.5.6",
|
||||
"@typescript-eslint/eslint-plugin": "6.7.4",
|
||||
"@typescript-eslint/parser": "6.7.4",
|
||||
"@typescript-eslint/eslint-plugin": "6.7.5",
|
||||
"@typescript-eslint/parser": "6.7.5",
|
||||
"aws-sdk-client-mock": "3.0.0",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint": "8.51.0",
|
||||
|
|
|
|||
|
|
@ -109,7 +109,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
this.queryService.generateBlockedUserQuery(query, me);
|
||||
|
||||
const notes = await query.getMany();
|
||||
notes.sort((a, b) => a.id > b.id ? -1 : 1);
|
||||
if (sinceId != null && untilId == null) {
|
||||
notes.sort((a, b) => a.id < b.id ? -1 : 1);
|
||||
} else {
|
||||
notes.sort((a, b) => a.id > b.id ? -1 : 1);
|
||||
}
|
||||
|
||||
if (notes.length > 0) {
|
||||
this.noteReadService.read(me.id, notes);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import type { NotesRepository, DriveFilesRepository } from '@/models/_.js';
|
||||
import { QueryService } from '@/core/QueryService.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { ApiError } from '../../../error.js';
|
||||
|
|
@ -41,6 +42,9 @@ export const meta = {
|
|||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
sinceId: { type: 'string', format: 'misskey:id' },
|
||||
untilId: { type: 'string', format: 'misskey:id' },
|
||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||
fileId: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
required: ['fileId'],
|
||||
|
|
@ -56,6 +60,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
private notesRepository: NotesRepository,
|
||||
|
||||
private noteEntityService: NoteEntityService,
|
||||
private queryService: QueryService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
// Fetch file
|
||||
|
|
@ -68,9 +73,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||
throw new ApiError(meta.errors.noSuchFile);
|
||||
}
|
||||
|
||||
const notes = await this.notesRepository.createQueryBuilder('note')
|
||||
.where(':file = ANY(note.fileIds)', { file: file.id })
|
||||
.getMany();
|
||||
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId);
|
||||
query.andWhere(':file = ANY(note.fileIds)', { file: file.id });
|
||||
|
||||
const notes = await query.limit(ps.limit).getMany();
|
||||
|
||||
return await this.noteEntityService.packMany(notes, me, {
|
||||
detail: true,
|
||||
|
|
|
|||
|
|
@ -457,6 +457,7 @@ export async function testPaginationConsistency<Entity extends { id: string, cre
|
|||
};
|
||||
|
||||
for (const limit of [1, 5, 10, 100, undefined]) {
|
||||
/*
|
||||
// 1. sinceId/DateとuntilId/Dateで両端を指定して取得した結果が期待通りになっていること
|
||||
if (ordering === 'desc') {
|
||||
const end = expected.at(-1)!;
|
||||
|
|
@ -485,6 +486,7 @@ export async function testPaginationConsistency<Entity extends { id: string, cre
|
|||
actual.map(({ id, createdAt }) => id + ':' + createdAt),
|
||||
expected.map(({ id, createdAt }) => id + ':' + createdAt));
|
||||
}
|
||||
*/
|
||||
|
||||
// 3. untilId指定+limitで取得してつなぎ合わせた結果が期待通りになっていること
|
||||
if (ordering === 'desc') {
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@
|
|||
"querystring": "0.2.1",
|
||||
"rollup": "4.0.2",
|
||||
"sanitize-html": "2.11.0",
|
||||
"sass": "1.69.0",
|
||||
"sass": "1.69.1",
|
||||
"strict-event-emitter-types": "2.0.0",
|
||||
"textarea-caret": "3.1.0",
|
||||
"three": "0.157.0",
|
||||
|
|
@ -103,7 +103,7 @@
|
|||
"@types/estree": "1.0.2",
|
||||
"@types/matter-js": "0.19.1",
|
||||
"@types/micromatch": "4.0.3",
|
||||
"@types/node": "20.8.3",
|
||||
"@types/node": "20.8.4",
|
||||
"@types/punycode": "2.1.0",
|
||||
"@types/sanitize-html": "2.9.1",
|
||||
"@types/throttle-debounce": "5.0.0",
|
||||
|
|
@ -111,8 +111,8 @@
|
|||
"@types/uuid": "9.0.5",
|
||||
"@types/websocket": "1.0.7",
|
||||
"@types/ws": "8.5.6",
|
||||
"@typescript-eslint/eslint-plugin": "6.7.4",
|
||||
"@typescript-eslint/parser": "6.7.4",
|
||||
"@typescript-eslint/eslint-plugin": "6.7.5",
|
||||
"@typescript-eslint/parser": "6.7.5",
|
||||
"@vitest/coverage-v8": "0.34.6",
|
||||
"@vue/runtime-core": "3.3.4",
|
||||
"acorn": "8.10.0",
|
||||
|
|
|
|||
|
|
@ -4,10 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
-->
|
||||
|
||||
<template>
|
||||
<button class="_button" :class="$style.root" @mousedown="toggle">
|
||||
<b>{{ modelValue ? i18n.ts._cw.hide : i18n.ts._cw.show }}</b>
|
||||
<span v-if="!modelValue" :class="$style.label">{{ label }}</span>
|
||||
</button>
|
||||
<MkButton rounded full small @click="toggle"><b>{{ modelValue ? i18n.ts._cw.hide : i18n.ts._cw.show }}</b><span v-if="!modelValue" :class="$style.label">{{ label }}</span></MkButton>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
|
@ -15,6 +12,7 @@ import { computed } from 'vue';
|
|||
import * as Misskey from 'misskey-js';
|
||||
import { concat } from '@/scripts/array.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean;
|
||||
|
|
@ -33,25 +31,12 @@ const label = computed(() => {
|
|||
] as string[][]).join(' / ');
|
||||
});
|
||||
|
||||
const toggle = () => {
|
||||
function toggle() {
|
||||
emit('update:modelValue', !props.modelValue);
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.root {
|
||||
display: inline-block;
|
||||
padding: 4px 8px;
|
||||
font-size: 0.7em;
|
||||
color: var(--cwFg);
|
||||
background: var(--cwBg);
|
||||
border-radius: 2px;
|
||||
|
||||
&:hover {
|
||||
background: var(--cwHoverBg);
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
margin-left: 4px;
|
||||
|
||||
|
|
|
|||
|
|
@ -45,9 +45,12 @@ import bytes from '@/filters/bytes.js';
|
|||
import * as os from '@/os.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { $i } from '@/account.js';
|
||||
import { useRouter } from '@/router.js';
|
||||
import { getDriveFileMenu, getDriveMultiFileMenu } from '@/scripts/get-drive-file-menu.js';
|
||||
import { isTouchUsing } from '@/scripts/touch.js';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
file: Misskey.entities.DriveFile;
|
||||
folder: Misskey.entities.DriveFolder | null;
|
||||
|
|
@ -84,6 +87,8 @@ function onClick(ev: MouseEvent) {
|
|||
os.popupMenu(getDriveMultiFileMenu(props.SelectFiles), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined);
|
||||
} else if (isTouchUsing && !isSelectedFile.value && props.SelectFiles.length === 0) {
|
||||
os.popupMenu(getDriveFileMenu(props.file, props.folder), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined);
|
||||
}else {
|
||||
router.push(`/my/drive/file/${props.file.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<p v-if="appearNote.cw != null" :class="$style.cw">
|
||||
<Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user"
|
||||
:i="$i"/>
|
||||
<MkCwButton v-model="showContent" :note="appearNote"/>
|
||||
<MkCwButton v-model="showContent" :note="appearNote" style="margin: 4px 0;"/>
|
||||
</p>
|
||||
<div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]">
|
||||
<div :class="$style.text">
|
||||
|
|
@ -190,7 +190,6 @@ const props = defineProps<{
|
|||
|
||||
const inChannel = inject('inChannel', null);
|
||||
const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null);
|
||||
;
|
||||
let note = $ref(deepClone(props.note));
|
||||
|
||||
// plugin
|
||||
|
|
|
|||
302
packages/frontend/src/pages/drive.file.info.vue
Normal file
302
packages/frontend/src/pages/drive.file.info.vue
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="_gaps">
|
||||
<MkInfo>{{ i18n.ts._fileViewer.thisPageCanBeSeenFromTheAuthor }}</MkInfo>
|
||||
<MkLoading v-if="fetching"/>
|
||||
<div v-else-if="file" class="_gaps">
|
||||
<div :class="$style.filePreviewRoot">
|
||||
<MkMediaList :mediaList="[file]"></MkMediaList>
|
||||
</div>
|
||||
<div :class="$style.fileQuickActionsRoot">
|
||||
<button class="_button" :class="$style.fileNameEditBtn" @click="rename()">
|
||||
<h2 class="_nowrap" :class="$style.fileName">{{ file.name }}</h2>
|
||||
<i class="ti ti-pencil" :class="$style.fileNameEditIcon"></i>
|
||||
</button>
|
||||
<div :class="$style.fileQuickActionsOthers">
|
||||
<button v-tooltip="i18n.ts.createNoteFromTheFile" class="_button" :class="$style.fileQuickActionsOthersButton" @click="postThis()">
|
||||
<i class="ti ti-pencil"></i>
|
||||
</button>
|
||||
<button v-if="isImage" v-tooltip="i18n.ts.cropImage" class="_button" :class="$style.fileQuickActionsOthersButton" @click="crop()">
|
||||
<i class="ti ti-crop"></i>
|
||||
</button>
|
||||
<button v-if="file.isSensitive" v-tooltip="i18n.ts.unmarkAsSensitive" class="_button" :class="$style.fileQuickActionsOthersButton" @click="toggleSensitive()">
|
||||
<i class="ti ti-eye"></i>
|
||||
</button>
|
||||
<button v-else v-tooltip="i18n.ts.markAsSensitive" class="_button" :class="$style.fileQuickActionsOthersButton" @click="toggleSensitive()">
|
||||
<i class="ti ti-eye-exclamation"></i>
|
||||
</button>
|
||||
<a v-tooltip="i18n.ts.download" :href="file.url" :download="file.name" class="_button" :class="$style.fileQuickActionsOthersButton">
|
||||
<i class="ti ti-download"></i>
|
||||
</a>
|
||||
<button v-tooltip="i18n.ts.delete" class="_button" :class="[$style.fileQuickActionsOthersButton, $style.danger]" @click="deleteFile()">
|
||||
<i class="ti ti-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<button class="_button" :class="$style.fileAltEditBtn" @click="describe()">
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.description }}</template>
|
||||
<template #value>{{ file.comment ? file.comment : `(${i18n.ts.none})` }}<i class="ti ti-pencil" :class="$style.fileAltEditIcon"></i></template>
|
||||
</MkKeyValue>
|
||||
</button>
|
||||
<MkKeyValue :class="$style.fileMetaDataChildren">
|
||||
<template #key>{{ i18n.ts._fileViewer.uploadedAt }}</template>
|
||||
<template #value><MkTime :time="file.createdAt" mode="detail"/></template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue :class="$style.fileMetaDataChildren">
|
||||
<template #key>{{ i18n.ts._fileViewer.type }}</template>
|
||||
<template #value>{{ file.type }}</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue :class="$style.fileMetaDataChildren">
|
||||
<template #key>{{ i18n.ts._fileViewer.size }}</template>
|
||||
<template #value>{{ bytes(file.size) }}</template>
|
||||
</MkKeyValue>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="_fullinfo">
|
||||
<img :src="infoImageUrl" class="_ghost"/>
|
||||
<div>{{ i18n.ts.nothing }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, defineAsyncComponent, onMounted } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkInfo from '@/components/MkInfo.vue';
|
||||
import MkMediaList from '@/components/MkMediaList.vue';
|
||||
import MkKeyValue from '@/components/MkKeyValue.vue';
|
||||
import bytes from '@/filters/bytes.js';
|
||||
import { infoImageUrl } from '@/instance.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import * as os from '@/os.js';
|
||||
import { useRouter } from '@/router.js';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const props = defineProps<{
|
||||
fileId: string;
|
||||
}>();
|
||||
|
||||
const fetching = ref(true);
|
||||
const file = ref<Misskey.entities.DriveFile>();
|
||||
const isImage = computed(() => file.value?.type.startsWith('image/'));
|
||||
|
||||
async function fetch() {
|
||||
fetching.value = true;
|
||||
|
||||
file.value = await os.api('drive/files/show', {
|
||||
fileId: props.fileId,
|
||||
}).catch((err) => {
|
||||
console.error(err);
|
||||
return undefined;
|
||||
});
|
||||
|
||||
fetching.value = false;
|
||||
}
|
||||
|
||||
function postThis() {
|
||||
if (!file.value) return;
|
||||
|
||||
os.post({
|
||||
initialFiles: [file.value],
|
||||
});
|
||||
}
|
||||
|
||||
function crop() {
|
||||
if (!file.value) return;
|
||||
|
||||
os.cropImage(file.value, {
|
||||
aspectRatio: NaN,
|
||||
uploadFolder: file.value.folderId ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
function toggleSensitive() {
|
||||
if (!file.value) return;
|
||||
|
||||
os.apiWithDialog('drive/files/update', {
|
||||
fileId: file.value.id,
|
||||
isSensitive: !file.value.isSensitive,
|
||||
}).then(async () => {
|
||||
await fetch();
|
||||
}).catch(err => {
|
||||
os.alert({
|
||||
type: 'error',
|
||||
title: i18n.ts.error,
|
||||
text: err.message,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function rename() {
|
||||
if (!file.value) return;
|
||||
|
||||
os.inputText({
|
||||
title: i18n.ts.renameFile,
|
||||
placeholder: i18n.ts.inputNewFileName,
|
||||
default: file.value.name,
|
||||
}).then(({ canceled, result: name }) => {
|
||||
if (canceled) return;
|
||||
os.apiWithDialog('drive/files/update', {
|
||||
fileId: file.value.id,
|
||||
name: name,
|
||||
}).then(async () => {
|
||||
await fetch();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function describe() {
|
||||
if (!file.value) return;
|
||||
|
||||
os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), {
|
||||
default: file.value.comment ?? '',
|
||||
file: file.value,
|
||||
}, {
|
||||
done: caption => {
|
||||
os.apiWithDialog('drive/files/update', {
|
||||
fileId: file.value.id,
|
||||
comment: caption.length === 0 ? null : caption,
|
||||
}).then(async () => {
|
||||
await fetch();
|
||||
});
|
||||
},
|
||||
}, 'closed');
|
||||
}
|
||||
|
||||
async function deleteFile() {
|
||||
if (!file.value) return;
|
||||
|
||||
const { canceled } = await os.confirm({
|
||||
type: 'warning',
|
||||
text: i18n.t('driveFileDeleteConfirm', { name: file.value.name }),
|
||||
});
|
||||
|
||||
if (canceled) return;
|
||||
await os.apiWithDialog('drive/files/delete', {
|
||||
fileId: file.value.id,
|
||||
});
|
||||
|
||||
router.push('/my/drive');
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await fetch();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
||||
.filePreviewRoot {
|
||||
background: var(--panel);
|
||||
border-radius: var(--radius);
|
||||
// MkMediaList 内の上部マージン 4px
|
||||
padding: calc(1rem - 4px) 1rem 1rem;
|
||||
}
|
||||
|
||||
.fileQuickActionsRoot {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
@container (min-width: 500px) {
|
||||
.fileQuickActionsRoot {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.fileQuickActionsOthers {
|
||||
margin-left: auto;
|
||||
margin-right: 1rem;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
.fileQuickActionsOthersButton {
|
||||
padding: .5rem;
|
||||
border-radius: 99rem;
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
background-color: var(--accentedBg);
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&.danger {
|
||||
color: #ff2a2a;
|
||||
}
|
||||
|
||||
&.danger:hover,
|
||||
&.danger:focus-visible {
|
||||
background-color: rgba(255, 42, 42, .15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fileNameEditBtn {
|
||||
padding: .5rem 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
font-weight: 700;
|
||||
border-radius: var(--radius);
|
||||
font-size: .8rem;
|
||||
|
||||
>.fileNameEditIcon {
|
||||
color: transparent;
|
||||
visibility: hidden;
|
||||
padding-left: .5rem;
|
||||
}
|
||||
|
||||
>.fileName {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--accentedBg);
|
||||
|
||||
>.fileName,
|
||||
>.fileNameEditIcon {
|
||||
visibility: visible;
|
||||
color: var(--accent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fileMetaDataChildren {
|
||||
padding: .5rem 1rem;
|
||||
}
|
||||
|
||||
.fileAltEditBtn {
|
||||
text-align: start;
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: .5rem 1rem;
|
||||
border-radius: var(--radius);
|
||||
|
||||
.fileAltEditIcon {
|
||||
display: inline-block;
|
||||
color: transparent;
|
||||
visibility: hidden;
|
||||
padding-left: .5rem;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--accent);
|
||||
background-color: var(--accentedBg);
|
||||
|
||||
.fileAltEditIcon {
|
||||
color: var(--accent);
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
33
packages/frontend/src/pages/drive.file.notes.vue
Normal file
33
packages/frontend/src/pages/drive.file.notes.vue
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="_gaps">
|
||||
<MkInfo>{{ i18n.ts._fileViewer.thisPageCanBeSeenFromTheAuthor }}</MkInfo>
|
||||
<MkNotes ref="tlComponent" :pagination="pagination"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { Paging } from '@/components/MkPagination.vue';
|
||||
import MkInfo from '@/components/MkInfo.vue';
|
||||
import MkNotes from '@/components/MkNotes.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
fileId: string;
|
||||
}>();
|
||||
|
||||
const realFileId = computed(() => props.fileId);
|
||||
|
||||
const pagination = ref<Paging>({
|
||||
endpoint: 'drive/files/attached-notes',
|
||||
limit: 10,
|
||||
params: {
|
||||
fileId: realFileId.value,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
52
packages/frontend/src/pages/drive.file.vue
Normal file
52
packages/frontend/src/pages/drive.file.vue
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<MkStickyContainer>
|
||||
<template #header>
|
||||
<MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/>
|
||||
</template>
|
||||
|
||||
<MkSpacer v-if="tab === 'info'" :contentMax="800">
|
||||
<XFileInfo :fileId="fileId"/>
|
||||
</MkSpacer>
|
||||
|
||||
<MkSpacer v-else-if="tab === 'notes'" :contentMax="800">
|
||||
<XNotes :fileId="fileId"/>
|
||||
</MkSpacer>
|
||||
</MkStickyContainer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, defineAsyncComponent } from 'vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
||||
|
||||
const props = defineProps<{
|
||||
fileId: string;
|
||||
}>();
|
||||
|
||||
const XFileInfo = defineAsyncComponent(() => import('./drive.file.info.vue'));
|
||||
const XNotes = defineAsyncComponent(() => import('./drive.file.notes.vue'));
|
||||
|
||||
const tab = ref('info');
|
||||
|
||||
const headerActions = computed(() => []);
|
||||
|
||||
const headerTabs = computed(() => [{
|
||||
key: 'info',
|
||||
title: i18n.ts.info,
|
||||
icon: 'ti ti-info-circle',
|
||||
}, {
|
||||
key: 'notes',
|
||||
title: i18n.ts._fileViewer.attachedNotes,
|
||||
icon: 'ti ti-pencil',
|
||||
}]);
|
||||
|
||||
definePageMetadata(computed(() => ({
|
||||
title: i18n.ts._fileViewer.title,
|
||||
icon: 'ti ti-file',
|
||||
})));
|
||||
</script>
|
||||
|
|
@ -61,20 +61,7 @@ function settings() {
|
|||
router.push(`/my/lists/${props.listId}`);
|
||||
}
|
||||
|
||||
async function timetravel() {
|
||||
const { canceled, result: date } = await os.inputDate({
|
||||
title: i18n.ts.date,
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
tlEl.timetravel(date);
|
||||
}
|
||||
|
||||
const headerActions = $computed(() => list ? [{
|
||||
icon: 'ti ti-calendar-time',
|
||||
text: i18n.ts.jumpToSpecifiedDate,
|
||||
handler: timetravel,
|
||||
}, {
|
||||
icon: 'ti ti-settings',
|
||||
text: i18n.ts.settings,
|
||||
handler: settings,
|
||||
|
|
|
|||
|
|
@ -467,6 +467,10 @@ export const routes = [{
|
|||
path: '/my/drive',
|
||||
component: page(() => import('./pages/drive.vue')),
|
||||
loginRequired: true,
|
||||
}, {
|
||||
path: '/my/drive/file/:fileId',
|
||||
component: page(() => import('./pages/drive.file.vue')),
|
||||
loginRequired: true,
|
||||
}, {
|
||||
path: '/my/follow-requests',
|
||||
component: page(() => import('./pages/follow-requests.vue')),
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ function rename(file: Misskey.entities.DriveFile) {
|
|||
|
||||
function describe(file: Misskey.entities.DriveFile) {
|
||||
os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), {
|
||||
default: file.comment != null ? file.comment : '',
|
||||
default: file.comment ?? '',
|
||||
file: file,
|
||||
}, {
|
||||
done: caption => {
|
||||
|
|
@ -139,6 +139,11 @@ export function getDriveFileMenu(file: Misskey.entities.DriveFile, folder?: Miss
|
|||
text: i18n.ts.download,
|
||||
icon: 'ti ti-download',
|
||||
download: file.name,
|
||||
}, null, {
|
||||
type: 'link',
|
||||
to: `/my/drive/file/${file.id}`,
|
||||
text: i18n.ts._fileViewer.title,
|
||||
icon: 'ti ti-file',
|
||||
}, null, {
|
||||
text: i18n.ts.delete,
|
||||
icon: 'ti ti-trash',
|
||||
|
|
|
|||
|
|
@ -54,9 +54,6 @@
|
|||
infoWarnBg: '#42321c',
|
||||
infoWarnFg: '#ffbd3e',
|
||||
switchBg: 'rgba(255, 255, 255, 0.15)',
|
||||
cwBg: '#687390',
|
||||
cwFg: '#393f4f',
|
||||
cwHoverBg: '#707b97',
|
||||
buttonBg: 'rgba(255, 255, 255, 0.05)',
|
||||
buttonHoverBg: 'rgba(255, 255, 255, 0.1)',
|
||||
buttonGradateA: '@accent',
|
||||
|
|
|
|||
|
|
@ -54,9 +54,6 @@
|
|||
infoWarnBg: '#fff0db',
|
||||
infoWarnFg: '#8f6e31',
|
||||
switchBg: 'rgba(0, 0, 0, 0.15)',
|
||||
cwBg: '#b1b9c1',
|
||||
cwFg: '#fff',
|
||||
cwHoverBg: '#bbc4ce',
|
||||
buttonBg: 'rgba(0, 0, 0, 0.05)',
|
||||
buttonHoverBg: 'rgba(0, 0, 0, 0.1)',
|
||||
buttonGradateA: '@accent',
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@
|
|||
props: {
|
||||
bg: '#232125',
|
||||
fg: '#efdab9',
|
||||
cwBg: '#687390',
|
||||
cwFg: '#393f4f',
|
||||
link: '#78b0a0',
|
||||
warn: '#ecb637',
|
||||
badge: '#31b1ce',
|
||||
|
|
@ -29,7 +27,6 @@
|
|||
success: '#86b300',
|
||||
buttonBg: 'rgba(255, 255, 255, 0.05)',
|
||||
acrylicBg: ':alpha<0.5<@bg',
|
||||
cwHoverBg: '#707b97',
|
||||
indicator: '@accent',
|
||||
mentionMe: '#fb5d38',
|
||||
messageBg: '@bg',
|
||||
|
|
|
|||
|
|
@ -21,8 +21,6 @@
|
|||
X15: ':alpha<0<@panel',
|
||||
X16: ':alpha<0.7<@panel',
|
||||
X17: ':alpha<0.8<@bg',
|
||||
cwBg: '#687390',
|
||||
cwFg: '#393f4f',
|
||||
link: '@accent',
|
||||
warn: '#ecb637',
|
||||
badge: '#31b1ce',
|
||||
|
|
@ -46,7 +44,6 @@
|
|||
buttonBg: 'rgba(255, 255, 255, 0.05)',
|
||||
switchBg: 'rgba(255, 255, 255, 0.15)',
|
||||
acrylicBg: ':alpha<0.5<@bg',
|
||||
cwHoverBg: '#707b97',
|
||||
indicator: '@accent',
|
||||
mentionMe: '@mention',
|
||||
messageBg: '@bg',
|
||||
|
|
|
|||
|
|
@ -21,8 +21,6 @@
|
|||
X15: ':alpha<0<@panel',
|
||||
X16: ':alpha<0.7<@panel',
|
||||
X17: ':alpha<0.8<@bg',
|
||||
cwBg: '#687390',
|
||||
cwFg: '#393f4f',
|
||||
link: '@accent',
|
||||
warn: '#ecb637',
|
||||
badge: '#31b1ce',
|
||||
|
|
@ -46,7 +44,6 @@
|
|||
buttonBg: '#0000000d',
|
||||
switchBg: 'rgba(255, 255, 255, 0.15)',
|
||||
acrylicBg: ':alpha<0.5<@bg',
|
||||
cwHoverBg: '#707b97',
|
||||
indicator: '@accent',
|
||||
mentionMe: '@mention',
|
||||
messageBg: '@bg',
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@
|
|||
props: {
|
||||
bg: '#fafafa',
|
||||
fg: '#444',
|
||||
cwBg: '#b1b9c1',
|
||||
cwFg: '#fff',
|
||||
link: '#ff9400',
|
||||
warn: '#ecb637',
|
||||
badge: '#31b1ce',
|
||||
|
|
@ -32,7 +30,6 @@
|
|||
success: '#86b300',
|
||||
buttonBg: 'rgba(0, 0, 0, 0.05)',
|
||||
acrylicBg: ':alpha<0.5<@bg',
|
||||
cwHoverBg: '#bbc4ce',
|
||||
indicator: '@accent',
|
||||
mentionMe: '@mention',
|
||||
messageBg: '@bg',
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@
|
|||
"@microsoft/api-extractor": "7.38.0",
|
||||
"@swc/jest": "0.2.29",
|
||||
"@types/jest": "29.5.5",
|
||||
"@types/node": "20.8.3",
|
||||
"@typescript-eslint/eslint-plugin": "6.7.4",
|
||||
"@typescript-eslint/parser": "6.7.4",
|
||||
"@types/node": "20.8.4",
|
||||
"@typescript-eslint/eslint-plugin": "6.7.5",
|
||||
"@typescript-eslint/parser": "6.7.5",
|
||||
"eslint": "8.51.0",
|
||||
"jest": "29.7.0",
|
||||
"jest-fetch-mock": "3.0.3",
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
"misskey-js": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/parser": "6.7.4",
|
||||
"@typescript-eslint/parser": "6.7.5",
|
||||
"@typescript/lib-webworker": "npm:@types/serviceworker@0.0.67",
|
||||
"eslint": "8.51.0",
|
||||
"eslint-plugin-import": "2.28.1",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue