Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhance(frontend): 通知音にドライブのファイルを使用できるように #12447

Merged
merged 23 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8b1095f
(enhance) サウンドにドライブのファイルを使用できるように
kakkokari-gtyih Nov 25, 2023
fe26cc8
Update Changelog
kakkokari-gtyih Nov 25, 2023
e4736b5
fix
kakkokari-gtyih Nov 25, 2023
6ce6c84
fix design
kakkokari-gtyih Nov 25, 2023
5f5e651
fix design
kakkokari-gtyih Nov 25, 2023
b1dd22e
Merge branch 'develop' into enh-9403
kakkokari-gtyih Nov 26, 2023
3673f1a
Merge branch 'develop' into enh-9403
kakkokari-gtyih Nov 26, 2023
ce1de0f
Update store.ts
kakkokari-gtyih Nov 26, 2023
bf63393
Merge remote-tracking branch 'msky/develop' into enh-9403
kakkokari-gtyih Nov 26, 2023
1b5beb1
(fix) ファイル名表示
kakkokari-gtyih Nov 26, 2023
acd87bb
refactor
kakkokari-gtyih Nov 26, 2023
3a9e671
(refactor) better types
kakkokari-gtyih Nov 26, 2023
2672874
operationTypeとsoundTypeの混同を防止
kakkokari-gtyih Nov 26, 2023
e54c04f
Merge remote-tracking branch 'msky/develop' into enh-9403
kakkokari-gtyih Nov 26, 2023
a03e240
(refactor)
kakkokari-gtyih Nov 26, 2023
9394d2d
(fix)
kakkokari-gtyih Nov 26, 2023
1d4181c
enhance jsdoc
kakkokari-gtyih Nov 26, 2023
19a2ea3
Merge remote-tracking branch 'msky/develop' into enh-9403
kakkokari-gtyih Nov 26, 2023
160f872
Merge branch 'develop' into enh-9403
kakkokari-gtyih Nov 26, 2023
3efd0f6
driveFile -> _driveFile_
kakkokari-gtyih Nov 26, 2023
fb30a9d
Merge branch 'enh-9403' of https://github.com/kakkokari-gtyih/misskey…
kakkokari-gtyih Nov 26, 2023
da174bb
Merge branch 'develop' into enh-9403
kakkokari-gtyih Nov 26, 2023
46a70c4
Merge branch 'develop' into enh-9403
kakkokari-gtyih Nov 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
- Enhance: 絵文字のオートコンプリート機能強化 #12364
- Enhance: ユーザーのRawデータを表示するページが復活
- Enhance: リアクション選択時に音を鳴らせるように
- Enhance: サウンドにドライブのファイルを使用できるように
- fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
- Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
- Fix: コードエディタが正しく表示されない問題を修正
Expand Down
8 changes: 8 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1947,6 +1947,14 @@ export interface Locale {
"channel": string;
"reaction": string;
};
"_soundSettings": {
"driveFile": string;
"driveFileWarn": string;
"driveFileTypeWarn": string;
"driveFileTypeWarnDescription": string;
"driveFileDurationWarn": string;
"driveFileDurationWarnDescription": string;
};
"_ago": {
"future": string;
"justNow": string;
Expand Down
8 changes: 8 additions & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1852,6 +1852,14 @@ _sfx:
channel: "チャンネル通知"
reaction: "リアクション選択時"

_soundSettings:
driveFile: "ドライブの音声を使用"
driveFileWarn: "ドライブのファイルを選択してください"
driveFileTypeWarn: "このファイルは対応していません"
driveFileTypeWarnDescription: "音声ファイルを選択してください"
driveFileDurationWarn: "音声が長すぎます"
driveFileDurationWarnDescription: "長い音声を使用するとMisskeyの使用に支障をきたす可能性があります。それでも続行しますか?"

_ago:
future: "未来"
justNow: "たった今"
Expand Down
141 changes: 132 additions & 9 deletions packages/frontend/src/pages/settings/sounds.sound.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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>
28 changes: 21 additions & 7 deletions packages/frontend/src/pages/settings/sounds.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<FormSection>
<template #label>{{ i18n.ts.sounds }}</template>
<div class="_gaps_s">
<MkFolder v-for="type in soundsKeys" :key="type">
<MkFolder v-for="type in operationTypes" :key="type">
<template #label>{{ i18n.t('_sfx.' + type) }}</template>
<template #suffix>{{ sounds[type].type ?? i18n.ts.none }}</template>
<template #suffix>{{ getSoundTypeName(sounds[type].type) }}</template>

<XSound :type="sounds[type].type" :volume="sounds[type].volume" @update="(res) => updated(type, res)"/>
<XSound :type="sounds[type].type" :volume="sounds[type].volume" :fileId="sounds[type].fileId" :fileUrl="sounds[type].fileUrl" @update="(res) => updated(type, res)"/>
</MkFolder>
</div>
</FormSection>
Expand All @@ -33,23 +33,24 @@ SPDX-License-Identifier: AGPL-3.0-only

<script lang="ts" setup>
import { Ref, computed, ref } from 'vue';
import type { SoundType, OperationType } from '@/scripts/sound.js';
import type { SoundStore } from '@/store.js';
import XSound from './sounds.sound.vue';
import MkRange from '@/components/MkRange.vue';
import MkButton from '@/components/MkButton.vue';
import FormSection from '@/components/form/section.vue';
import MkFolder from '@/components/MkFolder.vue';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { operationTypes } from '@/scripts/sound.js';
import { defaultStore } from '@/store.js';
import MkSwitch from '@/components/MkSwitch.vue';

const notUseSound = computed(defaultStore.makeGetterSetter('sound_notUseSound'));
const useSoundOnlyWhenActive = computed(defaultStore.makeGetterSetter('sound_useSoundOnlyWhenActive'));
const masterVolume = computed(defaultStore.makeGetterSetter('sound_masterVolume'));

const soundsKeys = ['note', 'noteMy', 'notification', 'antenna', 'channel', 'reaction'] as const;

const sounds = ref<Record<typeof soundsKeys[number], Ref<any>>>({
const sounds = ref<Record<OperationType, Ref<SoundStore>>>({
note: defaultStore.reactiveState.sound_note,
noteMy: defaultStore.reactiveState.sound_noteMy,
notification: defaultStore.reactiveState.sound_notification,
Expand All @@ -58,9 +59,22 @@ const sounds = ref<Record<typeof soundsKeys[number], Ref<any>>>({
reaction: defaultStore.reactiveState.sound_reaction,
});

function getSoundTypeName(f: SoundType): string {
switch (f) {
case null:
return i18n.ts.none;
case '_driveFile_':
return i18n.ts._soundSettings.driveFile;
default:
return f;
}
}

async function updated(type: keyof typeof sounds.value, sound) {
const v = {
const v: SoundStore = {
type: sound.type,
fileId: sound.fileId,
fileUrl: sound.fileUrl,
volume: sound.volume,
};

Expand Down