summaryrefslogtreecommitdiff
path: root/src/client/pages
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2020-08-18 22:44:21 +0900
committerGitHub <noreply@github.com>2020-08-18 22:44:21 +0900
commit9855405b8989713b81709fc1700e2ead97423467 (patch)
tree54254d2159378d1903e962f0fb37c799bb0f4464 /src/client/pages
parentSign (request-target) Fix #6652 (#6656) (diff)
downloadmisskey-9855405b8989713b81709fc1700e2ead97423467.tar.gz
misskey-9855405b8989713b81709fc1700e2ead97423467.tar.bz2
misskey-9855405b8989713b81709fc1700e2ead97423467.zip
Channel (#6621)
* wip * wip * wip * wip * wip * wip * wip * wip * wop * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * add notes * wip * wip * wip * wip * sound * wip * add kick_gaba2 * wip
Diffstat (limited to 'src/client/pages')
-rw-r--r--src/client/pages/channel-editor.vue128
-rw-r--r--src/client/pages/channel.vue190
-rw-r--r--src/client/pages/channels.vue86
-rw-r--r--src/client/pages/index.home.vue66
-rw-r--r--src/client/pages/messaging/messaging-room.form.vue8
-rw-r--r--src/client/pages/my-settings/index.vue24
-rw-r--r--src/client/pages/preferences/index.vue20
7 files changed, 481 insertions, 41 deletions
diff --git a/src/client/pages/channel-editor.vue b/src/client/pages/channel-editor.vue
new file mode 100644
index 0000000000..0178662119
--- /dev/null
+++ b/src/client/pages/channel-editor.vue
@@ -0,0 +1,128 @@
+<template>
+<div>
+ <portal to="icon"><fa :icon="faSatelliteDish"/></portal>
+ <portal to="title">{{ channelId ? $t('_channel.edit') : $t('_channel.create') }}</portal>
+
+ <div class="_card">
+ <div class="_content">
+ <mk-input v-model="name">{{ $t('name') }}</mk-input>
+
+ <mk-textarea v-model="description">{{ $t('description') }}</mk-textarea>
+
+ <div class="banner">
+ <mk-button v-if="bannerId == null" @click="setBannerImage"><fa :icon="faPlus"/> {{ $t('_channel.setBanner') }}</mk-button>
+ <div v-else-if="bannerUrl">
+ <img :src="bannerUrl" style="width: 100%;"/>
+ <mk-button @click="removeBannerImage()"><fa :icon="faTrashAlt"/> {{ $t('_channel.removeBanner') }}</mk-button>
+ </div>
+ </div>
+ </div>
+ <div class="_footer">
+ <mk-button @click="save()" primary><fa :icon="faSave"/> {{ channelId ? $t('save') : $t('create') }}</mk-button>
+ </div>
+ </div>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import { faPlus, faSatelliteDish } from '@fortawesome/free-solid-svg-icons';
+import { faSave, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
+import MkTextarea from '../components/ui/textarea.vue';
+import MkButton from '../components/ui/button.vue';
+import MkInput from '../components/ui/input.vue';
+import { selectFile } from '../scripts/select-file';
+
+export default Vue.extend({
+ components: {
+ MkTextarea, MkButton, MkInput,
+ },
+
+ props: {
+ channelId: {
+ type: String,
+ required: false
+ },
+ },
+
+ data() {
+ return {
+ channel: null,
+ name: null,
+ description: null,
+ bannerUrl: null,
+ bannerId: null,
+ faSave, faTrashAlt, faPlus,faSatelliteDish,
+ };
+ },
+
+ watch: {
+ async bannerId() {
+ if (this.bannerId == null) {
+ this.bannerUrl = null;
+ } else {
+ this.bannerUrl = (await this.$root.api('drive/files/show', {
+ fileId: this.bannerId,
+ })).url;
+ }
+ },
+ },
+
+ async created() {
+ if (this.channelId) {
+ this.channel = await this.$root.api('channels/show', {
+ channelId: this.channelId,
+ });
+
+ this.name = this.channel.name;
+ this.description = this.channel.description;
+ this.bannerId = this.channel.bannerId;
+ this.bannerUrl = this.channel.bannerUrl;
+ }
+ },
+
+ methods: {
+ save() {
+ const params = {
+ name: this.name,
+ description: this.description,
+ bannerId: this.bannerId,
+ };
+
+ if (this.channelId) {
+ params.channelId = this.channelId;
+ this.$root.api('channels/update', params)
+ .then(channel => {
+ this.$root.dialog({
+ type: 'success',
+ iconOnly: true, autoClose: true
+ });
+ });
+ } else {
+ this.$root.api('channels/create', params)
+ .then(channel => {
+ this.$root.dialog({
+ type: 'success',
+ iconOnly: true, autoClose: true
+ });
+ this.$router.push(`/channels/${channel.id}`);
+ });
+ }
+ },
+
+ setBannerImage(e) {
+ selectFile(this, e.currentTarget || e.target, null, false).then(file => {
+ this.bannerId = file.id;
+ });
+ },
+
+ removeBannerImage() {
+ this.bannerId = null;
+ }
+ }
+});
+</script>
+
+<style lang="scss" scoped>
+
+</style>
diff --git a/src/client/pages/channel.vue b/src/client/pages/channel.vue
new file mode 100644
index 0000000000..69631af74b
--- /dev/null
+++ b/src/client/pages/channel.vue
@@ -0,0 +1,190 @@
+<template>
+<div v-if="channel">
+ <portal to="icon"><fa :icon="faSatelliteDish"/></portal>
+ <portal to="title">{{ channel.name }}</portal>
+
+ <div class="wpgynlbz _panel _vMargin" :class="{ hide: !showBanner }">
+ <x-channel-follow-button :channel="channel" :full="true" class="subscribe"/>
+ <button class="_button toggle" @click="() => showBanner = !showBanner">
+ <template v-if="showBanner"><fa :icon="faAngleUp"/></template>
+ <template v-else><fa :icon="faAngleDown"/></template>
+ </button>
+ <div class="hideOverlay" v-if="!showBanner">
+ </div>
+ <div :style="{ backgroundImage: channel.bannerUrl ? `url(${channel.bannerUrl})` : null }" class="banner">
+ <div class="status">
+ <div><fa :icon="faUsers" fixed-width/><i18n path="_channel.usersCount" tag="span" style="margin-left: 4px;"><b place="n">{{ channel.usersCount }}</b></i18n></div>
+ <div><fa :icon="faPencilAlt" fixed-width/><i18n path="_channel.notesCount" tag="span" style="margin-left: 4px;"><b place="n">{{ channel.notesCount }}</b></i18n></div>
+ </div>
+ <div class="fade"></div>
+ </div>
+ <div class="description" v-if="channel.description">
+ <mfm :text="channel.description" :is-note="false" :i="$store.state.i"/>
+ </div>
+ </div>
+
+ <x-post-form :channel="channel" class="post-form _panel _vMargin" fixed/>
+
+ <x-timeline class="_vMargin" src="channel" :channel="channelId" @before="before" @after="after"/>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import { faSatelliteDish, faUsers, faPencilAlt, faAngleUp, faAngleDown } from '@fortawesome/free-solid-svg-icons';
+import { } from '@fortawesome/free-regular-svg-icons';
+import MkContainer from '../components/ui/container.vue';
+import XPostForm from '../components/post-form.vue';
+import XTimeline from '../components/timeline.vue';
+import XChannelFollowButton from '../components/channel-follow-button.vue';
+
+export default Vue.extend({
+ metaInfo() {
+ return {
+ title: this.$t('channel') as string
+ };
+ },
+
+ components: {
+ MkContainer,
+ XPostForm,
+ XTimeline,
+ XChannelFollowButton
+ },
+
+ props: {
+ channelId: {
+ type: String,
+ required: true
+ }
+ },
+
+ data() {
+ return {
+ channel: null,
+ showBanner: true,
+ pagination: {
+ endpoint: 'channels/timeline',
+ limit: 10,
+ params: () => ({
+ channelId: this.channelId,
+ })
+ },
+ faSatelliteDish, faUsers, faPencilAlt, faAngleUp, faAngleDown,
+ };
+ },
+
+ watch: {
+ channelId: {
+ async handler() {
+ this.channel = await this.$root.api('channels/show', {
+ channelId: this.channelId,
+ });
+ },
+ immediate: true
+ }
+ },
+
+ created() {
+
+ },
+});
+</script>
+
+<style lang="scss" scoped>
+.wpgynlbz {
+ > .subscribe {
+ position: absolute;
+ z-index: 1;
+ top: 16px;
+ left: 16px;
+ }
+
+ > .toggle {
+ position: absolute;
+ z-index: 2;
+ top: 8px;
+ right: 8px;
+ font-size: 1.2em;
+ width: 48px;
+ height: 48px;
+ color: #fff;
+ background: rgba(0, 0, 0, 0.5);
+ border-radius: 100%;
+
+ > [data-icon] {
+ vertical-align: middle;
+ }
+ }
+
+ > .banner {
+ position: relative;
+ height: 200px;
+ background-position: center;
+ background-size: cover;
+
+ > .fade {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 64px;
+ background: linear-gradient(0deg, var(--panel), var(--X15));
+ }
+
+ > .status {
+ position: absolute;
+ z-index: 1;
+ bottom: 16px;
+ right: 16px;
+ padding: 8px 12px;
+ font-size: 80%;
+ background: rgba(0, 0, 0, 0.7);
+ border-radius: 6px;
+ color: #fff;
+ }
+ }
+
+ > .description {
+ padding: 16px;
+ }
+
+ > .hideOverlay {
+ position: absolute;
+ z-index: 1;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ -webkit-backdrop-filter: blur(16px);
+ backdrop-filter: blur(16px);
+ background: rgba(0, 0, 0, 0.3);
+ }
+
+ &.hide {
+ > .subscribe {
+ display: none;
+ }
+
+ > .toggle {
+ top: 0;
+ right: 0;
+ height: 100%;
+ background: transparent;
+ }
+
+ > .banner {
+ height: 42px;
+ filter: blur(8px);
+
+ > * {
+ display: none;
+ }
+ }
+
+ > .description {
+ display: none;
+ }
+ }
+}
+</style>
diff --git a/src/client/pages/channels.vue b/src/client/pages/channels.vue
new file mode 100644
index 0000000000..85804141f1
--- /dev/null
+++ b/src/client/pages/channels.vue
@@ -0,0 +1,86 @@
+<template>
+<div>
+ <portal to="icon"><fa :icon="faSatelliteDish"/></portal>
+ <portal to="title">{{ $t('channel') }}</portal>
+
+ <mk-tab v-model="tab" :items="[{ label: $t('_channel.featured'), value: 'featured', icon: faFireAlt }, { label: $t('_channel.following'), value: 'following', icon: faHeart }, { label: $t('_channel.owned'), value: 'owned', icon: faEdit }]"/>
+
+ <div class="grwlizim featured" v-if="tab === 'featured'">
+ <mk-pagination :pagination="featuredPagination" #default="{items}">
+ <mk-channel-preview v-for="channel in items" class="uveselbe" :channel="channel" :key="channel.id"/>
+ </mk-pagination>
+ </div>
+
+ <div class="grwlizim following" v-if="tab === 'following'">
+ <mk-pagination :pagination="followingPagination" #default="{items}">
+ <mk-channel-preview v-for="channel in items" class="uveselbe" :channel="channel" :key="channel.id"/>
+ </mk-pagination>
+ </div>
+
+ <div class="grwlizim owned" v-if="tab === 'owned'">
+ <mk-button class="new" @click="create()"><fa :icon="faPlus"/></mk-button>
+ <mk-pagination :pagination="ownedPagination" #default="{items}">
+ <mk-channel-preview v-for="channel in items" class="uveselbe" :channel="channel" :key="channel.id"/>
+ </mk-pagination>
+ </div>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import { faSatelliteDish, faPlus, faEdit, faFireAlt } from '@fortawesome/free-solid-svg-icons';
+import { faHeart } from '@fortawesome/free-regular-svg-icons';
+import MkChannelPreview from '../components/channel-preview.vue';
+import MkPagination from '../components/ui/pagination.vue';
+import MkButton from '../components/ui/button.vue';
+import MkTab from '../components/tab.vue';
+
+export default Vue.extend({
+ components: {
+ MkChannelPreview, MkPagination, MkButton, MkTab
+ },
+ data() {
+ return {
+ tab: 'featured',
+ featuredPagination: {
+ endpoint: 'channels/featured',
+ limit: 5,
+ },
+ followingPagination: {
+ endpoint: 'channels/followed',
+ limit: 5,
+ },
+ ownedPagination: {
+ endpoint: 'channels/owned',
+ limit: 5,
+ },
+ faSatelliteDish, faPlus, faEdit, faHeart, faFireAlt
+ };
+ },
+ methods: {
+ create() {
+ this.$router.push(`/channels/new`);
+ }
+ }
+});
+</script>
+
+<style lang="scss" scoped>
+.grwlizim {
+ padding: 16px 0;
+
+ &.my .uveselbe:first-child {
+ margin-top: 16px;
+ }
+
+ .uveselbe:not(:last-child) {
+ margin-bottom: 8px;
+ }
+
+ @media (min-width: 500px) {
+ .uveselbe:not(:last-child) {
+ margin-bottom: 16px;
+ }
+ }
+}
+</style>
diff --git a/src/client/pages/index.home.vue b/src/client/pages/index.home.vue
index b40086c310..d3f60ea910 100644
--- a/src/client/pages/index.home.vue
+++ b/src/client/pages/index.home.vue
@@ -2,14 +2,15 @@
<div class="mk-home" v-hotkey.global="keymap">
<portal to="header" v-if="showTitle">
<button @click="choose" class="_button _kjvfvyph_">
- <i><fa v-if="$store.state.i.hasUnreadAntenna" :icon="faCircle"/></i>
+ <i><fa v-if="$store.state.i.hasUnreadAntenna || $store.state.i.hasUnreadChannel" :icon="faCircle"/></i>
<fa v-if="src === 'home'" :icon="faHome"/>
<fa v-if="src === 'local'" :icon="faComments"/>
<fa v-if="src === 'social'" :icon="faShareAlt"/>
<fa v-if="src === 'global'" :icon="faGlobe"/>
<fa v-if="src === 'list'" :icon="faListUl"/>
<fa v-if="src === 'antenna'" :icon="faSatellite"/>
- <span style="margin-left: 8px;">{{ src === 'list' ? list.name : src === 'antenna' ? antenna.name : $t('_timelines.' + src) }}</span>
+ <fa v-if="src === 'channel'" :icon="faSatelliteDish"/>
+ <span style="margin-left: 8px;">{{ src === 'list' ? list.name : src === 'antenna' ? antenna.name : src === 'channel' ? channel.name : $t('_timelines.' + src) }}</span>
<fa :icon="menuOpened ? faAngleUp : faAngleDown" style="margin-left: 8px;"/>
</button>
</portal>
@@ -19,13 +20,13 @@
<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 ? list.id : null" :antenna="antenna ? antenna.id : null" :sound="true" @before="before()" @after="after()" @queue="queueUpdated"/>
+ <x-timeline ref="tl" :key="src === 'list' ? `list:${list.id}` : src === 'antenna' ? `antenna:${antenna.id}` : src === 'channel' ? `channel:${channel.id}` : src" :src="src" :list="list ? list.id : null" :antenna="antenna ? antenna.id : null" :channel="channel ? channel.id : null" :sound="true" @before="before()" @after="after()" @queue="queueUpdated"/>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
-import { faAngleDown, faAngleUp, faHome, faShareAlt, faGlobe, faListUl, faSatellite, faCircle } from '@fortawesome/free-solid-svg-icons';
+import { faAngleDown, faAngleUp, faHome, faShareAlt, faGlobe, faListUl, faSatellite, faSatelliteDish, faCircle } from '@fortawesome/free-solid-svg-icons';
import { faComments } from '@fortawesome/free-regular-svg-icons';
import Progress from '../scripts/loading';
import XTimeline from '../components/timeline.vue';
@@ -57,10 +58,11 @@ export default Vue.extend({
src: 'home',
list: null,
antenna: null,
+ channel: null,
menuOpened: false,
queue: 0,
width: 0,
- faAngleDown, faAngleUp, faHome, faShareAlt, faGlobe, faComments, faListUl, faSatellite, faCircle
+ faAngleDown, faAngleUp, faHome, faShareAlt, faGlobe, faComments, faListUl, faSatellite, faSatelliteDish, faCircle
};
},
@@ -79,16 +81,20 @@ export default Vue.extend({
watch: {
src() {
this.showNav = false;
- this.saveSrc();
},
list(x) {
this.showNav = false;
- this.saveSrc();
if (x != null) this.antenna = null;
+ if (x != null) this.channel = null;
},
antenna(x) {
this.showNav = false;
- this.saveSrc();
+ if (x != null) this.list = null;
+ if (x != null) this.channel = null;
+ },
+ channel(x) {
+ this.showNav = false;
+ if (x != null) this.antenna = null;
if (x != null) this.list = null;
},
},
@@ -99,6 +105,8 @@ export default Vue.extend({
this.list = this.$store.state.deviceUser.tl.arg;
} else if (this.src === 'antenna') {
this.antenna = this.$store.state.deviceUser.tl.arg;
+ } else if (this.src === 'channel') {
+ this.channel = this.$store.state.deviceUser.tl.arg;
}
},
@@ -127,9 +135,10 @@ export default Vue.extend({
async choose(ev) {
if (this.meta == null) return;
this.menuOpened = true;
- const [antennas, lists] = await Promise.all([
+ const [antennas, lists, channels] = await Promise.all([
this.$root.api('antennas/list'),
- this.$root.api('users/lists/list')
+ this.$root.api('users/lists/list'),
+ this.$root.api('channels/followed'),
]);
const antennaItems = antennas.map(antenna => ({
text: antenna.name,
@@ -137,7 +146,8 @@ export default Vue.extend({
indicate: antenna.hasUnreadNote,
action: () => {
this.antenna = antenna;
- this.setSrc('antenna');
+ this.src = 'antenna';
+ this.saveSrc();
}
}));
const listItems = lists.map(list => ({
@@ -145,27 +155,40 @@ export default Vue.extend({
icon: faListUl,
action: () => {
this.list = list;
- this.setSrc('list');
+ this.src = 'list';
+ this.saveSrc();
+ }
+ }));
+ const channelItems = channels.map(channel => ({
+ text: channel.name,
+ icon: faSatelliteDish,
+ indicate: channel.hasUnreadNote,
+ action: () => {
+ // NOTE: チャンネルタイムラインをこのコンポーネントで表示するようにすると投稿フォームはどうするかなどの問題が生じるのでとりあえずページ遷移で
+ //this.channel = channel;
+ //this.src = 'channel';
+ //this.saveSrc();
+ this.$router.push(`/channels/${channel.id}`);
}
}));
this.$root.menu({
items: [{
text: this.$t('_timelines.home'),
icon: faHome,
- action: () => { this.setSrc('home') }
+ action: () => { this.src = 'home'; this.saveSrc(); }
}, this.meta.disableLocalTimeline && !this.$store.state.i.isModerator && !this.$store.state.i.isAdmin ? undefined : {
text: this.$t('_timelines.local'),
icon: faComments,
- action: () => { this.setSrc('local') }
+ action: () => { this.src = 'local'; this.saveSrc(); }
}, this.meta.disableLocalTimeline && !this.$store.state.i.isModerator && !this.$store.state.i.isAdmin ? undefined : {
text: this.$t('_timelines.social'),
icon: faShareAlt,
- action: () => { this.setSrc('social') }
+ action: () => { this.src = 'social'; this.saveSrc(); }
}, this.meta.disableGlobalTimeline && !this.$store.state.i.isModerator && !this.$store.state.i.isAdmin ? undefined : {
text: this.$t('_timelines.global'),
icon: faGlobe,
- action: () => { this.setSrc('global') }
- }, antennaItems.length > 0 ? null : undefined, ...antennaItems, listItems.length > 0 ? null : undefined, ...listItems],
+ action: () => { this.src = 'global'; this.saveSrc(); }
+ }, antennaItems.length > 0 ? null : undefined, ...antennaItems, listItems.length > 0 ? null : undefined, ...listItems, channelItems.length > 0 ? null : undefined, ...channelItems],
fixed: true,
noCenter: true,
source: ev.currentTarget || ev.target
@@ -174,14 +197,13 @@ export default Vue.extend({
});
},
- setSrc(src) {
- this.src = src;
- },
-
saveSrc() {
this.$store.commit('deviceUser/setTl', {
src: this.src,
- arg: this.src == 'list' ? this.list : this.antenna
+ arg:
+ this.src === 'list' ? this.list :
+ this.src === 'antenna' ? this.antenna :
+ this.channel
});
},
diff --git a/src/client/pages/messaging/messaging-room.form.vue b/src/client/pages/messaging/messaging-room.form.vue
index 1c33eaa76e..eda8914c4a 100644
--- a/src/client/pages/messaging/messaging-room.form.vue
+++ b/src/client/pages/messaging/messaging-room.form.vue
@@ -53,7 +53,7 @@ export default Vue.extend({
};
},
computed: {
- draftId(): string {
+ draftKey(): string {
return this.user ? 'user:' + this.user.id : 'group:' + this.group.id;
},
canSend(): boolean {
@@ -79,7 +79,7 @@ export default Vue.extend({
autosize(this.$refs.text);
// 書きかけの投稿を復元
- const draft = JSON.parse(localStorage.getItem('message_drafts') || '{}')[this.draftId];
+ const draft = JSON.parse(localStorage.getItem('message_drafts') || '{}')[this.draftKey];
if (draft) {
this.text = draft.data.text;
this.file = draft.data.file;
@@ -199,7 +199,7 @@ export default Vue.extend({
saveDraft() {
const data = JSON.parse(localStorage.getItem('message_drafts') || '{}');
- data[this.draftId] = {
+ data[this.draftKey] = {
updatedAt: new Date(),
data: {
text: this.text,
@@ -213,7 +213,7 @@ export default Vue.extend({
deleteDraft() {
const data = JSON.parse(localStorage.getItem('message_drafts') || '{}');
- delete data[this.draftId];
+ delete data[this.draftKey];
localStorage.setItem('message_drafts', JSON.stringify(data));
},
diff --git a/src/client/pages/my-settings/index.vue b/src/client/pages/my-settings/index.vue
index 16e786bfc8..6fb3116912 100644
--- a/src/client/pages/my-settings/index.vue
+++ b/src/client/pages/my-settings/index.vue
@@ -3,11 +3,11 @@
<portal to="icon"><fa :icon="faCog"/></portal>
<portal to="title">{{ $t('accountSettings') }}</portal>
- <x-profile-setting/>
- <x-privacy-setting/>
- <x-reaction-setting/>
+ <x-profile-setting class="_vMargin"/>
+ <x-privacy-setting class="_vMargin"/>
+ <x-reaction-setting class="_vMargin"/>
- <section class="_card">
+ <section class="_card _vMargin">
<div class="_title"><fa :icon="faCog"/> {{ $t('general') }}</div>
<div class="_content">
<mk-switch v-model="$store.state.i.autoWatch" @change="onChangeAutoWatch">
@@ -24,14 +24,14 @@
</div>
</section>
- <x-import-export/>
- <x-drive/>
- <x-mute-block/>
- <x-word-mute/>
- <x-security/>
- <x-2fa/>
- <x-integration/>
- <x-api/>
+ <x-import-export class="_vMargin"/>
+ <x-drive class="_vMargin"/>
+ <x-mute-block class="_vMargin"/>
+ <x-word-mute class="_vMargin"/>
+ <x-security class="_vMargin"/>
+ <x-2fa class="_vMargin"/>
+ <x-integration class="_vMargin"/>
+ <x-api class="_vMargin"/>
<router-link class="_panel _buttonPrimary" to="/my/apps" style="margin: var(--margin) auto;">{{ $t('installedApps') }}</router-link>
diff --git a/src/client/pages/preferences/index.vue b/src/client/pages/preferences/index.vue
index a2a5ab80f4..9d3959fcc2 100644
--- a/src/client/pages/preferences/index.vue
+++ b/src/client/pages/preferences/index.vue
@@ -5,11 +5,11 @@
<router-link v-if="$store.getters.isSignedIn" class="_panel _buttonPrimary" to="/my/settings" style="margin-bottom: var(--margin);">{{ $t('accountSettings') }}</router-link>
- <x-theme/>
+ <x-theme class="_vMargin"/>
- <x-sidebar/>
+ <x-sidebar class="_vMargin"/>
- <x-plugins/>
+ <x-plugins class="_vMargin"/>
<section class="_card _vMargin">
<div class="_title"><fa :icon="faMusic"/> {{ $t('sounds') }}</div>
@@ -50,6 +50,11 @@
<option v-for="sound in sounds" :value="sound" :key="sound">{{ sound || $t('none') }}</option>
<template #text><button class="_textButton" @click="listen(sfxAntenna)" v-if="sfxAntenna"><fa :icon="faPlay"/> {{ $t('listen') }}</button></template>
</mk-select>
+ <mk-select v-model="sfxChannel">
+ <template #label>{{ $t('_sfx.channel') }}</template>
+ <option v-for="sound in sounds" :value="sound" :key="sound">{{ sound || $t('none') }}</option>
+ <template #text><button class="_textButton" @click="listen(sfxChannel)" v-if="sfxChannel"><fa :icon="faPlay"/> {{ $t('listen') }}</button></template>
+ </mk-select>
</div>
</section>
@@ -142,10 +147,14 @@ const sounds = [
'syuilo/pirori',
'syuilo/pirori-wet',
'syuilo/pirori-square-wet',
+ 'syuilo/square-pico',
+ 'syuilo/reverved',
+ 'syuilo/ryukyu',
'aisha/1',
'aisha/2',
'aisha/3',
'noizenecio/kick_gaba',
+ 'noizenecio/kick_gaba2',
];
export default Vue.extend({
@@ -272,6 +281,11 @@ export default Vue.extend({
set(value) { this.$store.commit('device/set', { key: 'sfxAntenna', value }); }
},
+ sfxChannel: {
+ get() { return this.$store.state.device.sfxChannel; },
+ set(value) { this.$store.commit('device/set', { key: 'sfxChannel', value }); }
+ },
+
volumeIcon: {
get() {
return this.sfxVolume === 0 ? faVolumeMute : faVolumeUp;