enhance(client): improve ux of sound settings
This commit is contained in:
		
							parent
							
								
									a9a245b461
								
							
						
					
					
						commit
						d970534d92
					
				
					 4 changed files with 89 additions and 69 deletions
				
			
		|  | @ -509,6 +509,7 @@ deleteAll: "全て削除" | ||||||
| showFixedPostForm: "タイムライン上部に投稿フォームを表示する" | showFixedPostForm: "タイムライン上部に投稿フォームを表示する" | ||||||
| newNoteRecived: "新しいノートがあります" | newNoteRecived: "新しいノートがあります" | ||||||
| sounds: "サウンド" | sounds: "サウンド" | ||||||
|  | sound: "サウンド" | ||||||
| listen: "聴く" | listen: "聴く" | ||||||
| none: "なし" | none: "なし" | ||||||
| showInPage: "ページで表示" | showInPage: "ページで表示" | ||||||
|  |  | ||||||
							
								
								
									
										45
									
								
								packages/client/src/pages/settings/sounds.sound.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								packages/client/src/pages/settings/sounds.sound.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | ||||||
|  | <template> | ||||||
|  | <div class="_formRoot"> | ||||||
|  | 	<FormSelect 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> | ||||||
|  | 	</FormSelect> | ||||||
|  | 	<FormRange v-model="volume" :min="0" :max="1" :step="0.05" :text-converter="(v) => `${Math.floor(v * 100)}%`" class="_formBlock"> | ||||||
|  | 		<template #label>{{ i18n.ts.volume }}</template> | ||||||
|  | 	</FormRange> | ||||||
|  | 
 | ||||||
|  | 	<div style="display: flex; gap: var(--margin); flex-wrap: wrap;"> | ||||||
|  | 		<FormButton inline @click="listen"><i class="ti ti-player-play"></i> {{ i18n.ts.listen }}</FormButton> | ||||||
|  | 		<FormButton inline primary @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</FormButton> | ||||||
|  | 	</div> | ||||||
|  | </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import { } from 'vue'; | ||||||
|  | import FormSelect from '@/components/form/select.vue'; | ||||||
|  | import FormButton from '@/components/MkButton.vue'; | ||||||
|  | import FormRange from '@/components/form/range.vue'; | ||||||
|  | import { i18n } from '@/i18n'; | ||||||
|  | import { playFile, soundsTypes } from '@/scripts/sound'; | ||||||
|  | 
 | ||||||
|  | const props = defineProps<{ | ||||||
|  | 	type: string; | ||||||
|  | 	volume: number; | ||||||
|  | }>(); | ||||||
|  | 
 | ||||||
|  | const emit = defineEmits<{ | ||||||
|  | 	(ev: 'update', result: { type: string; volume: number; }): void; | ||||||
|  | }>(); | ||||||
|  | 
 | ||||||
|  | let type = $ref(props.type); | ||||||
|  | let volume = $ref(props.volume); | ||||||
|  | 
 | ||||||
