diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 5fcd562720..a76ab1f2c5 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -424,6 +424,16 @@ deleteAll: "全て削除" showFixedPostForm: "タイムライン上部に投稿フォームを表示する" newNoteRecived: "新しいノートがあります" useNotificationsPopup: "通知一覧をポップアップで表示" +sounds: "サウンド" +listen: "聴く" +none: "なし" +volume: "音量" + +_sfx: + note: "ノート" + notification: "通知" + chat: "チャット" + chatBg: "チャット(バックグラウンド)" _ago: unknown: "謎" diff --git a/src/client/app.vue b/src/client/app.vue index a3290486a7..cfb17c05f9 100644 --- a/src/client/app.vue +++ b/src/client/app.vue @@ -93,7 +93,7 @@ <fa :icon="faEllipsisH" fixed-width/><span class="text">{{ $t('more') }}</span> <i v-if="$store.getters.isSignedIn && ($store.state.i.hasUnreadMentions || $store.state.i.hasUnreadSpecifiedNotes)"><fa :icon="faCircle"/></i> </button> - <router-link class="item" active-class="active" to="/settings"> + <router-link class="item" active-class="active" to="/preferences"> <fa :icon="faCog" fixed-width/><span class="text">{{ $t('settings') }}</span> </router-link> </div> @@ -578,13 +578,19 @@ export default Vue.extend({ onNotification(notification) { // TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない - this.$root.stream.send('readNotification', { - id: notification.id - }); + if (true) { + this.$root.stream.send('readNotification', { + id: notification.id + }); - this.$root.new(MkToast, { - notification - }); + this.$root.new(MkToast, { + notification + }); + } + + const audio = new Audio(`/assets/sounds/${this.$store.state.device.sfxNotification}.mp3`); + audio.volume = this.$store.state.device.sfxVolume; + audio.play(); }, onMousedown(e) { diff --git a/src/client/assets/sounds/aisha/1.mp3 b/src/client/assets/sounds/aisha/1.mp3 new file mode 100644 index 0000000000..d8e9a2f265 Binary files /dev/null and b/src/client/assets/sounds/aisha/1.mp3 differ diff --git a/src/client/assets/sounds/aisha/2.mp3 b/src/client/assets/sounds/aisha/2.mp3 new file mode 100644 index 0000000000..477c2eba43 Binary files /dev/null and b/src/client/assets/sounds/aisha/2.mp3 differ diff --git a/src/client/assets/sounds/aisha/3.mp3 b/src/client/assets/sounds/aisha/3.mp3 new file mode 100644 index 0000000000..fe0d8063df Binary files /dev/null and b/src/client/assets/sounds/aisha/3.mp3 differ diff --git a/src/client/assets/sounds/noizenecio/kick_gaba.mp3 b/src/client/assets/sounds/noizenecio/kick_gaba.mp3 new file mode 100644 index 0000000000..616b506c4f Binary files /dev/null and b/src/client/assets/sounds/noizenecio/kick_gaba.mp3 differ diff --git a/src/client/assets/sounds/syuilo/pope1.mp3 b/src/client/assets/sounds/syuilo/pope1.mp3 new file mode 100644 index 0000000000..585bb0407e Binary files /dev/null and b/src/client/assets/sounds/syuilo/pope1.mp3 differ diff --git a/src/client/assets/sounds/syuilo/pope2.mp3 b/src/client/assets/sounds/syuilo/pope2.mp3 new file mode 100644 index 0000000000..83865b989e Binary files /dev/null and b/src/client/assets/sounds/syuilo/pope2.mp3 differ diff --git a/src/client/assets/sounds/syuilo/waon.mp3 b/src/client/assets/sounds/syuilo/waon.mp3 new file mode 100644 index 0000000000..c5797f066e Binary files /dev/null and b/src/client/assets/sounds/syuilo/waon.mp3 differ diff --git a/src/client/components/timeline.vue b/src/client/components/timeline.vue index 407aeb2161..ba367bf23d 100644 --- a/src/client/components/timeline.vue +++ b/src/client/components/timeline.vue @@ -21,6 +21,11 @@ export default Vue.extend({ }, antenna: { required: false + }, + sound: { + type: Boolean, + required: false, + default: false, } }, @@ -46,6 +51,12 @@ export default Vue.extend({ const prepend = note => { (this.$refs.tl as any).prepend(note); + + if (this.sound) { + const audio = new Audio(`/assets/sounds/${this.$store.state.device.sfxNote}.mp3`); + audio.volume = this.$store.state.device.sfxVolume; + audio.play(); + } }; const onUserAdded = () => { diff --git a/src/client/components/ui/select.vue b/src/client/components/ui/select.vue index 3d22b8198e..6266fb5a2d 100644 --- a/src/client/components/ui/select.vue +++ b/src/client/components/ui/select.vue @@ -56,7 +56,7 @@ export default Vue.extend({ } }, filled(): boolean { - return this.v != '' && this.v != null; + return true; } }, mounted() { @@ -100,6 +100,7 @@ export default Vue.extend({ > .input { display: flex; + position: relative; &:before { content: ''; diff --git a/src/client/mios.ts b/src/client/mios.ts index 3bf026af7f..e0eb99769e 100644 --- a/src/client/mios.ts +++ b/src/client/mios.ts @@ -197,6 +197,10 @@ export default class MiOS extends EventEmitter { this.store.dispatch('mergeMe', { hasUnreadMessagingMessage: true }); + + const audio = new Audio(`/assets/sounds/${this.store.state.device.sfxChatBg}.mp3`); + audio.volume = this.store.state.device.sfxVolume; + audio.play(); }); main.on('readAllAntennas', () => { diff --git a/src/client/pages/index.home.vue b/src/client/pages/index.home.vue index c3bc71b114..14a385d1a8 100644 --- a/src/client/pages/index.home.vue +++ b/src/client/pages/index.home.vue @@ -19,7 +19,7 @@ <x-tutorial class="tutorial" v-if="$store.state.settings.tutorial != -1"/> <x-post-form class="post-form _panel" fixed v-if="$store.state.device.showFixedPostForm"/> - <x-timeline ref="tl" :key="src === 'list' ? `list:${list.id}` : src === 'antenna' ? `antenna:${antenna.id}` : src" :src="src" :list="list" :antenna="antenna" @before="before()" @after="after()" @queue="queueUpdated"/> + <x-timeline ref="tl" :key="src === 'list' ? `list:${list.id}` : src === 'antenna' ? `antenna:${antenna.id}` : src" :src="src" :list="list" :antenna="antenna" :sound="true" @before="before()" @after="after()" @queue="queueUpdated"/> </div> </template> diff --git a/src/client/pages/messaging-room.vue b/src/client/pages/messaging-room.vue index b2584393a0..aa0c4c93b8 100644 --- a/src/client/pages/messaging-room.vue +++ b/src/client/pages/messaging-room.vue @@ -185,11 +185,9 @@ export default Vue.extend({ onMessage(message) { // サウンドを再生する - if (this.$store.state.device.enableSounds) { - const sound = new Audio(`${url}/assets/message.mp3`); - sound.volume = this.$store.state.device.soundVolume; - sound.play(); - } + const audio = new Audio(`/assets/sounds/${this.$store.state.device.sfxChat}.mp3`); + audio.volume = this.$store.state.device.sfxVolume; + audio.play(); const isBottom = this.isBottom(); diff --git a/src/client/pages/settings/index.vue b/src/client/pages/preferences/index.vue similarity index 60% rename from src/client/pages/settings/index.vue rename to src/client/pages/preferences/index.vue index b3ef4d17b9..4193959188 100644 --- a/src/client/pages/settings/index.vue +++ b/src/client/pages/preferences/index.vue @@ -5,6 +5,36 @@ <x-theme/> + <section class="_card"> + <div class="_title"><fa :icon="faMusic"/> {{ $t('sounds') }}</div> + <div class="_content"> + {{ $t('volume') }} + <input type="range" v-model="sfxVolume" min="0" max="1" step="0.1"/> + </div> + <div class="_content"> + <mk-select v-model="sfxNote"> + <template #label>{{ $t('_sfx.note') }}</template> + <option v-for="sound in sounds" :value="sound" :key="sound">{{ sound || $t('none') }}</option> + <template #text><button class="_textButton" @click="listen(sfxNote)" v-if="sfxNote"><fa :icon="faPlay"/> {{ $t('listen') }}</button></template> + </mk-select> + <mk-select v-model="sfxNotification"> + <template #label>{{ $t('_sfx.notification') }}</template> + <option v-for="sound in sounds" :value="sound" :key="sound">{{ sound || $t('none') }}</option> + <template #text><button class="_textButton" @click="listen(sfxNotification)" v-if="sfxNotification"><fa :icon="faPlay"/> {{ $t('listen') }}</button></template> + </mk-select> + <mk-select v-model="sfxChat"> + <template #label>{{ $t('_sfx.chat') }}</template> + <option v-for="sound in sounds" :value="sound" :key="sound">{{ sound || $t('none') }}</option> + <template #text><button class="_textButton" @click="listen(sfxChat)" v-if="sfxChat"><fa :icon="faPlay"/> {{ $t('listen') }}</button></template> + </mk-select> + <mk-select v-model="sfxChatBg"> + <template #label>{{ $t('_sfx.chatBg') }}</template> + <option v-for="sound in sounds" :value="sound" :key="sound">{{ sound || $t('none') }}</option> + <template #text><button class="_textButton" @click="listen(sfxChatBg)" v-if="sfxChatBg"><fa :icon="faPlay"/> {{ $t('listen') }}</button></template> + </mk-select> + </div> + </section> + <section class="_card"> <div class="_title"><fa :icon="faCog"/> {{ $t('accessibility') }}</div> <div class="_content"> @@ -45,7 +75,7 @@ <script lang="ts"> import Vue from 'vue'; -import { faImage, faCog } from '@fortawesome/free-solid-svg-icons'; +import { faImage, faCog, faMusic, faPlay } from '@fortawesome/free-solid-svg-icons'; import MkInput from '../../components/ui/input.vue'; import MkButton from '../../components/ui/button.vue'; import MkSwitch from '../../components/ui/switch.vue'; @@ -55,6 +85,17 @@ import XTheme from './theme.vue'; import i18n from '../../i18n'; import { langs } from '../../config'; +const sounds = [ + null, + 'syuilo/pope1', + 'syuilo/pope2', + 'syuilo/waon', + 'aisha/1', + 'aisha/2', + 'aisha/3', + 'noizenecio/kick_gaba', +]; + export default Vue.extend({ i18n, @@ -78,7 +119,8 @@ export default Vue.extend({ langs, lang: localStorage.getItem('lang'), fontSize: localStorage.getItem('fontSize'), - faImage, faCog + sounds, + faImage, faCog, faMusic, faPlay } }, @@ -117,6 +159,31 @@ export default Vue.extend({ get() { return this.$store.state.device.useNotificationsPopup; }, set(value) { this.$store.commit('device/set', { key: 'useNotificationsPopup', value }); } }, + + sfxVolume: { + get() { return this.$store.state.device.sfxVolume; }, + set(value) { this.$store.commit('device/set', { key: 'sfxVolume', value }); } + }, + + sfxNote: { + get() { return this.$store.state.device.sfxNote; }, + set(value) { this.$store.commit('device/set', { key: 'sfxNote', value }); } + }, + + sfxNotification: { + get() { return this.$store.state.device.sfxNotification; }, + set(value) { this.$store.commit('device/set', { key: 'sfxNotification', value }); } + }, + + sfxChat: { + get() { return this.$store.state.device.sfxChat; }, + set(value) { this.$store.commit('device/set', { key: 'sfxChat', value }); } + }, + + sfxChatBg: { + get() { return this.$store.state.device.sfxChatBg; }, + set(value) { this.$store.commit('device/set', { key: 'sfxChatBg', value }); } + }, }, watch: { @@ -137,6 +204,12 @@ export default Vue.extend({ }, methods: { + listen(sound) { + const audio = new Audio(`/assets/sounds/${sound}.mp3`); + audio.volume = this.$store.state.device.sfxVolume; + audio.play(); + }, + cacheClear() { // Clear cache (service worker) try { diff --git a/src/client/pages/settings/theme.vue b/src/client/pages/preferences/theme.vue similarity index 100% rename from src/client/pages/settings/theme.vue rename to src/client/pages/preferences/theme.vue diff --git a/src/client/router.ts b/src/client/router.ts index 86ef4c7056..3b2bbe9c80 100644 --- a/src/client/router.ts +++ b/src/client/router.ts @@ -46,7 +46,7 @@ export const router = new VueRouter({ { path: '/my/groups', component: page('my-groups/index') }, { path: '/my/groups/:group', component: page('my-groups/group') }, { path: '/my/antennas', component: page('my-antennas/index') }, - { path: '/settings', component: page('settings/index') }, + { path: '/preferences', component: page('preferences/index') }, { path: '/instance', component: page('instance/index') }, { path: '/instance/emojis', component: page('instance/emojis') }, { path: '/instance/users', component: page('instance/users') }, diff --git a/src/client/store.ts b/src/client/store.ts index 2d84b7b318..4a329a0ebb 100644 --- a/src/client/store.ts +++ b/src/client/store.ts @@ -41,6 +41,11 @@ const defaultDeviceSettings = { imageNewTab: false, showFixedPostForm: false, useNotificationsPopup: true, + sfxVolume: 0.3, + sfxNote: 'syuilo/pope1', + sfxNotification: 'syuilo/pope2', + sfxChat: 'syuilo/waon', + sfxChatBg: null, userData: {}, };