forked from mirrors/misskey
enhance(frontend): 通知音にドライブのファイルを使用できるように (#12447)
* (enhance) サウンドにドライブのファイルを使用できるように * Update Changelog * fix * fix design * fix design * Update store.ts * (fix) ファイル名表示 * refactor * (refactor) better types * operationTypeとsoundTypeの混同を防止 * (refactor) * (fix) * enhance jsdoc * driveFile -> _driveFile_
This commit is contained in:
@@ -7,8 +7,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div class="_gaps_m">
|
||||
<MkSelect v-model="type">
|
||||
<template #label>{{ i18n.ts.sound }}</template>
|
||||
<option v-for="x in soundsTypes" :key="x" :value="x">{{ x == null ? i18n.ts.none : x }}</option>
|
||||
<option v-for="x in soundsTypes" :key="x ?? 'null'" :value="x">{{ getSoundTypeName(x) }}</option>
|
||||
</MkSelect>
|
||||
<div v-if="type === '_driveFile_'" :class="$style.fileSelectorRoot">
|
||||
<MkButton :class="$style.fileSelectorButton" inline rounded primary @click="selectSound">{{ i18n.ts.selectFile }}</MkButton>
|
||||
<div :class="['_nowrap', !fileUrl && $style.fileNotSelected]">{{ friendlyFileName }}</div>
|
||||
</div>
|
||||
<MkRange v-model="volume" :min="0" :max="1" :step="0.05" :textConverter="(v) => `${Math.floor(v * 100)}%`">
|
||||
<template #label>{{ i18n.ts.volume }}</template>
|
||||
</MkRange>
|
||||
@@ -21,30 +25,149 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref, computed } from 'vue';
|
||||
import type { SoundType } from '@/scripts/sound.js';
|
||||
import MkSelect from '@/components/MkSelect.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkRange from '@/components/MkRange.vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { playFile, soundsTypes } from '@/scripts/sound.js';
|
||||
import * as os from '@/os.js';
|
||||
import { playFile, soundsTypes, getSoundDuration } from '@/scripts/sound.js';
|
||||
import { selectFile } from '@/scripts/select-file.js';
|
||||
|
||||
const props = defineProps<{
|
||||
type: string;
|
||||
type: SoundType;
|
||||
fileId?: string;
|
||||
fileUrl?: string;
|
||||
volume: number;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'update', result: { type: string; volume: number; }): void;
|
||||
(ev: 'update', result: { type: SoundType; fileId?: string; fileUrl?: string; volume: number; }): void;
|
||||
}>();
|
||||
|
||||
let type = $ref(props.type);
|
||||
let volume = $ref(props.volume);
|
||||
const type = ref<SoundType>(props.type);
|
||||
const fileId = ref(props.fileId);
|
||||
const fileUrl = ref(props.fileUrl);
|
||||
const fileName = ref<string>('');
|
||||
const volume = ref(props.volume);
|
||||
|
||||
if (type.value === '_driveFile_' && fileId.value) {
|
||||
const apiRes = await os.api('drive/files/show', {
|
||||
fileId: fileId.value,
|
||||
});
|
||||
fileName.value = apiRes.name;
|
||||
}
|
||||
|
||||
function getSoundTypeName(f: SoundType): string {
|
||||
switch (f) {
|
||||
case null:
|
||||
return i18n.ts.none;
|
||||
case '_driveFile_':
|
||||
return i18n.ts._soundSettings.driveFile;
|
||||
default:
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
const friendlyFileName = computed<string>(() => {
|
||||
if (fileName.value) {
|
||||
return fileName.value;
|
||||
}
|
||||
if (fileUrl.value) {
|
||||
return fileUrl.value;
|
||||
}
|
||||
|
||||
return i18n.ts._soundSettings.driveFileWarn;
|
||||
});
|
||||
|
||||
function selectSound(ev) {
|
||||
selectFile(ev.currentTarget ?? ev.target, i18n.ts._soundSettings.driveFile).then(async (file) => {
|
||||
if (!file.type.startsWith('audio')) {
|
||||
os.alert({
|
||||
type: 'warning',
|
||||
title: i18n.ts._soundSettings.driveFileTypeWarn,
|
||||
text: i18n.ts._soundSettings.driveFileTypeWarnDescription,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const duration = await getSoundDuration(file.url);
|
||||
if (duration >= 2000) {
|
||||
const { canceled } = await os.confirm({
|
||||
type: 'warning',
|
||||
title: i18n.ts._soundSettings.driveFileDurationWarn,
|
||||
text: i18n.ts._soundSettings.driveFileDurationWarnDescription,
|
||||
okText: i18n.ts.continue,
|
||||
cancelText: i18n.ts.cancel,
|
||||
});
|
||||
if (canceled) return;
|
||||
}
|
||||
|
||||
fileUrl.value = file.url;
|
||||
fileName.value = file.name;
|
||||
fileId.value = file.id;
|
||||
});
|
||||
}
|
||||
|
||||
function listen() {
|
||||
playFile(type, volume);
|
||||
if (type.value === '_driveFile_' && (!fileUrl.value || !fileId.value)) {
|
||||
os.alert({
|
||||
type: 'warning',
|
||||
text: i18n.ts._soundSettings.driveFileWarn,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
playFile(type.value === '_driveFile_' ? {
|
||||
type: '_driveFile_',
|
||||
fileId: fileId.value as string,
|
||||
fileUrl: fileUrl.value as string,
|
||||
volume: volume.value,
|
||||
} : {
|
||||
type: type.value,
|
||||
volume: volume.value,
|
||||
});
|
||||
}
|
||||
|
||||
function save() {
|
||||
emit('update', { type, volume });
|
||||
if (type.value === '_driveFile_' && !fileUrl.value) {
|
||||
os.alert({
|
||||
type: 'warning',
|
||||
text: i18n.ts._soundSettings.driveFileWarn,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (type.value !== '_driveFile_') {
|
||||
fileUrl.value = undefined;
|
||||
fileName.value = '';
|
||||
fileId.value = undefined;
|
||||
}
|
||||
|
||||
emit('update', {
|
||||
type: type.value,
|
||||
fileId: fileId.value,
|
||||
fileUrl: fileUrl.value,
|
||||
volume: volume.value,
|
||||
});
|
||||
|
||||
os.success();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style module>
|
||||
.fileSelectorRoot {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.fileSelectorButton {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.fileNotSelected {
|
||||
font-weight: 700;
|
||||
color: var(--infoWarnFg);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user