Compare commits
8 Commits
3a874dd59d
...
bff910c620
Author | SHA1 | Date |
---|---|---|
|
bff910c620 | |
|
3902fe89aa | |
|
4f57fd3e14 | |
|
36725b3e8c | |
|
0f789a519f | |
|
0c2c5688db | |
|
291659f462 | |
|
2d8779402a |
|
@ -8535,10 +8535,6 @@ export interface Locale extends ILocale {
|
||||||
* 入力ボックスの縁取り
|
* 入力ボックスの縁取り
|
||||||
*/
|
*/
|
||||||
"inputBorder": string;
|
"inputBorder": string;
|
||||||
/**
|
|
||||||
* ドライブフォルダーの背景
|
|
||||||
*/
|
|
||||||
"driveFolderBg": string;
|
|
||||||
/**
|
/**
|
||||||
* バッジ
|
* バッジ
|
||||||
*/
|
*/
|
||||||
|
@ -11903,6 +11899,10 @@ export interface Locale extends ILocale {
|
||||||
* アップロードされていないファイルがありますが、完了しますか?
|
* アップロードされていないファイルがありますが、完了しますか?
|
||||||
*/
|
*/
|
||||||
"doneConfirm": string;
|
"doneConfirm": string;
|
||||||
|
/**
|
||||||
|
* アップロード可能な最大ファイルサイズは{x}です。
|
||||||
|
*/
|
||||||
|
"maxFileSizeIsX": ParameterizedString<"x">;
|
||||||
};
|
};
|
||||||
"_clientPerformanceIssueTip": {
|
"_clientPerformanceIssueTip": {
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2237,7 +2237,6 @@ _theme:
|
||||||
buttonBg: "ボタンの背景"
|
buttonBg: "ボタンの背景"
|
||||||
buttonHoverBg: "ボタンの背景 (ホバー)"
|
buttonHoverBg: "ボタンの背景 (ホバー)"
|
||||||
inputBorder: "入力ボックスの縁取り"
|
inputBorder: "入力ボックスの縁取り"
|
||||||
driveFolderBg: "ドライブフォルダーの背景"
|
|
||||||
badge: "バッジ"
|
badge: "バッジ"
|
||||||
messageBg: "チャットの背景"
|
messageBg: "チャットの背景"
|
||||||
fgHighlighted: "強調された文字"
|
fgHighlighted: "強調された文字"
|
||||||
|
@ -3184,6 +3183,7 @@ _uploader:
|
||||||
savedXPercent: "{x}%節約"
|
savedXPercent: "{x}%節約"
|
||||||
abortConfirm: "アップロードされていないファイルがありますが、中止しますか?"
|
abortConfirm: "アップロードされていないファイルがありますが、中止しますか?"
|
||||||
doneConfirm: "アップロードされていないファイルがありますが、完了しますか?"
|
doneConfirm: "アップロードされていないファイルがありますが、完了しますか?"
|
||||||
|
maxFileSizeIsX: "アップロード可能な最大ファイルサイズは{x}です。"
|
||||||
|
|
||||||
_clientPerformanceIssueTip:
|
_clientPerformanceIssueTip:
|
||||||
title: "バッテリー消費が多いと感じたら"
|
title: "バッテリー消費が多いと感じたら"
|
||||||
|
|
|
@ -61,7 +61,6 @@
|
||||||
switchOnFg: '@accent',
|
switchOnFg: '@accent',
|
||||||
inputBorder: 'rgba(255, 255, 255, 0.1)',
|
inputBorder: 'rgba(255, 255, 255, 0.1)',
|
||||||
inputBorderHover: 'rgba(255, 255, 255, 0.2)',
|
inputBorderHover: 'rgba(255, 255, 255, 0.2)',
|
||||||
driveFolderBg: ':alpha<0.3<@accent',
|
|
||||||
badge: '#31b1ce',
|
badge: '#31b1ce',
|
||||||
messageBg: '@bg',
|
messageBg: '@bg',
|
||||||
success: '#86b300',
|
success: '#86b300',
|
||||||
|
|
|
@ -61,7 +61,6 @@
|
||||||
switchOnFg: '@fgOnAccent',
|
switchOnFg: '@fgOnAccent',
|
||||||
inputBorder: 'rgba(0, 0, 0, 0.1)',
|
inputBorder: 'rgba(0, 0, 0, 0.1)',
|
||||||
inputBorderHover: 'rgba(0, 0, 0, 0.2)',
|
inputBorderHover: 'rgba(0, 0, 0, 0.2)',
|
||||||
driveFolderBg: ':alpha<0.3<@accent',
|
|
||||||
badge: '#31b1ce',
|
badge: '#31b1ce',
|
||||||
messageBg: '@bg',
|
messageBg: '@bg',
|
||||||
success: '#86b300',
|
success: '#86b300',
|
||||||
|
|
|
@ -38,7 +38,6 @@
|
||||||
navIndicator: '@accent',
|
navIndicator: '@accent',
|
||||||
buttonGradateA: '@accent',
|
buttonGradateA: '@accent',
|
||||||
buttonGradateB: ':hue<-20<@accent',
|
buttonGradateB: ':hue<-20<@accent',
|
||||||
driveFolderBg: ':alpha<0.3<@accent',
|
|
||||||
fgHighlighted: ':lighten<3<@fg',
|
fgHighlighted: ':lighten<3<@fg',
|
||||||
panelHeaderBg: ':lighten<3<@panel',
|
panelHeaderBg: ':lighten<3<@panel',
|
||||||
panelHeaderFg: '@fg',
|
panelHeaderFg: '@fg',
|
||||||
|
|
|
@ -47,7 +47,6 @@
|
||||||
inputBorder: 'rgba(255, 255, 255, 0.1)',
|
inputBorder: 'rgba(255, 255, 255, 0.1)',
|
||||||
panelBorder: '" solid 1px var(--MI_THEME-divider)',
|
panelBorder: '" solid 1px var(--MI_THEME-divider)',
|
||||||
navIndicator: '@indicator',
|
navIndicator: '@indicator',
|
||||||
driveFolderBg: ':alpha<0.3<@accent',
|
|
||||||
fgHighlighted: ':lighten<3<@fg',
|
fgHighlighted: ':lighten<3<@fg',
|
||||||
panelHeaderBg: ':lighten<3<@panel',
|
panelHeaderBg: ':lighten<3<@panel',
|
||||||
panelHeaderFg: '@fg',
|
panelHeaderFg: '@fg',
|
||||||
|
|
|
@ -49,7 +49,6 @@
|
||||||
panelBorder: '" solid 1px var(--MI_THEME-divider)',
|
panelBorder: '" solid 1px var(--MI_THEME-divider)',
|
||||||
navIndicator: '@indicator',
|
navIndicator: '@indicator',
|
||||||
buttonHoverBg: '#0000001a',
|
buttonHoverBg: '#0000001a',
|
||||||
driveFolderBg: ':alpha<0.3<@accent',
|
|
||||||
fgHighlighted: ':lighten<3<@fg',
|
fgHighlighted: ':lighten<3<@fg',
|
||||||
panelHeaderBg: ':lighten<3<@panel',
|
panelHeaderBg: ':lighten<3<@panel',
|
||||||
panelHeaderFg: '@fg',
|
panelHeaderFg: '@fg',
|
||||||
|
|
|
@ -39,7 +39,6 @@
|
||||||
inputBorderHover: 'rgba(0, 0, 0, 0.2)',
|
inputBorderHover: 'rgba(0, 0, 0, 0.2)',
|
||||||
panelBorder: '" solid 1px var(--MI_THEME-divider)',
|
panelBorder: '" solid 1px var(--MI_THEME-divider)',
|
||||||
navIndicator: '@accent',
|
navIndicator: '@accent',
|
||||||
driveFolderBg: ':alpha<0.3<@accent',
|
|
||||||
fgHighlighted: ':darken<3<@fg',
|
fgHighlighted: ':darken<3<@fg',
|
||||||
fgOnWhite: '@accent',
|
fgOnWhite: '@accent',
|
||||||
panelHeaderBg: ':lighten<3<@panel',
|
panelHeaderBg: ':lighten<3<@panel',
|
||||||
|
|
|
@ -8,7 +8,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
:class="[$style.root, { [$style.isSelected]: isSelected }]"
|
:class="[$style.root, { [$style.isSelected]: isSelected }]"
|
||||||
draggable="true"
|
draggable="true"
|
||||||
:title="title"
|
:title="title"
|
||||||
@click="onClick"
|
|
||||||
@contextmenu.stop="onContextmenu"
|
@contextmenu.stop="onContextmenu"
|
||||||
@dragstart="onDragstart"
|
@dragstart="onDragstart"
|
||||||
@dragend="onDragend"
|
@dragend="onDragend"
|
||||||
|
@ -46,24 +45,17 @@ import * as os from '@/os.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { $i } from '@/i.js';
|
import { $i } from '@/i.js';
|
||||||
import { getDriveFileMenu } from '@/utility/get-drive-file-menu.js';
|
import { getDriveFileMenu } from '@/utility/get-drive-file-menu.js';
|
||||||
import { deviceKind } from '@/utility/device-kind.js';
|
|
||||||
import { useRouter } from '@/router.js';
|
|
||||||
import { setDragData } from '@/drag-and-drop.js';
|
import { setDragData } from '@/drag-and-drop.js';
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
file: Misskey.entities.DriveFile;
|
file: Misskey.entities.DriveFile;
|
||||||
folder: Misskey.entities.DriveFolder | null;
|
folder: Misskey.entities.DriveFolder | null;
|
||||||
isSelected?: boolean;
|
isSelected?: boolean;
|
||||||
selectMode?: boolean;
|
|
||||||
}>(), {
|
}>(), {
|
||||||
isSelected: false,
|
isSelected: false,
|
||||||
selectMode: false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(ev: 'chosen', r: Misskey.entities.DriveFile): void;
|
|
||||||
(ev: 'dragstart', dragEvent: DragEvent): void;
|
(ev: 'dragstart', dragEvent: DragEvent): void;
|
||||||
(ev: 'dragend'): void;
|
(ev: 'dragend'): void;
|
||||||
}>();
|
}>();
|
||||||
|
@ -72,18 +64,6 @@ const isDragging = ref(false);
|
||||||
|
|
||||||
const title = computed(() => `${props.file.name}\n${props.file.type} ${bytes(props.file.size)}`);
|
const title = computed(() => `${props.file.name}\n${props.file.type} ${bytes(props.file.size)}`);
|
||||||
|
|
||||||
function onClick(ev: MouseEvent) {
|
|
||||||
if (props.selectMode) {
|
|
||||||
emit('chosen', props.file);
|
|
||||||
} else {
|
|
||||||
if (deviceKind === 'desktop') {
|
|
||||||
router.push(`/my/drive/file/${props.file.id}`);
|
|
||||||
} else {
|
|
||||||
os.popupMenu(getDriveFileMenu(props.file, props.folder), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onContextmenu(ev: MouseEvent) {
|
function onContextmenu(ev: MouseEvent) {
|
||||||
os.contextMenu(getDriveFileMenu(props.file, props.folder), ev);
|
os.contextMenu(getDriveFileMenu(props.file, props.folder), ev);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
@dragend="onDragend"
|
@dragend="onDragend"
|
||||||
>
|
>
|
||||||
<svg :class="[$style.shape]" viewBox="0 0 200 150" preserveAspectRatio="none">
|
<svg :class="[$style.shape]" viewBox="0 0 200 150" preserveAspectRatio="none">
|
||||||
<path d="M190,25C195.523,25 200,29.477 200,35C200,58.415 200,116.585 200,140C200,145.523 195.523,150 190,150C155.86,150 44.14,150 10,150C4.477,150 0,145.523 0,140C0,112.727 0,37.273 0,10C0,4.477 4.477,0 10,-0C26.642,0 59.332,0 70.858,0C73.51,-0 76.054,1.054 77.929,2.929C82.74,7.74 92.26,17.26 97.071,22.071C98.946,23.946 101.49,25 104.142,25C118.808,25 168.535,25 190,25Z" style="fill:var(--MI_THEME-driveFolderBg);"/>
|
<path d="M190,25C195.523,25 200,29.477 200,35C200,58.415 200,116.585 200,140C200,145.523 195.523,150 190,150C155.86,150 44.14,150 10,150C4.477,150 0,145.523 0,140C0,112.727 0,37.273 0,10C0,4.477 4.477,0 10,-0C26.642,0 59.332,0 70.858,0C73.51,-0 76.054,1.054 77.929,2.929C82.74,7.74 92.26,17.26 97.071,22.071C98.946,23.946 101.49,25 104.142,25C118.808,25 168.535,25 190,25Z" style="fill:var(--MI_THEME-accentedBg);"/>
|
||||||
</svg>
|
</svg>
|
||||||
<div :class="$style.name">{{ folder.name }}</div>
|
<div :class="$style.name">{{ folder.name }}</div>
|
||||||
<div v-if="prefer.s.uploadFolder == folder.id" :class="$style.upload">
|
<div v-if="prefer.s.uploadFolder == folder.id" :class="$style.upload">
|
||||||
|
|
|
@ -60,8 +60,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
@drop.prevent.stop="onDrop"
|
@drop.prevent.stop="onDrop"
|
||||||
@contextmenu.stop="onContextmenu"
|
@contextmenu.stop="onContextmenu"
|
||||||
>
|
>
|
||||||
<MkInfo v-if="!store.r.readDriveTip.value" closable @close="closeTip()"><div v-html="i18n.ts.driveAboutTip"></div></MkInfo>
|
<div v-if="!store.r.readDriveTip.value" style="padding: 8px;">
|
||||||
<div v-show="foldersPaginator.items.value.length > 0">
|
<MkInfo closable @close="closeTip()"><div v-html="i18n.ts.driveAboutTip"></div></MkInfo>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div :class="$style.folders">
|
<div :class="$style.folders">
|
||||||
<XFolder
|
<XFolder
|
||||||
v-for="(f, i) in foldersPaginator.items.value"
|
v-for="(f, i) in foldersPaginator.items.value"
|
||||||
|
@ -80,9 +82,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<MkButton v-if="foldersPaginator.canFetchOlder.value" primary rounded @click="foldersPaginator.fetchOlder()">{{ i18n.ts.loadMore }}</MkButton>
|
<MkButton v-if="foldersPaginator.canFetchOlder.value" primary rounded @click="foldersPaginator.fetchOlder()">{{ i18n.ts.loadMore }}</MkButton>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-show="filesPaginator.items.value.length > 0">
|
|
||||||
<MkStickyContainer v-for="(item, i) in filesTimeline" :key="`${item.date.getFullYear()}/${item.date.getMonth() + 1}`">
|
<MkStickyContainer v-for="(item, i) in filesTimeline" :key="`${item.date.getFullYear()}/${item.date.getMonth() + 1}`">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div :class="$style.date">
|
<div :class="$style.date">
|
||||||
|
@ -104,16 +104,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
:class="$style.file"
|
:class="$style.file"
|
||||||
:file="file"
|
:file="file"
|
||||||
:folder="folder"
|
:folder="folder"
|
||||||
:selectMode="select === 'file' || isEditMode"
|
|
||||||
:isSelected="selectedFiles.some(x => x.id === file.id)"
|
:isSelected="selectedFiles.some(x => x.id === file.id)"
|
||||||
@chosen="onChooseFile"
|
@click="onFileClick($event, file)"
|
||||||
@dragstart="onFileDragstart(file, $event)"
|
@dragstart="onFileDragstart(file, $event)"
|
||||||
@dragend="isDragSource = false"
|
@dragend="isDragSource = false"
|
||||||
/>
|
/>
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
</MkStickyContainer>
|
</MkStickyContainer>
|
||||||
<MkButton v-show="filesPaginator.canFetchOlder.value" :class="$style.loadMore" primary rounded @click="filesPaginator.fetchOlder()">{{ i18n.ts.loadMore }}</MkButton>
|
<MkButton v-show="filesPaginator.canFetchOlder.value" :class="$style.loadMore" primary rounded @click="filesPaginator.fetchOlder()">{{ i18n.ts.loadMore }}</MkButton>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="filesPaginator.items.value.length == 0 && foldersPaginator.items.value.length == 0 && !fetching" :class="$style.empty">
|
<div v-if="filesPaginator.items.value.length == 0 && foldersPaginator.items.value.length == 0 && !fetching" :class="$style.empty">
|
||||||
<div v-if="draghover">{{ i18n.ts['empty-draghover'] }}</div>
|
<div v-if="draghover">{{ i18n.ts['empty-draghover'] }}</div>
|
||||||
|
@ -154,6 +152,7 @@ import { isSeparatorNeeded, getSeparatorInfo, makeDateGroupedTimelineComputedRef
|
||||||
import { usePagination } from '@/composables/use-pagination.js';
|
import { usePagination } from '@/composables/use-pagination.js';
|
||||||
import { globalEvents, useGlobalEvent } from '@/events.js';
|
import { globalEvents, useGlobalEvent } from '@/events.js';
|
||||||
import { checkDragDataType, getDragData, setDragData } from '@/drag-and-drop.js';
|
import { checkDragDataType, getDragData, setDragData } from '@/drag-and-drop.js';
|
||||||
|
import { getDriveFileMenu } from '@/utility/get-drive-file-menu.js';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
initialFolder?: Misskey.entities.DriveFolder['id'] | null;
|
initialFolder?: Misskey.entities.DriveFolder['id'] | null;
|
||||||
|
@ -458,7 +457,12 @@ function deleteFolder(folderToDelete: Misskey.entities.DriveFolder) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChooseFile(file: Misskey.entities.DriveFile) {
|
function onFileClick(ev: MouseEvent, file: Misskey.entities.DriveFile) {
|
||||||
|
if (ev.shiftKey) {
|
||||||
|
isEditMode.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.select === 'file' || isEditMode.value) {
|
||||||
const isAlreadySelected = selectedFiles.value.some(f => f.id === file.id);
|
const isAlreadySelected = selectedFiles.value.some(f => f.id === file.id);
|
||||||
|
|
||||||
if (isEditMode.value) {
|
if (isEditMode.value) {
|
||||||
|
@ -483,6 +487,9 @@ function onChooseFile(file: Misskey.entities.DriveFile) {
|
||||||
selectedFiles.value = [file];
|
selectedFiles.value = [file];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
os.popupMenu(getDriveFileMenu(file, folder.value), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function chooseFolder(folderToChoose: Misskey.entities.DriveFolder) {
|
function chooseFolder(folderToChoose: Misskey.entities.DriveFolder) {
|
||||||
|
@ -816,7 +823,14 @@ onBeforeUnmount(() => {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
||||||
grid-gap: 12px;
|
grid-gap: 12px;
|
||||||
padding: var(--MI-margin);
|
padding: 16px 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@container (max-width: 600px) {
|
||||||
|
.folders,
|
||||||
|
.files {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.date {
|
.date {
|
||||||
|
|
|
@ -15,7 +15,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<i class="ti ti-upload"></i> {{ i18n.tsx.uploadNFiles({ n: files.length }) }}
|
<i class="ti ti-upload"></i> {{ i18n.tsx.uploadNFiles({ n: files.length }) }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div :class="$style.root" class="_gaps_s">
|
<div :class="$style.root">
|
||||||
|
<div :class="[$style.overallProgress, canRetry ? $style.overallProgressError : null]" :style="{ '--op': `${overallProgress}%` }"></div>
|
||||||
|
|
||||||
|
<div :class="$style.main" class="_gaps_s">
|
||||||
<div :class="$style.items" class="_gaps_s">
|
<div :class="$style.items" class="_gaps_s">
|
||||||
<div
|
<div
|
||||||
v-for="ctx in items"
|
v-for="ctx in items"
|
||||||
|
@ -25,12 +28,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
:style="{ '--p': ctx.progress != null ? `${ctx.progress.value / ctx.progress.max * 100}%` : '0%' }"
|
:style="{ '--p': ctx.progress != null ? `${ctx.progress.value / ctx.progress.max * 100}%` : '0%' }"
|
||||||
>
|
>
|
||||||
<div :class="$style.itemInner">
|
<div :class="$style.itemInner">
|
||||||
<div>
|
<div :class="$style.itemActionWrapper">
|
||||||
<MkButton :iconOnly="true" rounded @click="showMenu($event, ctx)"><i class="ti ti-dots"></i></MkButton>
|
<MkButton :iconOnly="true" rounded @click="showMenu($event, ctx)"><i class="ti ti-dots"></i></MkButton>
|
||||||
</div>
|
</div>
|
||||||
<div :class="$style.itemThumbnail" :style="{ backgroundImage: `url(${ ctx.thumbnail })` }"></div>
|
<div :class="$style.itemThumbnail" :style="{ backgroundImage: `url(${ ctx.thumbnail })` }"></div>
|
||||||
<div :class="$style.itemBody">
|
<div :class="$style.itemBody">
|
||||||
<div>{{ ctx.name }}</div>
|
<div><MkCondensedLine :minScale="2 / 3">{{ ctx.name }}</MkCondensedLine></div>
|
||||||
<div :class="$style.itemInfo">
|
<div :class="$style.itemInfo">
|
||||||
<span>{{ ctx.file.type }}</span>
|
<span>{{ ctx.file.type }}</span>
|
||||||
<span>{{ bytes(ctx.file.size) }}</span>
|
<span>{{ bytes(ctx.file.size) }}</span>
|
||||||
|
@ -39,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<div>
|
<div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div :class="$style.itemIconWrapper">
|
||||||
<MkSystemIcon v-if="ctx.uploading" :class="$style.itemIcon" type="waiting"/>
|
<MkSystemIcon v-if="ctx.uploading" :class="$style.itemIcon" type="waiting"/>
|
||||||
<MkSystemIcon v-else-if="ctx.uploaded" :class="$style.itemIcon" type="success"/>
|
<MkSystemIcon v-else-if="ctx.uploaded" :class="$style.itemIcon" type="success"/>
|
||||||
<MkSystemIcon v-else-if="ctx.uploadFailed" :class="$style.itemIcon" type="error"/>
|
<MkSystemIcon v-else-if="ctx.uploadFailed" :class="$style.itemIcon" type="error"/>
|
||||||
|
@ -60,6 +63,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
>
|
>
|
||||||
<template #label>{{ i18n.ts.compress }}</template>
|
<template #label>{{ i18n.ts.compress }}</template>
|
||||||
</MkSelect>
|
</MkSelect>
|
||||||
|
|
||||||
|
<div>{{ i18n.tsx._uploader.maxFileSizeIsX({ x: $i.policies.maxFileSizeMb + 'MB' }) }}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
@ -91,6 +97,9 @@ import MkSelect from '@/components/MkSelect.vue';
|
||||||
import { isWebpSupported } from '@/utility/isWebpSupported.js';
|
import { isWebpSupported } from '@/utility/isWebpSupported.js';
|
||||||
import { uploadFile } from '@/utility/drive.js';
|
import { uploadFile } from '@/utility/drive.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
|
import { ensureSignin } from '@/i.js';
|
||||||
|
|
||||||
|
const $i = ensureSignin();
|
||||||
|
|
||||||
const COMPRESSION_SUPPORTED_TYPES = [
|
const COMPRESSION_SUPPORTED_TYPES = [
|
||||||
'image/jpeg',
|
'image/jpeg',
|
||||||
|
@ -144,6 +153,16 @@ const firstUploadAttempted = ref(false);
|
||||||
const isUploading = computed(() => items.value.some(item => item.uploading));
|
const isUploading = computed(() => items.value.some(item => item.uploading));
|
||||||
const canRetry = computed(() => firstUploadAttempted.value && !items.value.some(item => item.uploading || item.waiting) && items.value.some(item => item.uploaded == null));
|
const canRetry = computed(() => firstUploadAttempted.value && !items.value.some(item => item.uploading || item.waiting) && items.value.some(item => item.uploaded == null));
|
||||||
const canDone = computed(() => items.value.some(item => item.uploaded != null));
|
const canDone = computed(() => items.value.some(item => item.uploaded != null));
|
||||||
|
const overallProgress = computed(() => {
|
||||||
|
const max = items.value.length;
|
||||||
|
if (max === 0) return 0;
|
||||||
|
const v = items.value.reduce((acc, item) => {
|
||||||
|
if (item.uploaded) return acc + 1;
|
||||||
|
if (item.progress) return acc + (item.progress.value / item.progress.max);
|
||||||
|
return acc;
|
||||||
|
}, 0);
|
||||||
|
return Math.round((v / max) * 100);
|
||||||
|
});
|
||||||
|
|
||||||
const compressionLevel = ref<0 | 1 | 2 | 3>(2);
|
const compressionLevel = ref<0 | 1 | 2 | 3>(2);
|
||||||
const compressionSettings = computed(() => {
|
const compressionSettings = computed(() => {
|
||||||
|
@ -319,6 +338,25 @@ onMounted(() => {
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.root {
|
.root {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overallProgress {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: var(--op);
|
||||||
|
height: 4px;
|
||||||
|
background: var(--MI_THEME-accent);
|
||||||
|
border-radius: 0 999px 999px 0;
|
||||||
|
transition: width 0.2s ease;
|
||||||
|
|
||||||
|
&.overallProgressError {
|
||||||
|
background: var(--MI_THEME-warn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,12 +444,6 @@ onMounted(() => {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@container (max-width: 500px) {
|
|
||||||
.itemBody {
|
|
||||||
font-size: 90%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.itemInfo {
|
.itemInfo {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
|
@ -423,4 +455,34 @@ onMounted(() => {
|
||||||
.itemIcon {
|
.itemIcon {
|
||||||
width: 35px;
|
width: 35px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@container (max-width: 500px) {
|
||||||
|
.itemInner {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.itemBody {
|
||||||
|
font-size: 90%;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.itemActionWrapper {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.itemInfo {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.itemIconWrapper {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1247,6 +1247,12 @@ type DriveFilesFindRequest = operations['drive___files___find']['requestBody']['
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type DriveFilesFindResponse = operations['drive___files___find']['responses']['200']['content']['application/json'];
|
type DriveFilesFindResponse = operations['drive___files___find']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type DriveFilesMoveBulkRequest = operations['drive___files___move-bulk']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type DriveFilesMoveBulkResponse = operations['drive___files___move-bulk']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type DriveFilesRequest = operations['drive___files']['requestBody']['content']['application/json'];
|
type DriveFilesRequest = operations['drive___files']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
|
@ -1732,6 +1738,8 @@ declare namespace entities {
|
||||||
DriveFilesFindResponse,
|
DriveFilesFindResponse,
|
||||||
DriveFilesFindByHashRequest,
|
DriveFilesFindByHashRequest,
|
||||||
DriveFilesFindByHashResponse,
|
DriveFilesFindByHashResponse,
|
||||||
|
DriveFilesMoveBulkRequest,
|
||||||
|
DriveFilesMoveBulkResponse,
|
||||||
DriveFilesShowRequest,
|
DriveFilesShowRequest,
|
||||||
DriveFilesShowResponse,
|
DriveFilesShowResponse,
|
||||||
DriveFilesUpdateRequest,
|
DriveFilesUpdateRequest,
|
||||||
|
|
|
@ -2073,6 +2073,17 @@ declare module '../api.js' {
|
||||||
credential?: string | null,
|
credential?: string | null,
|
||||||
): Promise<SwitchCaseResponseType<E, P>>;
|
): Promise<SwitchCaseResponseType<E, P>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:drive*
|
||||||
|
*/
|
||||||
|
request<E extends 'drive/files/move-bulk', P extends Endpoints[E]['req']>(
|
||||||
|
endpoint: E,
|
||||||
|
params: P,
|
||||||
|
credential?: string | null,
|
||||||
|
): Promise<SwitchCaseResponseType<E, P>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the properties of a drive file.
|
* Show the properties of a drive file.
|
||||||
*
|
*
|
||||||
|
|
|
@ -282,6 +282,8 @@ import type {
|
||||||
DriveFilesFindResponse,
|
DriveFilesFindResponse,
|
||||||
DriveFilesFindByHashRequest,
|
DriveFilesFindByHashRequest,
|
||||||
DriveFilesFindByHashResponse,
|
DriveFilesFindByHashResponse,
|
||||||
|
DriveFilesMoveBulkRequest,
|
||||||
|
DriveFilesMoveBulkResponse,
|
||||||
DriveFilesShowRequest,
|
DriveFilesShowRequest,
|
||||||
DriveFilesShowResponse,
|
DriveFilesShowResponse,
|
||||||
DriveFilesUpdateRequest,
|
DriveFilesUpdateRequest,
|
||||||
|
@ -823,6 +825,7 @@ export type Endpoints = {
|
||||||
'drive/files/delete': { req: DriveFilesDeleteRequest; res: EmptyResponse };
|
'drive/files/delete': { req: DriveFilesDeleteRequest; res: EmptyResponse };
|
||||||
'drive/files/find': { req: DriveFilesFindRequest; res: DriveFilesFindResponse };
|
'drive/files/find': { req: DriveFilesFindRequest; res: DriveFilesFindResponse };
|
||||||
'drive/files/find-by-hash': { req: DriveFilesFindByHashRequest; res: DriveFilesFindByHashResponse };
|
'drive/files/find-by-hash': { req: DriveFilesFindByHashRequest; res: DriveFilesFindByHashResponse };
|
||||||
|
'drive/files/move-bulk': { req: DriveFilesMoveBulkRequest; res: DriveFilesMoveBulkResponse };
|
||||||
'drive/files/show': { req: DriveFilesShowRequest; res: DriveFilesShowResponse };
|
'drive/files/show': { req: DriveFilesShowRequest; res: DriveFilesShowResponse };
|
||||||
'drive/files/update': { req: DriveFilesUpdateRequest; res: DriveFilesUpdateResponse };
|
'drive/files/update': { req: DriveFilesUpdateRequest; res: DriveFilesUpdateResponse };
|
||||||
'drive/files/upload-from-url': { req: DriveFilesUploadFromUrlRequest; res: EmptyResponse };
|
'drive/files/upload-from-url': { req: DriveFilesUploadFromUrlRequest; res: EmptyResponse };
|
||||||
|
|
|
@ -285,6 +285,8 @@ export type DriveFilesFindRequest = operations['drive___files___find']['requestB
|
||||||
export type DriveFilesFindResponse = operations['drive___files___find']['responses']['200']['content']['application/json'];
|
export type DriveFilesFindResponse = operations['drive___files___find']['responses']['200']['content']['application/json'];
|
||||||
export type DriveFilesFindByHashRequest = operations['drive___files___find-by-hash']['requestBody']['content']['application/json'];
|
export type DriveFilesFindByHashRequest = operations['drive___files___find-by-hash']['requestBody']['content']['application/json'];
|
||||||
export type DriveFilesFindByHashResponse = operations['drive___files___find-by-hash']['responses']['200']['content']['application/json'];
|
export type DriveFilesFindByHashResponse = operations['drive___files___find-by-hash']['responses']['200']['content']['application/json'];
|
||||||
|
export type DriveFilesMoveBulkRequest = operations['drive___files___move-bulk']['requestBody']['content']['application/json'];
|
||||||
|
export type DriveFilesMoveBulkResponse = operations['drive___files___move-bulk']['responses']['200']['content']['application/json'];
|
||||||
export type DriveFilesShowRequest = operations['drive___files___show']['requestBody']['content']['application/json'];
|
export type DriveFilesShowRequest = operations['drive___files___show']['requestBody']['content']['application/json'];
|
||||||
export type DriveFilesShowResponse = operations['drive___files___show']['responses']['200']['content']['application/json'];
|
export type DriveFilesShowResponse = operations['drive___files___show']['responses']['200']['content']['application/json'];
|
||||||
export type DriveFilesUpdateRequest = operations['drive___files___update']['requestBody']['content']['application/json'];
|
export type DriveFilesUpdateRequest = operations['drive___files___update']['requestBody']['content']['application/json'];
|
||||||
|
|
|
@ -1799,6 +1799,15 @@ export type paths = {
|
||||||
*/
|
*/
|
||||||
post: operations['drive___files___find-by-hash'];
|
post: operations['drive___files___find-by-hash'];
|
||||||
};
|
};
|
||||||
|
'/drive/files/move-bulk': {
|
||||||
|
/**
|
||||||
|
* drive/files/move-bulk
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:drive*
|
||||||
|
*/
|
||||||
|
post: operations['drive___files___move-bulk'];
|
||||||
|
};
|
||||||
'/drive/files/show': {
|
'/drive/files/show': {
|
||||||
/**
|
/**
|
||||||
* drive/files/show
|
* drive/files/show
|
||||||
|
@ -16845,6 +16854,61 @@ export type operations = {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* drive/files/move-bulk
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:drive*
|
||||||
|
*/
|
||||||
|
'drive___files___move-bulk': {
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
fileIds: string[];
|
||||||
|
/** Format: misskey:id */
|
||||||
|
folderId?: string | null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description OK (with results) */
|
||||||
|
200: {
|
||||||
|
content: {
|
||||||
|
'application/json': unknown;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Client error */
|
||||||
|
400: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Authentication error */
|
||||||
|
401: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Forbidden error */
|
||||||
|
403: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description I'm Ai */
|
||||||
|
418: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Internal server error */
|
||||||
|
500: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* drive/files/show
|
* drive/files/show
|
||||||
* @description Show the properties of a drive file.
|
* @description Show the properties of a drive file.
|
||||||
|
|
Loading…
Reference in New Issue