|  | function listen() { | ||||||
|  | 	playFile(type, volume); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function save() { | ||||||
|  | 	emit('update', { type, volume }); | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | @ -6,11 +6,12 @@ | ||||||
| 
 | 
 | ||||||
| 	<FormSection> | 	<FormSection> | ||||||
| 		<template #label>{{ i18n.ts.sounds }}</template> | 		<template #label>{{ i18n.ts.sounds }}</template> | ||||||
| 		<FormLink v-for="type in Object.keys(sounds)" :key="type" style="margin-bottom: 8px;" @click="edit(type)"> | 		<FormFolder v-for="type in Object.keys(sounds)" :key="type" style="margin-bottom: 8px;"> | ||||||
| 			{{ $t('_sfx.' + type) }} | 			<template #label>{{ $t('_sfx.' + type) }}</template> | ||||||
| 			<template #suffix>{{ sounds[type].type || i18n.ts.none }}</template> | 			<template #suffix>{{ sounds[type].type ?? i18n.ts.none }}</template> | ||||||
| 			<template #suffixIcon><i class="ti ti-chevron-down"></i></template> | 
 | ||||||
| 		</FormLink> | 			<XSound :type="sounds[type].type" :volume="sounds[type].volume" @update="(res) => updated(type, res)"/> | ||||||
|  | 		</FormFolder> | ||||||
| 	</FormSection> | 	</FormSection> | ||||||
| 
 | 
 | ||||||
| 	<FormButton danger class="_formBlock" @click="reset()"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</FormButton> | 	<FormButton danger class="_formBlock" @click="reset()"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</FormButton> | ||||||
|  | @ -19,10 +20,12 @@ | ||||||
| 
 | 
 | ||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
| import { computed, ref } from 'vue'; | import { computed, ref } from 'vue'; | ||||||
|  | import XSound from './sounds.sound.vue'; | ||||||
| import FormRange from '@/components/form/range.vue'; | import FormRange from '@/components/form/range.vue'; | ||||||
| import FormButton from '@/components/MkButton.vue'; | import FormButton from '@/components/MkButton.vue'; | ||||||
| import FormLink from '@/components/form/link.vue'; | import FormLink from '@/components/form/link.vue'; | ||||||
| import FormSection from '@/components/form/section.vue'; | import FormSection from '@/components/form/section.vue'; | ||||||
|  | import FormFolder from '@/components/form/folder.vue'; | ||||||
| import * as os from '@/os'; | import * as os from '@/os'; | ||||||
| import { ColdDeviceStorage } from '@/store'; | import { ColdDeviceStorage } from '@/store'; | ||||||
| import { playFile } from '@/scripts/sound'; | import { playFile } from '@/scripts/sound'; | ||||||
|  | @ -50,71 +53,10 @@ const sounds = ref({ | ||||||
| 	channel: ColdDeviceStorage.get('sound_channel'), | 	channel: ColdDeviceStorage.get('sound_channel'), | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const soundsTypes = [ | async function updated(type, sound) { | ||||||
| 	null, |  | ||||||
| 	'syuilo/up', |  | ||||||
| 	'syuilo/down', |  | ||||||
| 	'syuilo/pope1', |  | ||||||
| 	'syuilo/pope2', |  | ||||||
| 	'syuilo/waon', |  | ||||||
| 	'syuilo/popo', |  | ||||||
| 	'syuilo/triple', |  | ||||||
| 	'syuilo/poi1', |  | ||||||
| 	'syuilo/poi2', |  | ||||||
| 	'syuilo/pirori', |  | ||||||
| 	'syuilo/pirori-wet', |  | ||||||
| 	'syuilo/pirori-square-wet', |  | ||||||
| 	'syuilo/square-pico', |  | ||||||
| 	'syuilo/reverved', |  | ||||||
| 	'syuilo/ryukyu', |  | ||||||
| 	'syuilo/kick', |  | ||||||
| 	'syuilo/snare', |  | ||||||
| 	'syuilo/queue-jammed', |  | ||||||
| 	'aisha/1', |  | ||||||
| 	'aisha/2', |  | ||||||
| 	'aisha/3', |  | ||||||
| 	'noizenecio/kick_gaba1', |  | ||||||
| 	'noizenecio/kick_gaba2', |  | ||||||
| 	'noizenecio/kick_gaba3', |  | ||||||
| 	'noizenecio/kick_gaba4', |  | ||||||
| 	'noizenecio/kick_gaba5', |  | ||||||
| 	'noizenecio/kick_gaba6', |  | ||||||
| 	'noizenecio/kick_gaba7', |  | ||||||
| ]; |  | ||||||
| 
 |  | ||||||
| async function edit(type) { |  | ||||||
| 	const { canceled, result } = await os.form(i18n.t('_sfx.' + type), { |  | ||||||
| 		type: { |  | ||||||
| 			type: 'enum', |  | ||||||
| 			enum: soundsTypes.map(x => ({ |  | ||||||
| 				value: x, |  | ||||||
| 				label: x == null ? i18n.ts.none : x, |  | ||||||
| 			})), |  | ||||||
| 			label: i18n.ts.sound, |  | ||||||
| 			default: sounds.value[type].type, |  | ||||||
| 		}, |  | ||||||
| 		volume: { |  | ||||||
| 			type: 'range', |  | ||||||
| 			min: 0, |  | ||||||
| 			max: 1, |  | ||||||
| 			step: 0.05, |  | ||||||
| 			textConverter: (v) => `${Math.floor(v * 100)}%`, |  | ||||||
| 			label: i18n.ts.volume, |  | ||||||
| 			default: sounds.value[type].volume, |  | ||||||
| 		}, |  | ||||||
| 		listen: { |  | ||||||
| 			type: 'button', |  | ||||||
| 			content: i18n.ts.listen, |  | ||||||
| 			action: (_, values) => { |  | ||||||
| 				playFile(values.type, values.volume); |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 	}); |  | ||||||
| 	if (canceled) return; |  | ||||||
| 
 |  | ||||||
| 	const v = { | 	const v = { | ||||||
| 		type: result.type, | 		type: sound.type, | ||||||
| 		volume: result.volume, | 		volume: sound.volume, | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| 	ColdDeviceStorage.set('sound_' + type, v); | 	ColdDeviceStorage.set('sound_' + type, v); | ||||||
|  |  | ||||||
|  | @ -2,6 +2,38 @@ import { ColdDeviceStorage } from '@/store'; | ||||||
| 
 | 
 | ||||||
| const cache = new Map<string, HTMLAudioElement>(); | const cache = new Map<string, HTMLAudioElement>(); | ||||||
| 
 | 
 | ||||||
|  | export const soundsTypes = [ | ||||||
|  | 	null, | ||||||
|  | 	'syuilo/up', | ||||||
|  | 	'syuilo/down', | ||||||
|  | 	'syuilo/pope1', | ||||||
|  | 	'syuilo/pope2', | ||||||
|  | 	'syuilo/waon', | ||||||
|  | 	'syuilo/popo', | ||||||
|  | 	'syuilo/triple', | ||||||
|  | 	'syuilo/poi1', | ||||||
|  | 	'syuilo/poi2', | ||||||
|  | 	'syuilo/pirori', | ||||||
|  | 	'syuilo/pirori-wet', | ||||||
|  | 	'syuilo/pirori-square-wet', | ||||||
|  | 	'syuilo/square-pico', | ||||||
|  | 	'syuilo/reverved', | ||||||
|  | 	'syuilo/ryukyu', | ||||||
|  | 	'syuilo/kick', | ||||||
|  | 	'syuilo/snare', | ||||||
|  | 	'syuilo/queue-jammed', | ||||||
|  | 	'aisha/1', | ||||||
|  | 	'aisha/2', | ||||||
|  | 	'aisha/3', | ||||||
|  | 	'noizenecio/kick_gaba1', | ||||||
|  | 	'noizenecio/kick_gaba2', | ||||||
|  | 	'noizenecio/kick_gaba3', | ||||||
|  | 	'noizenecio/kick_gaba4', | ||||||
|  | 	'noizenecio/kick_gaba5', | ||||||
|  | 	'noizenecio/kick_gaba6', | ||||||
|  | 	'noizenecio/kick_gaba7', | ||||||
|  | ] as const; | ||||||
|  | 
 | ||||||
| export function getAudio(file: string, useCache = true): HTMLAudioElement { | export function getAudio(file: string, useCache = true): HTMLAudioElement { | ||||||
| 	let audio: HTMLAudioElement; | 	let audio: HTMLAudioElement; | ||||||
| 	if (useCache && cache.has(file)) { | 	if (useCache && cache.has(file)) { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue